diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c5c282ed..2f323c05f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ cmake_minimum_required(VERSION 3.15) cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime project(Verilator - VERSION 5.008 + VERSION 5.010 HOMEPAGE_URL https://verilator.org LANGUAGES CXX ) @@ -23,7 +23,8 @@ project(Verilator option(DEBUG_AND_RELEASE_AND_COVERAGE "Builds both the debug and release binaries, overriding CMAKE_BUILD_TYPE. Not supported under MSBuild.") -set(PYTHON3 python) +find_package(Python3 COMPONENTS Interpreter) +set(PYTHON3 Python3::Interpreter) set(CMAKE_INSTALL_DATADIR ${CMAKE_INSTALL_PREFIX}) include(GNUInstallDirs) include(CMakePackageConfigHelpers) @@ -44,15 +45,7 @@ if (MSVC) if (NOT WIN_FLEX_BISON) message(FATAL_ERROR "Please install https://github.com/lexxmark/winflexbison and set WIN_FLEX_BISON environment variable. Please use install cmake target after a successful build.") endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++20") -else() - if (NOT DEFINED ENV{FLEX_INCLUDE}) - message(FATAL_ERROR "Please set environment variable FLEX_INCLUDE to the directory containing FlexLexer.h") - endif() - set(WIN_FLEX_BISON "$ENV{FLEX_INCLUDE}") - if (WIN32 AND NOT MINGW) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") - endif() + set(CMAKE_CXX_STANDARD 20) endif() find_package(BISON) @@ -99,6 +92,7 @@ configure_package_config_file(verilator-config-version.cmake.in verilator-config install(FILES ${CMAKE_CURRENT_BINARY_DIR}/verilator-config-version.cmake DESTINATION ${CMAKE_INSTALL_PREFIX}) foreach (program + verilator verilator_gantt verilator_ccache_report verilator_difftree diff --git a/Changes b/Changes index 583a85abc..637d4196d 100644 --- a/Changes +++ b/Changes @@ -8,6 +8,72 @@ The changes in each Verilator version are described below. The contributors that suggested a given feature are shown in []. Thanks! +Verilator 5.010 2023-04-30 +========================== + +**Minor:** + +* Add --public-depth to force public to a certain instance depth (#3952). [Andrew Nolte] +* Add --public-params flag (#3990). [Andrew Nolte] +* Add CONSTRAINTIGN warning when constraint ignored. +* Add STATICVAR warning and convert to automatic (#4018) (#4027) (#4030). [Ryszard Rozak, Antmicro Ltd] +* Add error if class types don't match (#4064). [Ryszard Rozak, Antmicro Ltd] +* Support class extends of package::class. +* Support class srandom and class random stability. +* Support class method calls without parenthesis (#3902) (#4082). [Srinivasan Venkataramanan] +* Support method calls without parenthesis (#4034). [Ryszard Rozak, Antmicro Ltd] +* Support parameterized return types of methods (#4122). [Ryszard Rozak, Antmicro Ltd] +* Support parameterized class references in extends statement (#4146). [Ryszard Rozak, Antmicro Ltd] +* Support complicated IEEE 'for' assignments. +* Support $fopen as an expression. +* Support ++/-- on dotted member variables. +* Optimize static trigger evaluation (#4142). [Geza Lore, X-EPIC] +* Optimize more xor trees (#4071). [Yutetsu TAKATSUKASA] +* Change range order warning from LITENDIAN to ASCRANGE (#4010). [Iztok Jeras] +* Change ZERODLY to a warning. +* Fix random internal crashes (#666). [Dag Lem] +* Fix install, standardization in cmake CMakeLists.txt (#3974). [Yu-Sheng Lin] +* Fix UNDRIVEN warning seg fault (#3989). [Felix Neumärker] +* Fix symbol entries when inheriting classes (#3995) (#3996). [Krzysztof Boroński] +* Fix event controls reusing same variable (#4014). Kamil Rakoczy +* Fix push to dynamic queue in struct (#4015). [ezchi] +* Fix names for blocks in do..while loop (#4019). [Ryszard Rozak, Antmicro Ltd] +* Fix randomize on null field (#4023). [Ryszard Rozak, Antmicro Ltd] +* Fix rand fields in base classes (#4025). [Ryszard Rozak, Antmicro Ltd] +* Fix large return blocks with --comp-limit-blocks (#4028). [tenghtt] +* Fix clocking block scope internal error (#4032). [Srinivasan Venkataramanan] +* Fix false LATCH warning on --assert 'unique else if' (#4033) ($4054). [Jesse Taube] +* Fix characters from DEFENV literals for Conda (#4035) (#4044). [Tim Snyder] +* Fix info message prints under --assert (#4036) (#4053). [Srinivasan Venkataramanan] +* Fix C++ compile errors when passing class refs as task argument (#4063). [Krzysztof Bieganski, Antmicro Ltd] +* Fix NBAs inside fork-joins (#4050). [Aleksander Kiryk, Antmicro Ltd] +* Fix task calls as fork statements (#4055). [Krzysztof Bieganski, Antmicro Ltd] +* Fix _Vilp used before declaration (#4057) (#4062). [Josep Sans] +* Fix incorrect optimization of bit op tree (#4059) (#4070). [Yutetsu TAKATSUKASA] +* Fix parameters in a class body to be localparam (#4061). [Ryszard Rozak, Antmicro Ltd] +* Fix interface generate begin (#4065). [Srinivasan Venkataramanan] +* Fix tracing with awaits at end of block (#4075) (#4076). [Krzysztof Bieganski, Antmicro Ltd] +* Fix sense expression variable naming (#4081). [Kamil Rakoczy] +* Fix importing symbols from base class (#4084). [Ryszard Rozak, Antmicro Ltd] +* Fix false error on new const assignment (#4098). [Tudor Timi] +* Fix unpacked structs under classes (#4102). [Tudor Timi] +* Fix variables in class methods to be automatic (#4111) (#4137). [Peter Monsson] +* Fix to use parallel build for projects with a lot of files (#4116). [Krzysztof Boroński] +* Fix including __Syms header in generated C++ files (#4123). [Krzysztof Boroński] +* Fix systemc namespace issues (#4126) (#4127). [Eyck Jentzsch] +* Fix class param extends A=B (#4128). [Ryszard Rozak, Antmicro Ltd] +* Fix missing begin block hierarchy in --xml-only cells section (#4129) (#4133). [Risto Pejašinović] +* Fix resolution of class lvalues after parameterization (#4131). [Krzysztof Boroński] +* Fix DFG error on $countbits (#4101) (#4143). [Paul Donahue] +* Fix duplicating parameter class types (#4115). [Ryszard Rozak, Antmicro Ltd] +* Fix class extend param references (#4136). [Ryszard Rozak, Antmicro Ltd] +* Fix -CFLAGS to allow overriding optimization levels (#4140). [Peter Monsson] +* Fix DPI function type alias (#4148) (#4149). [Toru Niina] +* Fix deleting unused parameterized classes (#4150). [Ryszard Rozak, Antmicro Ltd] +* Fix false ENUMVALUE on expressions and arrays. +* Fix unnecessary verilated_std.sv waivers in --waiver-output. + + Verilator 5.008 2023-03-04 ========================== @@ -31,7 +97,6 @@ Verilator 5.008 2023-03-04 * Support function calls without parenthesis (#3903) (#3902). [Ryszard Rozak, Antmicro Ltd] * Support class extending its parameter (#3904). [Ryszard Rozak, Antmicro Ltd] * Support static function variables (#3830). [Ryszard Rozak, Antmicro Ltd] -* Support vpiDefName (#3906) (#3931). [Andrew Nolte] * Support recursive methods (#3987). [Ryszard Rozak, Antmicro Ltd] * Fix real parameters of infinity and NaN. * Fix pattern assignment to unpacked structs (#3510). [Mostafa Garnal] @@ -41,8 +106,8 @@ Verilator 5.008 2023-03-04 * Fix namespace fallback resolution (#3863) (#3942). [Aleksander Kiryk, Antmicro Ltd] * Fix std:: to be parsed first (#3864) (#3928). [Aleksander Kiryk, Antmicro Ltd] * Fix cmake warning if multiple SOURCES w/o PREFIX (#3916) (#3927). [Yoda Lee] -* Fix paramaterized class function linkage (#3917). [Ryszard Rozak] -* Fix static members of type aliases of a parametrized class (#3922). [Ryszard Rozak, Antmicro Ltd] +* Fix parameterized class function linkage (#3917). [Ryszard Rozak] +* Fix static members of type aliases of a parameterized class (#3922). [Ryszard Rozak, Antmicro Ltd] * Fix class extend parameter dot case (#3926). [Ryszard Rozak, Antmicro Ltd] * Fix MsWin missing directory exception, and ::std (#3928) (#3933) (#3935). [Kritik Bhimani] * Fix very long VPI signal names (#3929). [Marlon James] @@ -54,13 +119,13 @@ Verilator 5.008 2023-03-04 * Fix CMake bad C identifiers (#3948) (#3951). [Zixi Li] * Fix build on HP PA architecture (#3954). [John David Anglin] * Fix date on the front page of verilator.pdf (#3956) (#3957). [Larry Doolittle] -* Fix dicts declared with ref type (#3960). [Ryszard Rozak, Antmicro Ltd] +* Fix associative arrays declared with ref type (#3960). [Ryszard Rozak, Antmicro Ltd] * Fix missing error on negative replicate (#3963). [Benjamin Menküc] * Fix self references to parameterized classes (#3962). [Ryszard Rozak, Antmicro Ltd] * Fix LITENDIAN warning is backwards (#3966) (#3967). [Cameron Kirk] * Fix subsequent parameter declarations (#3969). [Ryszard Rozak, Antmicro Ltd] * Fix timing delays to not truncate below 64 bits (#3973) (#3982). [Felix Neumärker] -* Fix cmake on macOS to mark weak symbols with -U linker flag (#3978) (#3979). [Peter Debacker] +* Fix cmake on MacOS to mark weak symbols with -U linker flag (#3978) (#3979). [Peter Debacker] * Fix UNDRIVEN warning seg fault (#3989). [Felix Neumärker] * Fix coverage of class methods (#3998). [Tim Paine] * Fix packed array structure replication. @@ -80,7 +145,7 @@ Verilator 5.006 2023-01-22 * Support import/export lists in modport (#3886). [Gökçe Aydos] * Support class queue equality (#3895). [Ilya Barkov] * Support type case and type equality comparisons. -* Add IMPLICITSTATIC warning when a ftask/function is implicitly static (#3839). [Ryszard Rozak, Antmicro Ltd] +* Add IMPLICITSTATIC warning when a task/function is implicitly static (#3839). [Ryszard Rozak, Antmicro Ltd] * Add VL_VALUE_STRING_MAX_WORDS override (#3869). [Andrew Nolte] * Optimize expansion of extend operators. * Internal multithreading tests. [Mariusz Glebocki, et al, Antmicro Ltd] @@ -94,7 +159,7 @@ Verilator 5.006 2023-01-22 * Fix self references when param class instantiated (#3833). [Ryszard Rozak, Antmicro Ltd] * Fix memory leak in V3Sched, etc. (#3834). [Geza Lore] * Fix compatibility with musl libc / Alpine Linux (#3845). [Sören Tempel] -* Fix empty case items crash (#3851). [rporter] +* Fix empty case items crash (#3851). [Rich Porter] * Fix VL_CPU_RELAX on MIPS/Armel/s390/sparc (#3843) (#3891). [Kamil Rakoczy] * Fix module parameter name collision (#3854) (#3855). [James Shi] * Fix unpacked array expansion (#3861). [Joey Liu] diff --git a/bin/verilator b/bin/verilator index 587a9b85f..5bab12f33 100755 --- a/bin/verilator +++ b/bin/verilator @@ -404,6 +404,7 @@ detailed descriptions of these arguments. --protect-key Key for symbol protection --protect-lib Create a DPI protected library --public Debugging; see docs + --public-params Mark all parameters as public_flat --public-flat-rw Mark all variables, etc as public_flat_rw -pvalue+= Overwrite toplevel parameter --quiet-exit Don't print the command on failure diff --git a/configure.ac b/configure.ac index 74d1beb5b..3e45cc298 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.008 2023-03-04], +AC_INIT([Verilator],[5.010 2023-04-30], [https://verilator.org], [verilator],[https://verilator.org]) @@ -68,7 +68,7 @@ AC_MSG_CHECKING(whether to use -m32) AC_ARG_ENABLE([m32], [AS_HELP_STRING([--enable-m32], [Use -m32 for all compilation and link, - including Verialtor and generated models.])], + including Verilator and generated models.])], [case "${enableval}" in yes) CFG_ENABLE_M32=yes ;; no) CFG_ENABLE_M32=no ;; diff --git a/docs/CONTRIBUTING.rst b/docs/CONTRIBUTING.rst index 1e81842ad..c7a4de79d 100644 --- a/docs/CONTRIBUTING.rst +++ b/docs/CONTRIBUTING.rst @@ -10,9 +10,12 @@ contributions flow more efficiently. Did you find a bug? ------------------- -- Please **Ensure the bug was not already reported** by searching +- Please **ensure the bug was not already reported** by searching `Verilator Issues `__. +- Please **download the latest development GitHub version**, build, and see + if the issue has been fixed. + - If you're unable to find an open issue addressing the problem, `open a new Verilator issue `__. @@ -20,21 +23,28 @@ Did you find a bug? demonstrating the bug and expected behavior that is not occurring. - The ideal example works against other simulators, and is in the - test_regress/t test format, as described in `docs/internals - `__. + test_regress/t test format, as described in + `Verilator Internals Documentation + `__. Did you write a patch that fixes a bug? --------------------------------------- -- Please `Open a new issue `__. +- Please `Open a new Verilator issue `__ + if there is not one already describing the bug. -- You may attach a patch to the issue, or (preferred) may request a - GitHub pull request. +- Please `Open a Verilator pull request + `__. - - Verilator uses GitHub Actions to provide continuous integration. You - may want to enable Actions on your GitHub branch to ensure your changes - keep the tests passing. See `docs/internals `__. +- See the coding conventions, and other developer information in + ``docs/internals.rst`` in the distribution, or as rendered at + `Verilator Internals Documentation + `__. + +- Verilator uses GitHub Actions to provide continuous integration. You + may want to enable Actions on your GitHub branch to ensure your changes + keep the tests passing. - Your source-code contributions must be certified as open source, under the `Developer Certificate of @@ -56,13 +66,10 @@ Did you write a patch that fixes a bug? contribution. - Your test contributions are generally considered released into the - Creative Commons Public Domain (CC0), unless you request otherwise or + Creative Commons Public Domain (CC0), unless you request otherwise, or put a GNU/Artistic license on your file. -- Most important is we get your patch. If you'd like to clean up - indentation and related issues ahead of our feedback, that is - appreciated; please see the coding conventions in `docs/internals - `__. +- Most important is we get your patch. Do you have questions? diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index aad4beb04..629517575 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -29,6 +29,7 @@ Drew Taussig Driss Hafdi Edgar E. Iglesias Eric Rippey +Eyck Jentzsch Fan Shupei Felix Neumärker Felix Yan @@ -61,6 +62,7 @@ Jamie Iles Jan Van Winkel Jean Berniolles Jeremy Bennett +Jesse Taube Jevin Sweval Jiacheng Qian Jiuyang Liu @@ -69,6 +71,7 @@ John Coiner John Demme Jonathan Drolet Joseph Nwabueze +Josep Sans Josh Redford Julie Schwartz Julien Margetts @@ -119,12 +122,15 @@ Qingyao Sun Rafal Kapuscik Raynard Qiao Richard Myers +Risto Pejašinović +Robert Balas Rupert Swarbrick Ryszard Rozak Samuel Riedel Sean Cross Sebastien Van Cauwenberghe Sergi Granell +Srinivasan Venkataramanan Stefan Wallentowitz Stephen Henry Steven Hugg @@ -136,6 +142,7 @@ Tobias Wölfel Todd Strader Tomasz Gorochowik Topa Topino +Toru Niina Tymoteusz Blazejczyk Udi Finkelstein Unai Martinez-Corral diff --git a/docs/guide/connecting.rst b/docs/guide/connecting.rst index 9762e264a..10b35eec9 100644 --- a/docs/guide/connecting.rst +++ b/docs/guide/connecting.rst @@ -120,6 +120,15 @@ code. This is a feature, as using the SystemC pin interconnect scheme everywhere would reduce performance by an order of magnitude. +Verilated API +============= + +The API to a Verilated model is the C++ headers in the include/ directory +in the distribution. These headers use Doxygen comments, `///` and `//<`, +to indicate and document those functions that are part of the Verilated +public API. + + Direct Programming Interface (DPI) ================================== diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 0a4b90249..604db0702 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -1101,6 +1101,18 @@ Summary: marking only those signals that need public_flat_rw is typically significantly better performing. +.. 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-params + + Declares all parameters public as if they had + :code:`/*verilator public_flat_rd*/` + metacomments. + .. option:: -pvalue+= Overwrites the given parameter(s) of the top-level module. See @@ -1502,10 +1514,10 @@ Summary: .. option:: -Wno-lint Disable all lint-related warning messages, and all style warnings. This is - equivalent to ``-Wno-ALWCOMBORDER -Wno-BSSPACE -Wno-CASEINCOMPLETE + equivalent to ``-Wno-ALWCOMBORDER -Wno-ASCRANGE -Wno-BSSPACE -Wno-CASEINCOMPLETE -Wno-CASEOVERLAP -Wno-CASEX -Wno-CASTCONST -Wno-CASEWITHX -Wno-CMPCONST -Wno-COLONPLUS - -Wno-IMPLICIT -Wno-IMPLICITSTATIC -Wno-LITENDIAN -Wno-PINCONNECTEMPTY - -Wno-PINMISSING -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED + -Wno-IMPLICIT -Wno-IMPLICITSTATIC -Wno-PINCONNECTEMPTY + -Wno-PINMISSING -Wno-STATICVAR -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED -Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL -Wno-WIDTH`` plus the list shown for Wno-style. @@ -1542,7 +1554,7 @@ Summary: enabled), but do not affect style messages. This is equivalent to ``-Wwarn-ALWCOMBORDER -Wwarn-BSSPACE -Wwarn-CASEINCOMPLETE -Wwarn-CASEOVERLAP -Wwarn-CASEX -Wwarn-CASTCONST -Wwarn-CASEWITHX -Wwarn-CMPCONST - -Wwarn-COLONPLUS -Wwarn-IMPLICIT -Wwarn-LITENDIAN + -Wwarn-COLONPLUS -Wwarn-IMPLICIT -Wwarn-ASCRANGE -Wwarn-PINMISSING -Wwarn-REALCVT -Wwarn-UNSIGNED -Wwarn-WIDTH``. .. option:: -Wwarn-style diff --git a/docs/guide/simulating.rst b/docs/guide/simulating.rst index b65573be1..5f9440da9 100644 --- a/docs/guide/simulating.rst +++ b/docs/guide/simulating.rst @@ -498,3 +498,20 @@ Clang. The process in GCC 10 was as follows: Clang and GCC also support -fauto-profile, which uses sample-based feedback-directed optimization. See the appropriate compiler documentation. + + +.. _Runtime Debugging + +Runtime Debugging +================= + +To debug a Verilated executable, use the standard GNU debugger ``gdb`` or a +similar tool. Typically you will want to have debugger symbols inserted by +the compiler, assertions enabled in the C library, and assertions enabled +in the Verilated library. (These options slow down the executable, so do +this only when debugging.) To enable this, Verilate with: + + -CFLAGS -ggdb -LDFLAGS -ggdb -CFLAGS -DVL_DEBUG=1 -CFLAGS -D_GLIBCXX_DEBUG + +The :vlopt:`-CFLAGS` and/or :vlopt:`-LDFLAGS` options pass arguments +directly to the compiler or linker. diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index ccfb0e763..b88f2ce20 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -97,6 +97,24 @@ List Of Warnings simulate correctly. +.. option:: ASCRANGE + + .. TODO better example + + Warns that a packed vector is declared with ascending bit range + (i.e. [0:7]). Descending bit range is now the overwhelming standard, + and ascending ranges are now thus often due to simple oversight + instead of intent (a notable exception is the OpenPOWER code base). + + It also warns that an instance is declared with ascending range + (i.e. [0:7] or [7]) and is connected to an N-wide signal. + The bits will likely be in the reversed order from what people may expect + (i.e., instance [0] will connect to signal bit [N-1] not bit [0]). + + Ignoring this warning will only suppress the lint check; it will + simulate correctly. + + .. option:: ASSIGNDLY .. TODO better example @@ -378,6 +396,16 @@ List Of Warnings simulators. +.. option:: CONSTRAINTIGN + + Warns that Verilator does not support :code:`constraint`, + :code:`constraint_mode`, or :code:`rand_mode`, and the construct was are + ignored. + + Ignoring this warning may make Verilator randomize() simulations differ + from other simulators. + + .. option:: CONTASSREG .. TODO better example @@ -738,7 +766,7 @@ List Of Warnings This is a warning because the static default differs from C++, differs from class member function/tasks. Static is a more dangerous default - then automatic as static prevents the function from being reinterant, + then automatic as static prevents the function from being reentrant, which may be a source of bugs, and/or performance issues. If the function does not require static behavior, change it to "function @@ -850,18 +878,10 @@ List Of Warnings .. TODO better example - Warns that a packed vector is declared with big endian bit numbering - (i.e. [0:7]). Little endian bit numbering is now the overwhelming - standard, and big numbering is now thus often due to simple oversight - instead of intent. - - It also warns that an instance is declared with big endian range - (i.e. [0:7] or [7]) and is connected to an N-wide signal. - The bits will likely be backward from what people may expect - (i.e., instance [0] will connect to signal bit [N-1] not bit [0]). - - Ignoring this warning will only suppress the lint check; it will - simulate correctly. + The naming of this warning is in contradiction with the common + interpretation of little endian. It was therefore renamed to + :option:`ASCRANGE`. While :option:`LITENDIAN` remains for + backwards compatibility, new projects should use :option:`ASCRANGE`. .. option:: MINTYPMAX @@ -1302,6 +1322,17 @@ List Of Warnings * The variable is tristate or bidirectional. (e.g., :code:`inout`). +.. option:: STATICVAR + + Warns that a static variable declared in a loop with declaration assignment + was converted to automatic. Often such variables were intended to + instead be declared "automatic". + + Ignoring this warning may make Verilator differ from other simulators, + which will treat the variable as static. Verilator may in future versions also + treat the variable as static. + + .. option:: STMTDLY Warns that the code has a statement with a delayed time in front of it. @@ -1813,7 +1844,7 @@ List Of Warnings .. option:: WIDTHXZEXPAND - A more granular WIDTH warning, for when a value is xz expanded + A more granular WIDTH warning, for when a value is X/Z expanded .. option:: WIDTHCONCAT diff --git a/docs/internals.rst b/docs/internals.rst index 12cbd720c..c055aae8c 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -628,16 +628,12 @@ coroutines ``co_await`` its ``join`` function, and forked ones call ``done`` when they're finished. Once the required number of coroutines (set using ``setCounter``) finish execution, the forking coroutine is resumed. -Awaitable Utilities -~~~~~~~~~~~~~~~~~~~ +``VlForever`` +~~~~~~~~~~~~~ -There are also two small utility awaitable types: - -* ``VlNow`` is an awaitable that suspends and immediately resumes coroutines. - It is used for forcing a coroutine to be moved onto the heap. See the `Forks` - section for more detail. -* ``VlForever`` is used for blocking a coroutine forever. See the `Timing pass` - section for more detail. +A small utility awaitable type. It allows for blocking a coroutine forever. It +is currently only used for ``wait`` statements that await a constant false +condition. See the `Timing Pass` section for more details. Timing Pass ~~~~~~~~~~~ @@ -749,9 +745,7 @@ doesn't suspend the forking process. In forked processes, references to local variables are only allowed in ``fork..join``, as this is the only case that ensures the lifetime of these -locals are at least as long as the execution of the forked processes. This is -where ``VlNow`` is used, to ensure the locals are moved to the heap before they -are passed by reference to the forked processes. +locals are at least as long as the execution of the forked processes. Multithreaded Mode @@ -1807,8 +1801,6 @@ IEEE 1800-2017 6.12 "shortreal" Little/no tool support, and easily promoted to real. IEEE 1800-2017 11.11 Min, typ, max No SDF support, so will always use typical. -IEEE 1800-2017 11.12 "let" - Little/no tool support, makes it difficult to implement parsers. IEEE 1800-2017 20.16 Stochastic analysis Little industry use. IEEE 1800-2017 20.17 PLA modeling diff --git a/docs/spelling.txt b/docs/spelling.txt index b77ffb233..f4660bed4 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -6,6 +6,7 @@ Alexandre Ami Amir Anastasiadis +Anglin Anikin Antmicro Antonin @@ -42,6 +43,8 @@ Chandan Chitlesh Christophe Cochrane +Conda +Corteggiani Cuan Cygwin DErrico @@ -96,6 +99,7 @@ Fredieu GTKWave Galbi Gantt +Garnal Gartner Gelinek Geoff @@ -188,6 +192,7 @@ Laurens Lavino Leber Leendert +Lem Lesik Liland Liu @@ -207,6 +212,7 @@ Matveyenko Maupin Mdir Mednick +Menküc Michiels Microsystems Milanovic @@ -223,8 +229,10 @@ Muhlestein Multithreaded Multithreading NOUNOPTFLAT +NaN Nalbantis Narayan +Nassim Nauticus Newgard Nigam @@ -295,6 +303,7 @@ Solomatnikov Solt Southwell Srini +Srinivasan Stamness Stephane Stoddard @@ -319,6 +328,7 @@ Thiede Thierry Thyer Tichelaar +Timi Tomov Tood Topa @@ -343,6 +353,7 @@ Vdeeptemp Vdly Vemumtab Vemuri +Venkataramanan Veriable Verialted Verilate @@ -353,6 +364,7 @@ Verilator Verilog Vighnesh Viktor +Vilp Vm Vukobratovic Wai @@ -376,6 +388,7 @@ Xuan Xuanqi Yazdanbakhsh Yi +Yike Yinan Yosys Yu @@ -452,6 +465,7 @@ commandArgsPlusMatch compilable concat concats +conf config const constexpr @@ -512,6 +526,7 @@ dumpportson dumpvars dut dx +dynarray elab elike elsif @@ -520,6 +535,7 @@ endcelldefine endfunction endgenerate endian +endianness endif endmodule endprotect @@ -541,6 +557,7 @@ exe executables expr extern +ezchi fanin fasttrace fauto @@ -635,6 +652,7 @@ lcov ld leavinel len +libc libext libgoogle libsystemc @@ -653,7 +671,9 @@ logicals longint lossy lsb +lubc lvalue +lvalues lxt macromodule makefile @@ -678,12 +698,15 @@ mtasks mulithreaded mult multidim +multidriven multiinterfaces multiline multipling +multipoint multithread multithreaded multithreading +musl mutexes mux myftptoyman @@ -714,6 +737,7 @@ ortegon oversubscription parallelized param +parameterization parameterized params parens @@ -762,6 +786,7 @@ pulldowns pullup pvalue pwd +py qrq radix randc @@ -807,6 +832,7 @@ specparam splitme spp sqrt +srandom src srcdir srcfile @@ -838,6 +864,7 @@ svdpi swrite sys systemc +tenghtt testbench threadsafe threashold @@ -909,6 +936,7 @@ vlt vltstd vluint vpi +vpiDefName vpiLeftRange vpiModule vpiSize @@ -938,4 +966,3 @@ ypq yurivict zdave Øyvind - diff --git a/examples/make_hello_sc/sc_main.cpp b/examples/make_hello_sc/sc_main.cpp index 91fe02220..5f9ab2429 100644 --- a/examples/make_hello_sc/sc_main.cpp +++ b/examples/make_hello_sc/sc_main.cpp @@ -7,7 +7,7 @@ //====================================================================== // SystemC global header -#include +#include // Include common routines #include @@ -15,6 +15,8 @@ // Include model header, generated from Verilating "top.v" #include "Vtop.h" +using namespace sc_core; + int sc_main(int argc, char* argv[]) { // See a similar example walkthrough in the verilator manpage. diff --git a/examples/make_tracing_sc/sc_main.cpp b/examples/make_tracing_sc/sc_main.cpp index 223cce885..44e69c7f0 100644 --- a/examples/make_tracing_sc/sc_main.cpp +++ b/examples/make_tracing_sc/sc_main.cpp @@ -10,7 +10,7 @@ #include // SystemC global header -#include +#include // Include common routines #include @@ -23,6 +23,8 @@ // Include model header, generated from Verilating "top.v" #include "Vtop.h" +using namespace sc_core; + int sc_main(int argc, char* argv[]) { // This is a more complicated example, please also see the simpler examples/make_hello_c. diff --git a/include/gtkwave/fstapi.c b/include/gtkwave/fstapi.c index 11cb0c501..df8b23866 100644 --- a/include/gtkwave/fstapi.c +++ b/include/gtkwave/fstapi.c @@ -58,6 +58,7 @@ #endif #ifdef __MINGW32__ +#define WIN32_LEAN_AND_MEAN #include #endif @@ -131,7 +132,7 @@ void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint3 #define FST_GZIO_LEN (32768) #define FST_HDR_FOURPACK_DUO_SIZE (4*1024*1024) -#if defined(__i386__) || defined(__x86_64__) || defined(_AIX) +#if defined(__i386__) || defined(__x86_64__) || defined(_AIX) || defined(__aarch64__) #define FST_DO_MISALIGNED_OPS #endif @@ -334,26 +335,22 @@ return(NULL); /* * mmap compatibility */ -#if defined __CYGWIN__ || defined __MINGW32__ +#if defined __MINGW32__ #include #define fstMmap(__addr,__len,__prot,__flags,__fd,__off) fstMmap2((__len), (__fd), (__off)) -#define fstMunmap(__addr,__len) free(__addr) +#define fstMunmap(__addr,__len) UnmapViewOfFile((LPCVOID)__addr) static void *fstMmap2(size_t __len, int __fd, fst_off_t __off) { -(void)__off; +HANDLE handle = CreateFileMapping((HANDLE)_get_osfhandle(__fd), NULL, + PAGE_READWRITE, (DWORD)(__len >> 32), + (DWORD)__len, NULL); +if (!handle) { return NULL; } -unsigned char *pnt = (unsigned char *)malloc(__len); -fst_off_t cur_offs = lseek(__fd, 0, SEEK_CUR); -size_t i; - -lseek(__fd, 0, SEEK_SET); -for(i=0;i<__len;i+=SSIZE_MAX) - { - read(__fd, pnt + i, ((__len - i) >= SSIZE_MAX) ? SSIZE_MAX : (__len - i)); - } -lseek(__fd, cur_offs, SEEK_SET); -return(pnt); +void *ptr = MapViewOfFileEx(handle, FILE_MAP_READ | FILE_MAP_WRITE, + 0, (DWORD)__off, (SIZE_T)__len, (LPVOID)NULL); +CloseHandle(handle); +return ptr; } #else #include @@ -984,14 +981,26 @@ fflush(xc->handle); */ static void fstWriterMmapSanity(void *pnt, const char *file, int line, const char *usage) { -#if !defined(__CYGWIN__) && !defined(__MINGW32__) -if(pnt == MAP_FAILED) +if(pnt == NULL +#ifdef MAP_FAILED + || pnt == MAP_FAILED +#endif + ) { fprintf(stderr, "fstMmap() assigned to %s failed: errno: %d, file %s, line %d.\n", usage, errno, file, line); +#if !defined(__MINGW32__) perror("Why"); +#else + LPSTR mbuf = NULL; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&mbuf, 0, NULL); + fprintf(stderr, "%s", mbuf); + LocalFree(mbuf); +#endif pnt = NULL; } -#endif } @@ -1037,34 +1046,11 @@ if(!xc->curval_mem) static void fstDestroyMmaps(struct fstWriterContext *xc, int is_closing) { -#if !defined __CYGWIN__ && !defined __MINGW32__ (void)is_closing; -#endif fstMunmap(xc->valpos_mem, xc->maxhandle * 4 * sizeof(uint32_t)); xc->valpos_mem = NULL; -#if defined __CYGWIN__ || defined __MINGW32__ -if(xc->curval_mem) - { - if(!is_closing) /* need to flush out for next emulated mmap() read */ - { - unsigned char *pnt = xc->curval_mem; - int __fd = fileno(xc->curval_handle); - fst_off_t cur_offs = lseek(__fd, 0, SEEK_CUR); - size_t i; - size_t __len = xc->maxvalpos; - - lseek(__fd, 0, SEEK_SET); - for(i=0;i<__len;i+=SSIZE_MAX) - { - write(__fd, pnt + i, ((__len - i) >= SSIZE_MAX) ? SSIZE_MAX : (__len - i)); - } - lseek(__fd, cur_offs, SEEK_SET); - } - } -#endif - fstMunmap(xc->curval_mem, xc->maxvalpos); xc->curval_mem = NULL; } @@ -3412,6 +3398,7 @@ int flat_hier_alloc_len; unsigned do_rewind : 1; char str_scope_nam[FST_ID_NAM_SIZ+1]; char str_scope_comp[FST_ID_NAM_SIZ+1]; +char *str_scope_attr; unsigned fseek_failed : 1; @@ -4140,7 +4127,7 @@ if(!(isfeof=feof(xc->fh))) cl = 0; while((ch = fgetc(xc->fh))) { - if(cl <= FST_ID_NAM_SIZ) + if(cl < FST_ID_NAM_SIZ) { pnt[cl++] = ch; } @@ -4152,7 +4139,7 @@ if(!(isfeof=feof(xc->fh))) cl = 0; while((ch = fgetc(xc->fh))) { - if(cl <= FST_ID_NAM_SIZ) + if(cl < FST_ID_NAM_SIZ) { pnt[cl++] = ch; } @@ -4169,15 +4156,19 @@ if(!(isfeof=feof(xc->fh))) xc->hier.htyp = FST_HT_ATTRBEGIN; xc->hier.u.attr.typ = fgetc(xc->fh); xc->hier.u.attr.subtype = fgetc(xc->fh); - xc->hier.u.attr.name = pnt = xc->str_scope_nam; + if(!xc->str_scope_attr) + { + xc->str_scope_attr = (char *)calloc(1, FST_ID_NAM_ATTR_SIZ+1); + } + xc->hier.u.attr.name = pnt = xc->str_scope_attr; cl = 0; while((ch = fgetc(xc->fh))) { - if(cl <= FST_ID_NAM_SIZ) + if(cl < FST_ID_NAM_ATTR_SIZ) { pnt[cl++] = ch; } - }; /* scopename */ + }; /* attrname */ pnt[cl] = 0; xc->hier.u.attr.name_length = cl; @@ -4237,7 +4228,7 @@ if(!(isfeof=feof(xc->fh))) cl = 0; while((ch = fgetc(xc->fh))) { - if(cl <= FST_ID_NAM_SIZ) + if(cl < FST_ID_NAM_SIZ) { pnt[cl++] = ch; } @@ -4376,7 +4367,7 @@ while(!feof(xc->fh)) cl = 0; while((ch = fgetc(xc->fh))) { - if(cl <= FST_ID_NAM_ATTR_SIZ) + if(cl < FST_ID_NAM_ATTR_SIZ) { pnt[cl++] = ch; } @@ -4398,7 +4389,7 @@ while(!feof(xc->fh)) cl = 0; while((ch = fgetc(xc->fh))) { - if(cl <= FST_ID_NAM_ATTR_SIZ) + if(cl < FST_ID_NAM_ATTR_SIZ) { pnt[cl++] = ch; } @@ -4487,7 +4478,7 @@ while(!feof(xc->fh)) cl = 0; while((ch = fgetc(xc->fh))) { - if(cl <= FST_ID_NAM_ATTR_SIZ) + if(cl < FST_ID_NAM_ATTR_SIZ) { pnt[cl++] = ch; } @@ -4974,6 +4965,7 @@ if(xc) free(xc->signal_typs); xc->signal_typs = NULL; free(xc->signal_lens); xc->signal_lens = NULL; free(xc->filename); xc->filename = NULL; + free(xc->str_scope_attr); xc->str_scope_attr = NULL; if(xc->fh) { diff --git a/include/verilated.cpp b/include/verilated.cpp index 995656840..dc23517cc 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -232,9 +232,9 @@ std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE { char* const bufp = new char[len + 1]; VL_VSNPRINTF(bufp, len + 1, formatp, ap); - std::string out{bufp, len}; // Not const to allow move optimization + std::string result{bufp, len}; // Not const to allow move optimization delete[] bufp; - return out; + return result; } uint64_t _vl_dbg_sequence_number() VL_MT_SAFE { @@ -255,30 +255,76 @@ void VL_DBG_MSGF(const char* formatp, ...) VL_MT_SAFE { // includes that otherwise would be required in every Verilated module va_list ap; va_start(ap, formatp); - const std::string out = _vl_string_vprintf(formatp, ap); + const std::string result = _vl_string_vprintf(formatp, ap); va_end(ap); // printf("-imm-V{t%d,%" PRId64 "}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(), - // out.c_str()); + // result.c_str()); // Using VL_PRINTF not VL_PRINTF_MT so that we can call VL_DBG_MSGF // from within the guts of the thread execution machinery (and it goes // to the screen and not into the queues we're debugging) - VL_PRINTF("-V{t%u,%" PRIu64 "}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(), out.c_str()); + VL_PRINTF("-V{t%u,%" PRIu64 "}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(), result.c_str()); } void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE { va_list ap; va_start(ap, formatp); - const std::string out = _vl_string_vprintf(formatp, ap); + const std::string result = _vl_string_vprintf(formatp, ap); va_end(ap); VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { // - VL_PRINTF("%s", out.c_str()); + VL_PRINTF("%s", result.c_str()); }}); } //=========================================================================== // Random -- Mostly called at init time, so not inline. +VlRNG::VlRNG() VL_MT_SAFE { + // Starting point for this new class comes from the global RNG + VlRNG& fromr = vl_thread_rng(); + m_state = fromr.m_state; + // Advance the *source* so it can later generate a new number + // Xoroshiro128+ algorithm + fromr.m_state[1] ^= fromr.m_state[0]; + fromr.m_state[0] = (((fromr.m_state[0] << 55) | (fromr.m_state[0] >> 9)) ^ fromr.m_state[1] + ^ (fromr.m_state[1] << 14)); + fromr.m_state[1] = (fromr.m_state[1] << 36) | (fromr.m_state[1] >> 28); +} +uint64_t VlRNG::rand64() VL_MT_UNSAFE { + // Xoroshiro128+ algorithm + const uint64_t result = m_state[0] + m_state[1]; + m_state[1] ^= m_state[0]; + m_state[0] = (((m_state[0] << 55) | (m_state[0] >> 9)) ^ m_state[1] ^ (m_state[1] << 14)); + m_state[1] = (m_state[1] << 36) | (m_state[1] >> 28); + return result; +} +uint64_t VlRNG::vl_thread_rng_rand64() VL_MT_SAFE { + VlRNG& fromr = vl_thread_rng(); + const uint64_t result = fromr.m_state[0] + fromr.m_state[1]; + fromr.m_state[1] ^= fromr.m_state[0]; + fromr.m_state[0] = (((fromr.m_state[0] << 55) | (fromr.m_state[0] >> 9)) ^ fromr.m_state[1] + ^ (fromr.m_state[1] << 14)); + fromr.m_state[1] = (fromr.m_state[1] << 36) | (fromr.m_state[1] >> 28); + return result; +} +void VlRNG::srandom(uint64_t n) VL_MT_UNSAFE { + m_state[0] = n; + m_state[1] = m_state[0]; + // Fix state as algorithm is slow to randomize if many zeros + // This causes a loss of ~ 1 bit of seed entropy, no big deal + if (VL_COUNTONES_I(m_state[0]) < 10) m_state[0] = ~m_state[0]; + if (VL_COUNTONES_I(m_state[1]) < 10) m_state[1] = ~m_state[1]; +} +// Unused: void VlRNG::set_randstate(const std::string& state) VL_MT_UNSAFE { +// Unused: if (VL_LIKELY(state.length() == sizeof(m_state))) { +// Unused: memcpy(m_state, state.data(), sizeof(m_state)); +// Unused: } +// Unused: } +// Unused: std::string VlRNG::get_randstate() const VL_MT_UNSAFE { +// Unused: std::string result{reinterpret_cast(&m_state), sizeof(m_state)}; +// Unused: return result; +// Unused: } + static uint32_t vl_sys_rand32() VL_MT_SAFE { // Return random 32-bits using system library. // Used only to construct seed for Verilator's PRNG. @@ -292,27 +338,23 @@ static uint32_t vl_sys_rand32() VL_MT_SAFE { #endif } -uint64_t vl_rand64() VL_MT_SAFE { - static thread_local uint64_t t_state[2]; +VlRNG& VlRNG::vl_thread_rng() VL_MT_SAFE { + static thread_local VlRNG t_rng{0}; static thread_local uint32_t t_seedEpoch = 0; // For speed, we use a thread-local epoch number to know when to reseed // A thread always belongs to a single context, so this works out ok if (VL_UNLIKELY(t_seedEpoch != VerilatedContextImp::randSeedEpoch())) { // Set epoch before state, to avoid race case with new seeding t_seedEpoch = VerilatedContextImp::randSeedEpoch(); - t_state[0] = Verilated::threadContextp()->impp()->randSeedDefault64(); - t_state[1] = t_state[0]; + // Same as srandom() but here as needs to be VL_MT_SAFE + t_rng.m_state[0] = Verilated::threadContextp()->impp()->randSeedDefault64(); + t_rng.m_state[1] = t_rng.m_state[0]; // Fix state as algorithm is slow to randomize if many zeros // This causes a loss of ~ 1 bit of seed entropy, no big deal - if (VL_COUNTONES_I(t_state[0]) < 10) t_state[0] = ~t_state[0]; - if (VL_COUNTONES_I(t_state[1]) < 10) t_state[1] = ~t_state[1]; + if (VL_COUNTONES_I(t_rng.m_state[0]) < 10) t_rng.m_state[0] = ~t_rng.m_state[0]; + if (VL_COUNTONES_I(t_rng.m_state[1]) < 10) t_rng.m_state[1] = ~t_rng.m_state[1]; } - // Xoroshiro128+ algorithm - const uint64_t result = t_state[0] + t_state[1]; - t_state[1] ^= t_state[0]; - t_state[0] = (((t_state[0] << 55) | (t_state[0] >> 9)) ^ t_state[1] ^ (t_state[1] << 14)); - t_state[1] = (t_state[1] << 36) | (t_state[1] >> 28); - return result; + return t_rng; } WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE { @@ -321,6 +363,12 @@ WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE { return outwp; } +WDataOutP VL_RANDOM_RNG_W(VlRNG& rngr, int obits, WDataOutP outwp) VL_MT_UNSAFE { + for (int i = 0; i < VL_WORDS_I(obits); ++i) outwp[i] = rngr.rand64(); + // Last word is unclean + return outwp; +} + IData VL_RANDOM_SEEDED_II(IData& seedr) VL_MT_SAFE { // $random - seed is a new seed to apply, then we return new seed Verilated::threadContextp()->randSeed(static_cast(seedr)); @@ -518,12 +566,12 @@ QData VL_POW_QQW(int, int, int rbits, QData lhs, const WDataInP rwp) VL_MT_SAFE // Skip check for rhs == 0, as short-circuit doesn't save time if (VL_UNLIKELY(lhs == 0)) return 0; QData power = lhs; - QData out = 1ULL; + QData result = 1ULL; for (int bit = 0; bit < rbits; ++bit) { if (bit > 0) power = power * power; - if (VL_BITISSET_W(rwp, bit)) out *= power; + if (VL_BITISSET_W(rwp, bit)) result *= power; } - return out; + return result; } WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP lwp, @@ -645,7 +693,7 @@ std::string VL_DECIMAL_NW(int width, const WDataInP lwp) VL_MT_SAFE { } template -std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t width) { +std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t width) VL_MT_SAFE { const VerilatedContextImp* const ctxImpp = Verilated::threadContextp()->impp(); const std::string suffix = ctxImpp->timeFormatSuffix(); const int userUnits = ctxImpp->timeFormatUnits(); // 0..-15 @@ -1651,15 +1699,15 @@ std::string VL_STACKTRACE_N() VL_MT_SAFE { // cppcheck-suppress knownConditionTrueFalse if (!strings) return "Unable to backtrace\n"; - std::string out = "Backtrace:\n"; - for (int j = 0; j < nptrs; j++) out += std::string{strings[j]} + std::string{"\n"}; + std::string result = "Backtrace:\n"; + for (int j = 0; j < nptrs; j++) result += std::string{strings[j]} + std::string{"\n"}; free(strings); - return out; + return result; } void VL_STACKTRACE() VL_MT_SAFE { - const std::string out = VL_STACKTRACE_N(); - VL_PRINTF("%s", out.c_str()); + const std::string result = VL_STACKTRACE_N(); + VL_PRINTF("%s", result.c_str()); } IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE { @@ -1805,14 +1853,14 @@ std::string VL_TO_STRING_W(int words, const WDataInP obj) { } std::string VL_TOLOWER_NN(const std::string& ld) VL_PURE { - std::string out = ld; - for (auto& cr : out) cr = std::tolower(cr); - return out; + std::string result = ld; + for (auto& cr : result) cr = std::tolower(cr); + return result; } std::string VL_TOUPPER_NN(const std::string& ld) VL_PURE { - std::string out = ld; - for (auto& cr : out) cr = std::toupper(cr); - return out; + std::string result = ld; + for (auto& cr : result) cr = std::toupper(cr); + return result; } std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE { @@ -1871,7 +1919,6 @@ IData VL_ATOI_N(const std::string& str, int base) VL_PURE { IData VL_NTOI_I(int obits, const std::string& str) VL_PURE { return VL_NTOI_Q(obits, str); } QData VL_NTOI_Q(int obits, const std::string& str) VL_PURE { QData out = 0; - const size_t procLen = std::min(str.length(), static_cast(8)); const char* const datap = str.data(); int pos = static_cast(str.length()) - 1; int bit = 0; diff --git a/include/verilated.h b/include/verilated.h index 822fc89fa..ac2d3c773 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -30,6 +30,13 @@ #ifndef VERILATOR_VERILATED_H_ #define VERILATOR_VERILATED_H_ #define VERILATOR_VERILATED_H_INTERNAL_ +#ifdef VERILATOR_INTERNAL_ +// This file contains definition of VerilationMutex that should +// only be used by verilated code. Verilator itself should use +// mutex from V3Mutex.h. Make sure this file isn't included in +// verilator code. +#error "verilated.h should only be included in verilated code" +#endif // clang-format off #include "verilatedos.h" @@ -142,6 +149,15 @@ enum VerilatedVarFlags { VLVF_DPI_CLAY = (1 << 10) // DPI compatible C standard layout }; +//============================================================================= +// Utility functions + +template +inline constexpr size_t roundUpToMultipleOf(size_t value) { + static_assert((N & (N - 1)) == 0, "'N' must be a power of 2"); + return (value + N - 1) & ~(N - 1); +} + //========================================================================= // Mutex and threading support @@ -607,6 +623,7 @@ public: // But for internal use only VerilatedEvalMsgQueue* __Vm_evalMsgQp; explicit VerilatedSyms(VerilatedContext* contextp); // Pass null for default context ~VerilatedSyms(); + VL_UNCOPYABLE(VerilatedSyms); }; //=========================================================================== @@ -945,18 +962,18 @@ void VerilatedContext::timeprecision(int value) VL_MT_SAFE { const VerilatedLockGuard lock{m_mutex}; m_s.m_timeprecision = value; #if VM_SC - const sc_time sc_res = sc_get_time_resolution(); - if (sc_res == sc_time(1, SC_SEC)) { + const sc_core::sc_time sc_res = sc_core::sc_get_time_resolution(); + if (sc_res == sc_core::sc_time(1, sc_core::SC_SEC)) { sc_prec = 0; - } else if (sc_res == sc_time(1, SC_MS)) { + } else if (sc_res == sc_core::sc_time(1, sc_core::SC_MS)) { sc_prec = 3; - } else if (sc_res == sc_time(1, SC_US)) { + } else if (sc_res == sc_core::sc_time(1, sc_core::SC_US)) { sc_prec = 6; - } else if (sc_res == sc_time(1, SC_NS)) { + } else if (sc_res == sc_core::sc_time(1, sc_core::SC_NS)) { sc_prec = 9; - } else if (sc_res == sc_time(1, SC_PS)) { + } else if (sc_res == sc_core::sc_time(1, sc_core::SC_PS)) { sc_prec = 12; - } else if (sc_res == sc_time(1, SC_FS)) { + } else if (sc_res == sc_core::sc_time(1, sc_core::SC_FS)) { sc_prec = 15; } #endif diff --git a/include/verilated.mk.in b/include/verilated.mk.in index 56257b9f4..7a2d51ff6 100644 --- a/include/verilated.mk.in +++ b/include/verilated.mk.in @@ -235,14 +235,16 @@ $(VM_PREFIX)__ALL.a: $(VK_OBJS) $(VM_HIER_LIBS) ifneq ($(VM_DEFAULT_RULES),0) # Anything not in $(VK_SLOW_OBJS) or $(VK_GLOBAL_OBJS), including verilated.o # and user files passed on the Verilator command line use this rule. +# We put OPT_FAST/OPT_SLOW/OPT_GLOBAL before the other flags to +# allow USER_CPPFLAGS to override them %.o: %.cpp - $(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -c -o $@ $< + $(OBJCACHE) $(CXX) $(OPT_FAST) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< $(VK_SLOW_OBJS): %.o: %.cpp - $(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_SLOW) -c -o $@ $< + $(OBJCACHE) $(CXX) $(OPT_SLOW) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< $(VK_GLOBAL_OBJS): %.o: %.cpp - $(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_GLOBAL) -c -o $@ $< + $(OBJCACHE) $(CXX) $(OPT_GLOBAL) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< endif #Default rule embedded in make: diff --git a/include/verilated_cov.cpp b/include/verilated_cov.cpp index 23953173c..7ed7e0031 100644 --- a/include/verilated_cov.cpp +++ b/include/verilated_cov.cpp @@ -208,11 +208,11 @@ private: // Forward to . so we have a whole word const std::string suffix = *bpost ? std::string{bpost + 1} : ""; - std::string out = prefix + "*" + suffix; + std::string result = prefix + "*" + suffix; // cout << "\nch pre="<add_trace_file(this); + sc_core::sc_get_curr_simcontext()->add_trace_file(this); // We want to avoid a depreciated warning, but still be back compatible. // Turning off the message just for this still results in an // annoying "to turn off" message. - const sc_time t1sec{1, SC_SEC}; + const sc_core::sc_time t1sec{1, sc_core::SC_SEC}; if (t1sec.to_default_time_units() != 0) { - const sc_time tunits{1.0 / t1sec.to_default_time_units(), SC_SEC}; + const sc_core::sc_time tunits{1.0 / t1sec.to_default_time_units(), sc_core::SC_SEC}; spTrace()->set_time_unit(tunits.to_string()); } - spTrace()->set_time_resolution(sc_get_time_resolution().to_string()); + spTrace()->set_time_resolution(sc_core::sc_get_time_resolution().to_string()); } /// Destruct, flush, and close the dump ~VerilatedFstSc() override { close(); } @@ -60,7 +60,7 @@ public: // METHODS - for SC kernel // Called by SystemC simulate() void cycle(bool delta_cycle) override { - if (!delta_cycle) this->dump(sc_time_stamp().to_double()); + if (!delta_cycle) this->dump(sc_core::sc_time_stamp().to_double()); } // Override VerilatedFstC. Must be called after starting simulation. @@ -79,7 +79,7 @@ private: // Cadence Incisive has these as abstract functions so we must create them void set_time_unit(int exponent10_seconds) override {} // deprecated #endif - void set_time_unit(double v, sc_time_unit tu) override {} // LCOV_EXCL_LINE + void set_time_unit(double v, sc_core::sc_time_unit tu) override {} // LCOV_EXCL_LINE //-------------------------------------------------- // SystemC 2.1.v1 @@ -96,8 +96,8 @@ private: // Formatting matches that of sc_trace.h // LCOV_EXCL_START #if (SYSTEMC_VERSION >= 20171012) - DECL_TRACE_METHOD_A( sc_event ) - DECL_TRACE_METHOD_A( sc_time ) + DECL_TRACE_METHOD_A( sc_core::sc_event ) + DECL_TRACE_METHOD_A( sc_core::sc_time ) #endif DECL_TRACE_METHOD_A( bool ) diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index bf39e3c12..983f19b4c 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -260,7 +260,7 @@ extern void _vl_debug_print_w(int lbits, WDataInP const iwp) VL_MT_SAFE; #if defined(SYSTEMC_VERSION) /// Return current simulation time // Already defined: extern sc_time sc_time_stamp(); -inline uint64_t vl_time_stamp64() VL_MT_SAFE { return sc_time_stamp().value(); } +inline uint64_t vl_time_stamp64() VL_MT_SAFE { return sc_core::sc_time_stamp().value(); } #else // Non-SystemC # if !defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY) # ifdef VL_TIME_STAMP64 @@ -2201,10 +2201,10 @@ inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs return lhs + rhs; } inline std::string VL_REPLICATEN_NNQ(const std::string& lhs, IData rep) VL_PURE { - std::string out; - out.reserve(lhs.length() * rep); - for (unsigned times = 0; times < rep; ++times) out += lhs; - return out; + std::string result; + result.reserve(lhs.length() * rep); + for (unsigned times = 0; times < rep; ++times) result += lhs; + return result; } inline std::string VL_REPLICATEN_NNI(const std::string& lhs, IData rep) VL_PURE { return VL_REPLICATEN_NNQ(lhs, rep); diff --git a/include/verilated_imp.h b/include/verilated_imp.h index e357ddcc8..dfc1cdf92 100644 --- a/include/verilated_imp.h +++ b/include/verilated_imp.h @@ -148,7 +148,7 @@ public: private: VL_UNCOPYABLE(VerilatedThreadMsgQueue); // METHODS - static VerilatedThreadMsgQueue& threadton() { + static VerilatedThreadMsgQueue& threadton() VL_MT_SAFE { static thread_local VerilatedThreadMsgQueue t_s; return t_s; } diff --git a/include/verilated_sc.h b/include/verilated_sc.h index da6c479ba..81777c3c9 100644 --- a/include/verilated_sc.h +++ b/include/verilated_sc.h @@ -29,7 +29,7 @@ #include "verilatedos.h" -#include "systemc.h" +#include //============================================================================= // For \internal use, get a pointer to m_data in the sc_bv_base class, @@ -37,9 +37,9 @@ // use cast magic to get at it. Saves patching get_datap in SystemC. #define VL_SC_BV_DATAP(bv) (VlScBvExposer::sp_datap(bv)) // This class is thread safe (though most of SystemC is not). -class VlScBvExposer final : public sc_bv_base { +class VlScBvExposer final : public sc_dt::sc_bv_base { public: - static const uint32_t* sp_datap(const sc_bv_base& base) VL_MT_SAFE { + static const uint32_t* sp_datap(const sc_dt::sc_bv_base& base) VL_MT_SAFE { return static_cast(&base)->sp_datatp(); } const uint32_t* sp_datatp() const { return reinterpret_cast(m_data); } diff --git a/include/verilated_std.sv b/include/verilated_std.sv index 1c7e60e0e..9a1153c76 100644 --- a/include/verilated_std.sv +++ b/include/verilated_std.sv @@ -26,93 +26,134 @@ // verilator lint_off TIMESCALEMOD // verilator lint_off UNUSEDSIGNAL package std; - // The process class is not implemented, but it's predeclared here, - // so the linter accepts references to it. - typedef class process; + // The process class is not implemented, but it's predeclared here, + // so the linter accepts references to it. + typedef class process; - class mailbox #(type T); - protected int m_bound; - protected T m_queue[$]; + class mailbox #(type T); + protected int m_bound; + protected T m_queue[$]; - function new(int bound = 0); - m_bound = bound; - endfunction + function new(int bound = 0); + m_bound = bound; + endfunction - function int num(); - return m_queue.size(); - endfunction + function int num(); + return m_queue.size(); + endfunction - task put(T message); - `ifdef VERILATOR_TIMING - if (m_bound != 0) - wait (m_queue.size() < m_bound); + task put(T message); +`ifdef VERILATOR_TIMING + if (m_bound != 0) + wait (m_queue.size() < m_bound); + m_queue.push_back(message); +`endif + endtask + + function int try_put(T message); + if (num() < m_bound) begin m_queue.push_back(message); - `endif - endtask + return 1; + end + return 0; + endfunction - function int try_put(T message); - if (num() < m_bound) begin - m_queue.push_back(message); - return 1; - end - return 0; - endfunction + task get(ref T message); +`ifdef VERILATOR_TIMING + wait (m_queue.size() > 0); + message = m_queue.pop_front(); +`endif + endtask - task get(ref T message); - `ifdef VERILATOR_TIMING - wait (m_queue.size() > 0); + function int try_get(ref T message); + if (num() > 0) begin message = m_queue.pop_front(); - `endif - endtask + return 1; + end + return 0; + endfunction - function int try_get(ref T message); - if (num() > 0) begin - message = m_queue.pop_front(); - return 1; - end - return 0; - endfunction + task peek(ref T message); +`ifdef VERILATOR_TIMING + wait (m_queue.size() > 0); + message = m_queue[0]; +`endif + endtask - task peek(ref T message); - `ifdef VERILATOR_TIMING - wait (m_queue.size() > 0); + function int try_peek(ref T message); + if (num() > 0) begin message = m_queue[0]; - `endif - endtask + return 1; + end + return 0; + endfunction + endclass - function int try_peek(ref T message); - if (num() > 0) begin - message = m_queue[0]; - return 1; - end - return 0; - endfunction - endclass + class semaphore; + protected int m_keyCount; - class semaphore; - protected int m_keyCount; + function new(int keyCount = 0); + m_keyCount = keyCount; + endfunction - function new(int keyCount = 0); - m_keyCount = keyCount; - endfunction + function void put(int keyCount = 1); + m_keyCount += keyCount; + endfunction - function void put(int keyCount = 1); - m_keyCount += keyCount; - endfunction + task get(int keyCount = 1); +`ifdef VERILATOR_TIMING + wait (m_keyCount >= keyCount); + m_keyCount -= keyCount; +`endif + endtask - task get(int keyCount = 1); - `ifdef VERILATOR_TIMING - wait (m_keyCount >= keyCount); + function int try_get(int keyCount = 1); + if (m_keyCount >= keyCount) begin m_keyCount -= keyCount; - `endif - endtask + return 1; + end + return 0; + endfunction + endclass + + class process; + typedef enum { FINISHED, RUNNING, WAITING, SUSPENDED, KILLED } state; + static process _s_global_process; + static function process self(); + // Unsupported, emulating with single process' state + if (!_s_global_process) _s_global_process = new; + return _s_global_process; + endfunction + function state status(); + // Unsupported, emulating with single process' state + return RUNNING; + endfunction + function void kill(); + $error("std::process::kill() not supported"); + endfunction + task await(); + $error("std::process::await() not supported"); + endtask + function void suspend(); + $error("std::process::suspend() not supported"); + endfunction + function void resume(); + $error("std::process::resume() not supported"); + endfunction + // When really implemented, srandom must operates on the process, but for + // now rely on the srandom() that is automatically generated for all + // classes. + // function void srandom(int seed); + // endfunction + function string get_randstate(); + // Could operate on all proceses for now + // No error, as harmless until set_randstate is called + return "NOT_SUPPORTED"; + endfunction + function void set_randstate(string randstate); + $error("std::process::set_randstate() not supported"); + // Could operate on all proceses for now + endfunction + endclass - function int try_get(int keyCount = 1); - if (m_keyCount >= keyCount) begin - m_keyCount -= keyCount; - return 1; - end - return 0; - endfunction - endclass endpackage diff --git a/include/verilated_timing.h b/include/verilated_timing.h index 535839389..019573185 100644 --- a/include/verilated_timing.h +++ b/include/verilated_timing.h @@ -312,16 +312,6 @@ public: } }; -//============================================================================= -// VlNow is a helper awaitable type that always suspends, and then immediately resumes a coroutine. -// Allows forcing the move of coroutine locals to the heap. - -struct VlNow { - bool await_ready() const { return false; } // Always suspend - bool await_suspend(std::coroutine_handle<>) const { return false; } // Resume immediately - void await_resume() const {} -}; - //============================================================================= // VlForever is a helper awaitable type for suspending coroutines forever. Used for constant // wait statements. diff --git a/include/verilated_types.h b/include/verilated_types.h index cb7265e32..0214e8fb4 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -79,9 +79,9 @@ extern std::string VL_TO_STRING_W(int words, const WDataInP obj); template // class VlTriggerVec final { // TODO: static assert T_size > 0, and don't generate when empty -private: + // MEMBERS - std::array m_flags; // State of the assoc array + alignas(16) std::array(T_size) / 64> m_flags; // The flags public: // CONSTRUCTOR @@ -91,10 +91,18 @@ public: // METHODS // Set all elements to false - void clear() { m_flags.fill(false); } + void clear() { m_flags.fill(0); } - // Reference to element at 'index' - bool& at(size_t index) { return m_flags.at(index); } + // Word at given 'wordIndex' + uint64_t word(size_t wordIndex) const { return m_flags[wordIndex]; } + + // Set specified flag to given value + void set(size_t index, bool value) { + uint64_t& w = m_flags[index / 64]; + const size_t bitIndex = index % 64; + w &= ~(1ULL << bitIndex); + w |= (static_cast(value) << bitIndex); + } // Return true iff at least one element is set bool any() const { @@ -104,13 +112,13 @@ public: } // Set all elements true in 'this' that are set in 'other' - void set(const VlTriggerVec& other) { + void thisOr(const VlTriggerVec& other) { for (size_t i = 0; i < m_flags.size(); ++i) m_flags[i] |= other.m_flags[i]; } // Set elements of 'this' to 'a & !b' element-wise void andNot(const VlTriggerVec& a, const VlTriggerVec& b) { - for (size_t i = 0; i < m_flags.size(); ++i) m_flags[i] = a.m_flags[i] && !b.m_flags[i]; + for (size_t i = 0; i < m_flags.size(); ++i) m_flags[i] = a.m_flags[i] & ~b.m_flags[i]; } }; @@ -136,14 +144,34 @@ public: }; inline std::string VL_TO_STRING(const VlEvent& e) { - return std::string("triggered=") + (e.isTriggered() ? "true" : "false"); + return std::string{"triggered="} + (e.isTriggered() ? "true" : "false"); } //=================================================================== -// Shuffle RNG +// Random -extern uint64_t vl_rand64() VL_MT_SAFE; +// Random Number Generator with internal state +class VlRNG final { + std::array m_state; +public: + // The default constructor simply sets state, to avoid vl_rand64() + // having to check for construction at each call + // Alternative: seed with zero and check on rand64() call + VlRNG() VL_MT_SAFE; + explicit VlRNG(uint64_t seed0) VL_MT_SAFE : m_state{0x12341234UL, seed0} {} + void srandom(uint64_t n) VL_MT_UNSAFE; + // Unused: std::string get_randstate() const VL_MT_UNSAFE; + // Unused: void set_randstate(const std::string& state) VL_MT_UNSAFE; + uint64_t rand64() VL_MT_UNSAFE; + // Threadsafe, but requires use on vl_thread_rng + static uint64_t vl_thread_rng_rand64() VL_MT_SAFE; + static VlRNG& vl_thread_rng() VL_MT_SAFE; +}; + +inline uint64_t vl_rand64() VL_MT_SAFE { return VlRNG::vl_thread_rng_rand64(); } + +// RNG for shuffle() class VlURNG final { public: using result_type = size_t; @@ -152,6 +180,11 @@ public: size_t operator()() { return VL_MASK_I(31) & vl_rand64(); } }; +// These require the class object to have the thread safety lock +inline IData VL_RANDOM_RNG_I(VlRNG& rngr) VL_MT_UNSAFE { return rngr.rand64(); } +inline QData VL_RANDOM_RNG_Q(VlRNG& rngr) VL_MT_UNSAFE { return rngr.rand64(); } +extern WDataOutP VL_RANDOM_RNG_W(VlRNG& rngr, int obits, WDataOutP outwp) VL_MT_UNSAFE; + //=================================================================== // Readmem/Writemem operation classes @@ -370,18 +403,17 @@ public: // Can't just overload operator[] or provide a "at" reference to set, // because we need to be able to insert only when the value is set T_Value& at(int32_t index) { - static thread_local T_Value s_throwAway; + static thread_local T_Value t_throwAway; // Needs to work for dynamic arrays, so does not use T_MaxSize if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) { - s_throwAway = atDefault(); - return s_throwAway; + t_throwAway = atDefault(); + return t_throwAway; } else { return m_deque[index]; } } // Accessing. Verilog: v = assoc[index] const T_Value& at(int32_t index) const { - static thread_local T_Value s_throwAway; // Needs to work for dynamic arrays, so does not use T_MaxSize if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) { return atDefault(); @@ -389,6 +421,10 @@ public: return m_deque[index]; } } + // Access with an index counted from end (e.g. q[$]) + T_Value& atBack(int32_t index) { return at(m_deque.size() - 1 - index); } + const T_Value& atBack(int32_t index) const { return at(m_deque.size() - 1 - index); } + // function void q.insert(index, value); void insert(int32_t index, const T_Value& value) { if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return; @@ -404,6 +440,12 @@ public: for (int32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]); return out; } + VlQueue sliceFrontBack(int32_t lsb, int32_t msb) const { + return slice(lsb, m_deque.size() - 1 - msb); + } + VlQueue sliceBackBack(int32_t lsb, int32_t msb) const { + return slice(m_deque.size() - 1 - lsb, m_deque.size() - 1 - msb); + } // For save/restore const_iterator begin() const { return m_deque.begin(); } @@ -1166,7 +1208,8 @@ public: // CONSTRUCTORS VlClassRef() = default; // Init with nullptr - explicit VlClassRef(VlNull){}; + // cppcheck-suppress noExplicitConstructor + VlClassRef(VlNull){}; template VlClassRef(VlDeleter& deleter, T_Args&&... args) // () required here to avoid narrowing conversion warnings, @@ -1188,6 +1231,16 @@ public: // cppcheck-suppress noExplicitConstructor VlClassRef(VlClassRef&& moved) : m_objp{vlstd::exchange(moved.m_objp, nullptr)} {} + // cppcheck-suppress noExplicitConstructor + template + VlClassRef(const VlClassRef& copied) + : m_objp{copied.m_objp} { + refCountInc(); + } + // cppcheck-suppress noExplicitConstructor + template + VlClassRef(VlClassRef&& moved) + : m_objp{vlstd::exchange(moved.m_objp, nullptr)} {} ~VlClassRef() { refCountDec(); } // METHODS diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index f1d93a183..e9efd953c 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -61,16 +61,6 @@ constexpr unsigned VL_TRACE_MAX_VCD_CODE_SIZE = 5; // Maximum length of a VCD s // cache-lines. constexpr unsigned VL_TRACE_SUFFIX_ENTRY_SIZE = 8; // Size of a suffix entry -//============================================================================= -// Utility functions: TODO: put these in a common place and share them. - -template -static size_t roundUpToMultipleOf(size_t value) { - static_assert((N & (N - 1)) == 0, "'N' must be a power of 2"); - size_t mask = N - 1; - return (value + mask) & ~mask; -} - //============================================================================= // Specialization of the generics for this trace format diff --git a/include/verilated_vcd_sc.h b/include/verilated_vcd_sc.h index 4ef558fb4..53d5efe43 100644 --- a/include/verilated_vcd_sc.h +++ b/include/verilated_vcd_sc.h @@ -36,23 +36,23 @@ /// with the SystemC simulation kernel, just like a SystemC-documented /// trace format. -class VerilatedVcdSc final : sc_trace_file, public VerilatedVcdC { +class VerilatedVcdSc final : sc_core::sc_trace_file, public VerilatedVcdC { // CONSTRUCTORS VL_UNCOPYABLE(VerilatedVcdSc); public: /// Construct a SC trace object, and register with the SystemC kernel VerilatedVcdSc() { - sc_get_curr_simcontext()->add_trace_file(this); + sc_core::sc_get_curr_simcontext()->add_trace_file(this); // We want to avoid a depreciated warning, but still be back compatible. // Turning off the message just for this still results in an // annoying "to turn off" message. - const sc_time t1sec{1, SC_SEC}; + const sc_core::sc_time t1sec{1, sc_core::SC_SEC}; if (t1sec.to_default_time_units() != 0) { - const sc_time tunits{1.0 / t1sec.to_default_time_units(), SC_SEC}; + const sc_core::sc_time tunits{1.0 / t1sec.to_default_time_units(), sc_core::SC_SEC}; spTrace()->set_time_unit(tunits.to_string()); } - spTrace()->set_time_resolution(sc_get_time_resolution().to_string()); + spTrace()->set_time_resolution(sc_core::sc_get_time_resolution().to_string()); } /// Destruct, flush, and close the dump ~VerilatedVcdSc() override { close(); } @@ -60,7 +60,7 @@ public: // METHODS - for SC kernel // Called by SystemC simulate() void cycle(bool delta_cycle) override { - if (!delta_cycle) this->dump(sc_time_stamp().to_double()); + if (!delta_cycle) this->dump(sc_core::sc_time_stamp().to_double()); } // Override VerilatedVcdC. Must be called after starting simulation. @@ -78,7 +78,7 @@ private: // Cadence Incisive has these as abstract functions so we must create them void set_time_unit(int exponent10_seconds) override {} // deprecated #endif - void set_time_unit(double v, sc_time_unit tu) override {} // LCOV_EXCL_LINE + void set_time_unit(double v, sc_core::sc_time_unit tu) override {} // LCOV_EXCL_LINE //-------------------------------------------------- // SystemC 2.1.v1 @@ -95,8 +95,8 @@ private: // Formatting matches that of sc_trace.h // LCOV_EXCL_START #if (SYSTEMC_VERSION >= 20171012) - DECL_TRACE_METHOD_A( sc_event ) - DECL_TRACE_METHOD_A( sc_time ) + DECL_TRACE_METHOD_A( sc_core::sc_event ) + DECL_TRACE_METHOD_A( sc_core::sc_time ) #endif DECL_TRACE_METHOD_A( bool ) diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 4c255ed79..4d84eca49 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -791,7 +791,6 @@ void VerilatedVpiImp::dumpCbs() VL_MT_UNSAFE_ONE { } for (auto& ifuture : s().m_nextCbs) { const QData time = ifuture.first.first; - const uint64_t id = ifuture.first.second; VerilatedVpiCbHolder& ho = ifuture.second; if (VL_UNLIKELY(!ho.invalid())) { VL_DBG_MSGF("- vpi: time=%" PRId64 "(NEXT) reason=%d=%s id=%" PRId64 "\n", time, @@ -802,7 +801,6 @@ void VerilatedVpiImp::dumpCbs() VL_MT_UNSAFE_ONE { } for (auto& ifuture : s().m_futureCbs) { const QData time = ifuture.first.first; - const uint64_t id = ifuture.first.second; VerilatedVpiCbHolder& ho = ifuture.second; if (VL_UNLIKELY(!ho.invalid())) { VL_DBG_MSGF("- vpi: time=%" PRId64 " reason=%d=%s id=%" PRId64 "\n", time, diff --git a/include/verilatedos.h b/include/verilatedos.h index 7efd61e21..625552d4f 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -160,6 +160,13 @@ #else # define VL_MT_SAFE #endif +// Comment tag that function is threadsafe, only if +// other threads doesn't change tree topology +#if defined(__clang__) +# define VL_MT_STABLE __attribute__((annotate("MT_STABLE"))) +#else +# define VL_MT_STABLE +#endif // Comment tag that function is threadsafe, only // during normal operation (post-init) #if defined(__clang__) @@ -596,8 +603,12 @@ reverse_wrapper reverse_view(const T& v) { } // C++17's std::as_const +// `VL_MT_SAFE` annotation only applies to this function. +// Object that is returned by this function is not considered +// as MT_SAFE and any function call on this object still +// needs to be `VL_MT_SAFE`. template -T const& as_const(T& v) { +T const& as_const(T& v) VL_MT_SAFE { return v; } diff --git a/nodist/clang_check_attributes b/nodist/clang_check_attributes index 5c6aec2df..36c5be05f 100755 --- a/nodist/clang_check_attributes +++ b/nodist/clang_check_attributes @@ -37,6 +37,7 @@ def fully_qualified_name(node): class VlAnnotations: mt_start: bool = False mt_safe: bool = False + stable_tree: bool = False mt_safe_postinit: bool = False mt_unsafe: bool = False mt_unsafe_one: bool = False @@ -48,12 +49,17 @@ class VlAnnotations: release: bool = False def is_mt_safe_context(self): - return (not (self.mt_unsafe or self.mt_unsafe_one) - and (self.mt_safe or self.mt_start)) + return self.mt_safe and not (self.mt_unsafe or self.mt_unsafe_one) def is_pure_context(self): return self.pure + def is_stabe_tree_context(self): + # stable tree context requires calls to be marked + # as MT_SAFE or MT_STABLE + # Functions in MT_START needs to be MT_SAFE or MT_STABLE + return self.stable_tree or self.mt_start + def is_mt_unsafe_call(self): return self.mt_unsafe or self.mt_unsafe_one @@ -66,6 +72,9 @@ class VlAnnotations: def is_pure_call(self): return self.pure + def is_stabe_tree_call(self): + return self.stable_tree + def __or__(self, other: "VlAnnotations"): result = VlAnnotations() for key, value in dataclasses.asdict(self).items(): @@ -94,6 +103,8 @@ class VlAnnotations: result.mt_start = True elif node.displayname == "MT_SAFE": result.mt_safe = True + elif node.displayname == "MT_STABLE": + result.stable_tree = True elif node.displayname == "MT_SAFE_POSTINIT": result.mt_safe_postinit = True elif node.displayname == "MT_UNSAFE": @@ -196,6 +207,7 @@ class DiagnosticKind(Enum): ANNOTATIONS_DEF_DECL_MISMATCH = enum.auto() NON_PURE_CALL_IN_PURE_CTX = enum.auto() NON_MT_SAFE_CALL_IN_MT_SAFE_CTX = enum.auto() + NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX = enum.auto() def __lt__(self, other): return self.value < other.value @@ -377,6 +389,17 @@ class CallAnnotationsValidator: FunctionInfo.from_node(refd, refd, annotations), DiagnosticKind.NON_MT_SAFE_CALL_IN_MT_SAFE_CTX) + # stable tree context + if ctx.is_stabe_tree_context(): + if annotations.is_mt_unsafe_call() or not ( + annotations.is_stabe_tree_call() + or annotations.is_pure_call() + or self.check_mt_safe_call(node, refd, annotations)): + self.emit_diagnostic( + FunctionInfo.from_node(refd, refd, annotations), + DiagnosticKind.NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX) + + # pure context if ctx.is_pure_context(): if not annotations.is_pure_call(): self.emit_diagnostic( @@ -394,6 +417,17 @@ class CallAnnotationsValidator: self.emit_diagnostic( FunctionInfo.from_node(refd, refd, annotations), DiagnosticKind.NON_MT_SAFE_CALL_IN_MT_SAFE_CTX) + + # stable tree context + if ctx.is_stabe_tree_context(): + if annotations.is_mt_unsafe_call() or not ( + annotations.is_pure_call() + or annotations.is_mt_safe_call() + or annotations.is_stabe_tree_call()): + self.emit_diagnostic( + FunctionInfo.from_node(refd, refd, annotations), + DiagnosticKind.NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX) + # pure context if ctx.is_pure_context(): if not annotations.is_pure_call(): @@ -414,6 +448,13 @@ class CallAnnotationsValidator: self.dispatch_node_inside_definition) self._constructor_context -= 1 + # stable tree context + if ctx.is_stabe_tree_context(): + self._constructor_context += 1 + self.iterate_children(refd.get_children(), + self.dispatch_node_inside_definition) + self._constructor_context -= 1 + # pure context if ctx.is_pure_context(): if not annotations.is_pure_call( @@ -428,11 +469,19 @@ class CallAnnotationsValidator: if not supported: self.iterate_children(node.get_children(), self.dispatch_node_inside_definition) - return + return True assert refd is not None if self._is_ignored_call(refd): - return + return True + + if "std::function" in refd.displayname: + # Workaroud for missing support for lambda annotations + # in c++11. + # If function takes std::function as argument, + # assume, that this std::function will be called inside it. + self.process_function_definition(node) + return False assert self._call_location is not None node_file = os.path.abspath(node.location.file.name) @@ -444,47 +493,40 @@ class CallAnnotationsValidator: or refd.kind == CursorKind.CXX_METHOD and refd.is_static_method()): self.process_function_call(refd, annotations) - self.iterate_children(node.get_children(), - self.dispatch_node_inside_definition) - return # Function pointer - if refd.kind in [ + elif refd.kind in [ CursorKind.VAR_DECL, CursorKind.FIELD_DECL, CursorKind.PARM_DECL ]: self.process_function_call(refd, annotations) - self.iterate_children(node.get_children(), - self.dispatch_node_inside_definition) - return # Non-static class methods - if refd.kind == CursorKind.CXX_METHOD: + elif refd.kind == CursorKind.CXX_METHOD: self.process_method_call(node, refd, annotations) - self.iterate_children(node.get_children(), - self.dispatch_node_inside_definition) - return # Conversion method (e.g. `operator int()`) - if refd.kind == CursorKind.CONVERSION_FUNCTION: + elif refd.kind == CursorKind.CONVERSION_FUNCTION: self.process_method_call(node, refd, annotations) - self.iterate_children(node.get_children(), - self.dispatch_node_inside_definition) - return # Constructors - if refd.kind == CursorKind.CONSTRUCTOR: + elif refd.kind == CursorKind.CONSTRUCTOR: self.process_constructor_call(refd, annotations) - self.iterate_children(node.get_children(), - self.dispatch_node_inside_definition) - return - - # Ignore other callables - print(f"{refd.location.file.name}:{refd.location.line}: " - f"{refd.displayname} {refd.kind}\n" - f" from: {node.location.file.name}:{node.location.line}") + else: + # Ignore other callables, but report them + print("Unknown callable: " + f"{refd.location.file.name}:{refd.location.line}: " + f"{refd.displayname} {refd.kind}\n" + f" from: {node.location.file.name}:{node.location.line}") + return True # Definition handling def dispatch_node_inside_definition(self, node: clang.cindex.Cursor): if node.kind == CursorKind.CALL_EXPR: - self.dispatch_call_node(node) + if self.dispatch_call_node(node) is False: + return None + elif node.is_definition() and node.kind in [ + CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL, + CursorKind.CONSTRUCTOR, CursorKind.CONVERSION_FUNCTION + ]: + self.process_function_definition(node) return None return self.iterate_children(node.get_children(), @@ -519,12 +561,14 @@ class CallAnnotationsValidator: # for callees validation. self._caller = FunctionInfo.from_node(node, refd, def_annotations | annotations) + prev_call_location = self._call_location self._call_location = self._caller self.iterate_children(node_children, self.dispatch_node_inside_definition) - self._call_location = None + self._call_location = prev_call_location + self._caller = prev_call_location return None @@ -788,6 +832,8 @@ class TopDownSummaryPrinter(): name += "is mtsafe but calls non-mtsafe function(s)" elif func.reason == DiagnosticKind.NON_PURE_CALL_IN_PURE_CTX: name += "is pure but calls non-pure function(s)" + elif func.reason == DiagnosticKind.NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX: + name += "is stable_tree but calls non-stable_tree or non-mtsafe" else: name += "for unknown reason (please add description)" diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 1db782e52..617a8880c 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -92,6 +92,7 @@ endif # -lfl not needed as Flex invoked with %nowrap option LIBS = $(CFG_LIBS) -lm +CPPFLAGS += -DVERILATOR_INTERNAL_ CPPFLAGS += -MMD CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir) -I../../include #CPPFLAGS += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool diff --git a/src/V3Active.cpp b/src/V3Active.cpp index 68ef3dbf1..87194907f 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -54,7 +54,7 @@ private: const string m_name; // Only used for .dot file generation const VertexType m_type; // Vertex type (BLOCK/BRANCH/OUTPUT) - string typestr() const { // " + string typestr() const VL_MT_SAFE { // " switch (m_type) { case VT_BLOCK: return "(||)"; // basic block node case VT_BRANCH: return "(&&)"; // if/else branch mode @@ -68,7 +68,7 @@ public: : V3GraphVertex{graphp} , m_name{name} , m_type{type} {} - string name() const override { return m_name + " " + typestr(); } + string name() const override VL_MT_STABLE { return m_name + " " + typestr(); } string dotColor() const override { return user() ? "green" : "black"; } virtual int type() const { return m_type; } }; @@ -309,7 +309,7 @@ AstActive*& ActiveNamer::getSpecialActive() { //###################################################################### // Latch checking visitor -class ActiveLatchCheckVisitor final : public VNVisitor { +class ActiveLatchCheckVisitor final : public VNVisitorConst { private: // NODE STATE // Input: @@ -329,20 +329,20 @@ private: LatchDetectGraphVertex* const parentp = m_graph.currentp(); LatchDetectGraphVertex* const branchp = m_graph.addPathVertex(parentp, "BRANCH", true); m_graph.addPathVertex(branchp, "IF"); - iterateAndNextNull(nodep->thensp()); + iterateAndNextConstNull(nodep->thensp()); m_graph.addPathVertex(branchp, "ELSE"); - iterateAndNextNull(nodep->elsesp()); + iterateAndNextConstNull(nodep->elsesp()); m_graph.currentp(parentp); } } //-------------------- - void visit(AstNode* nodep) override { iterateChildren(nodep); } + void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } public: // CONSTRUCTORS ActiveLatchCheckVisitor(AstNode* nodep, bool expectLatch) { m_graph.begin(); - iterate(nodep); + iterateConst(nodep); m_graph.latchCheck(nodep, expectLatch); } ~ActiveLatchCheckVisitor() override = default; diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index ac8d96a42..15969106a 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -51,14 +51,22 @@ private: bool m_inSampled = false; // True inside a sampled expression // METHODS - string assertDisplayMessage(AstNode* nodep, const string& prefix, const string& message) { - return (string("[%0t] " + prefix + ": ") + nodep->fileline()->filebasename() + ":" - + cvtToStr(nodep->fileline()->lineno()) + ": Assertion failed in %m" - + ((message != "") ? ": " : "") + message + "\n"); + string assertDisplayMessage(AstNode* nodep, const string& prefix, const string& message, + VDisplayType severity) { + if (severity == VDisplayType::DT_ERROR || severity == VDisplayType::DT_FATAL) { + return (string{"[%0t] " + prefix + ": "} + nodep->fileline()->filebasename() + ":" + + cvtToStr(nodep->fileline()->lineno()) + ": Assertion failed in %m" + + ((message != "") ? ": " : "") + message + "\n"); + } else { + return (string{"[%0t] " + prefix + ": "} + nodep->fileline()->filebasename() + ":" + + cvtToStr(nodep->fileline()->lineno()) + ": %m" + + ((message != "") ? ": " : "") + message + "\n"); + } } void replaceDisplay(AstDisplay* nodep, const string& prefix) { + nodep->fmtp()->text( + assertDisplayMessage(nodep, prefix, nodep->fmtp()->text(), nodep->displayType())); nodep->displayType(VDisplayType::DT_WRITE); - nodep->fmtp()->text(assertDisplayMessage(nodep, prefix, nodep->fmtp()->text())); // cppcheck-suppress nullPointer AstNodeExpr* const timenewp = new AstTime{nodep->fileline(), m_modp->timeunit()}; if (AstNodeExpr* const timesp = nodep->fmtp()->exprsp()) { @@ -182,7 +190,7 @@ private: const bool force = VN_IS(nodep, AssertIntrinsic); if (passsp) passsp = newIfAssertOn(passsp, force); if (failsp) failsp = newIfAssertOn(failsp, force); - if (!failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed."); + if (!passsp && !failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed."); ifp = new AstIf{nodep->fileline(), propp, passsp, failsp}; ifp->isBoundsCheck(true); // To avoid LATCH warning // It's more LIKELY that we'll take the nullptr if clause @@ -419,9 +427,10 @@ private: replaceDisplay(nodep, "-Info"); } else if (nodep->displayType() == VDisplayType::DT_WARNING) { replaceDisplay(nodep, "%%Warning"); - } else if (nodep->displayType() == VDisplayType::DT_ERROR - || nodep->displayType() == VDisplayType::DT_FATAL) { + } else if (nodep->displayType() == VDisplayType::DT_ERROR) { replaceDisplay(nodep, "%%Error"); + } else if (nodep->displayType() == VDisplayType::DT_FATAL) { + replaceDisplay(nodep, "%%Fatal"); } else if (nodep->displayType() == VDisplayType::DT_MONITOR) { nodep->displayType(VDisplayType::DT_DISPLAY); const auto fl = nodep->fileline(); diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index 8221ee0ab..51a12ad14 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -229,7 +229,7 @@ private: // always queue.push(); AstCMethodHard* const pushp = new AstCMethodHard{ flp, new AstVarRef{flp, queueVarp, VAccess::WRITE}, "push", - new AstTime(nodep->fileline(), m_modp->timeunit())}; + new AstTime{nodep->fileline(), m_modp->timeunit()}}; pushp->addPinsp(exprp->cloneTree(false)); pushp->dtypeSetVoid(); m_clockingp->addNextHere( @@ -238,7 +238,7 @@ private: // always @ queue.pop(, /*out*/}); AstCMethodHard* const popp = new AstCMethodHard{ flp, new AstVarRef{flp, queueVarp, VAccess::READWRITE}, "pop", - new AstTime(nodep->fileline(), m_modp->timeunit())}; + new AstTime{nodep->fileline(), m_modp->timeunit()}}; popp->addPinsp(skewp->unlinkFrBack()); popp->addPinsp(refp); popp->dtypeSetVoid(); diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index db5722c64..dcc43148c 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -125,7 +125,9 @@ string AstNode::encodeNumber(int64_t num) { } } -string AstNode::nameProtect() const { return VIdProtect::protectIf(name(), protect()); } +string AstNode::nameProtect() const VL_MT_STABLE { + return VIdProtect::protectIf(name(), protect()); +} string AstNode::origNameProtect() const { return VIdProtect::protectIf(origName(), protect()); } string AstNode::shortName() const { @@ -155,7 +157,7 @@ string AstNode::vcdName(const string& namein) { return prettyName(pretty); } -string AstNode::prettyName(const string& namein) { +string AstNode::prettyName(const string& namein) VL_PURE { // This function is somewhat hot, so we short-circuit some compares string pretty; pretty.reserve(namein.length()); @@ -283,7 +285,7 @@ string AstNode::vpiName(const string& namein) { return pretty; } -string AstNode::prettyTypeName() const { +string AstNode::prettyTypeName() const VL_MT_STABLE { if (name() == "") return typeName(); return std::string{typeName()} + " '" + prettyName() + "'"; } @@ -296,17 +298,19 @@ void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, int line // Called on all major tree changers. // Only for use for those really nasty bugs relating to internals // Note this may be null. -// if (debug()) cout<<"-treeChange: V3Ast.cpp:"<"<" +// << "m_iterpp=" << (void*)nodep->m_iterpp << endl; // if (debug()) { // cout<<"-treeChange: V3Ast.cpp:"<dumpTree("- treeChange: "); -// if (next||1) this->dumpTreeAndNext(cout, prefix); -// else this->dumpTree(prefix); -// this->checkTree(); +// if (next||1) nodep->dumpTreeAndNext(cout, prefix); +// else nodep->dumpTree(prefix); +// nodep->checkTree(); // v3Global.rootp()->checkTree(); //} #endif @@ -345,7 +349,8 @@ AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) { newtailp->m_headtailp = headp; headp->m_headtailp = newtailp; newp->editCountInc(); - if (oldtailp->m_iterpp) *(oldtailp->m_iterpp) = newp; // Iterate on new item + // No change of m_iterpp, as only changing m_nextp of current node; + // the current node is still the one at the iteration point } debugTreeChange(nodep, "-addNextOut:", __LINE__, true); return nodep; @@ -395,7 +400,8 @@ void AstNode::addNextHere(AstNode* newp) { } // else is head, and we're inserting into the middle, so no other change } - if (this->m_iterpp) *(this->m_iterpp) = newp; // Iterate on new item + // No change of m_iterpp, as adding after current node; + // the current node is still the one at the iteration point debugTreeChange(this, "-addHereOut: ", __LINE__, true); } @@ -563,8 +569,10 @@ AstNode* AstNode::unlinkFrBackWithNext(VNRelinker* linkerp) { // Relink oldp->m_backp = nullptr; // Iterator fixup - if (oldp->m_iterpp) *(oldp->m_iterpp) = nullptr; - oldp->m_iterpp = nullptr; + if (oldp->m_iterpp) { + *(oldp->m_iterpp) = nullptr; + oldp->m_iterpp = nullptr; + } debugTreeChange(oldp, "-unlinkWNextOut: ", __LINE__, true); return oldp; } @@ -578,7 +586,11 @@ AstNode* AstNode::unlinkFrBack(VNRelinker* linkerp) { if (linkerp) { linkerp->m_oldp = oldp; linkerp->m_backp = backp; - linkerp->m_iterpp = oldp->m_iterpp; + if (oldp->m_iterpp) { // Assumes we will always relink() if want to keep iterating + linkerp->m_iterpp = oldp->m_iterpp; + *(oldp->m_iterpp) = nullptr; + oldp->m_iterpp = nullptr; + } if (backp->m_nextp == oldp) { linkerp->m_chg = VNRelinker::RELINK_NEXT; } else if (backp->m_op1p == oldp) { @@ -623,12 +635,15 @@ AstNode* AstNode::unlinkFrBack(VNRelinker* linkerp) { } } // Iterator fixup - if (oldp->m_iterpp) *(oldp->m_iterpp) = oldp->m_nextp; + if (oldp->m_iterpp) { // Only if no linker, point to next in list + if (oldp->m_nextp) oldp->m_nextp->m_iterpp = oldp->m_iterpp; + *(oldp->m_iterpp) = oldp->m_nextp; + oldp->m_iterpp = nullptr; + } // Relink oldp->m_nextp = nullptr; oldp->m_backp = nullptr; - oldp->m_headtailp = this; - oldp->m_iterpp = nullptr; + oldp->m_headtailp = oldp; debugTreeChange(oldp, "-unlinkFrBkOut: ", __LINE__, true); return oldp; } @@ -666,12 +681,12 @@ void AstNode::relink(VNRelinker* linkerp) { // Iterator fixup if (linkerp->m_iterpp) { // If we're iterating over a next() link, we need to follow links off the - // NEW node. Thus we pass iteration information via a pointer in the node. + // NEW node, which is always assumed to be what we are relinking to. // This adds a unfortunate hot 8 bytes to every AstNode, but is faster than passing // across every function. // If anyone has a cleaner way, I'd be grateful. - *(linkerp->m_iterpp) = newp; newp->m_iterpp = linkerp->m_iterpp; + *(newp->m_iterpp) = newp; } // Empty the linker so not used twice accidentally linkerp->m_backp = nullptr; @@ -833,6 +848,7 @@ void AstNode::deleteNode() { this->m_op2p = reinterpret_cast(0x1); this->m_op3p = reinterpret_cast(0x1); this->m_op4p = reinterpret_cast(0x1); + this->m_iterpp = reinterpret_cast(0x1); if ( #if !defined(VL_DEBUG) || defined(VL_LEAK_CHECKS) true @@ -907,7 +923,7 @@ void AstNode::iterateChildren(VNVisitor& v) { if (m_op4p) m_op4p->iterateAndNext(v); } -void AstNode::iterateChildrenConst(VNVisitor& v) { +void AstNode::iterateChildrenConst(VNVisitorConst& v) { // This is a very hot function ASTNODE_PREFETCH(m_op1p); ASTNODE_PREFETCH(m_op2p); @@ -948,13 +964,13 @@ void AstNode::iterateAndNext(VNVisitor& v) { niterp->m_iterpp = nullptr; if (VL_UNLIKELY(niterp != nodep)) { // Edited node inside accept nodep = niterp; - } else { // Unchanged node, just continue loop + } else { // Unchanged node (though maybe updated m_next), just continue loop nodep = niterp->m_nextp; } } } -void AstNode::iterateListBackwards(VNVisitor& v) { +void AstNode::iterateListBackwardsConst(VNVisitorConst& v) { AstNode* nodep = this; while (nodep->m_nextp) nodep = nodep->m_nextp; while (nodep) { @@ -968,14 +984,14 @@ void AstNode::iterateListBackwards(VNVisitor& v) { } } -void AstNode::iterateChildrenBackwards(VNVisitor& v) { - if (m_op1p) m_op1p->iterateListBackwards(v); - if (m_op2p) m_op2p->iterateListBackwards(v); - if (m_op3p) m_op3p->iterateListBackwards(v); - if (m_op4p) m_op4p->iterateListBackwards(v); +void AstNode::iterateChildrenBackwardsConst(VNVisitorConst& v) { + if (m_op1p) m_op1p->iterateListBackwardsConst(v); + if (m_op2p) m_op2p->iterateListBackwardsConst(v); + if (m_op3p) m_op3p->iterateListBackwardsConst(v); + if (m_op4p) m_op4p->iterateListBackwardsConst(v); } -void AstNode::iterateAndNextConst(VNVisitor& v) { +void AstNode::iterateAndNextConst(VNVisitorConst& v) { // Keep following the current list even if edits change it AstNode* nodep = this; do { @@ -1079,7 +1095,7 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig //====================================================================== // Debugging -void AstNode::checkTreeIter(const AstNode* prevBackp) const { +void AstNode::checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE { // private: Check a tree and children UASSERT_OBJ(prevBackp == this->backp(), this, "Back node inconsistent"); switch (this->type()) { @@ -1129,7 +1145,7 @@ void AstNode::checkIter() const { if (m_iterpp) { dumpPtrs(cout); // Perhaps something forgot to clear m_iterpp? - this->v3fatalSrc("Iteration link should be nullptr"); + this->v3fatalSrc("Iteration link m_iterpp should be nullptr"); } } @@ -1342,7 +1358,9 @@ void AstNode::dtypeChgWidthSigned(int width, int widthMin, VSigning numeric) { dtypeSetLogicUnsized(width, widthMin, numeric); } else { if (width == dtypep()->width() && widthMin == dtypep()->widthMin() - && numeric == dtypep()->numeric()) + && numeric == dtypep()->numeric() + // Enums need to become direct sizes to avoid later ENUMVALUE errors + && !VN_IS(dtypep()->skipRefToEnump(), EnumDType)) return; // Correct already // FUTURE: We may be pointing at a two state data type, and this may // convert it to logic. Since the AstVar remains correct, we diff --git a/src/V3Ast.h b/src/V3Ast.h index eb7d345f7..6a286bde8 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -102,11 +102,11 @@ public: : m_e(static_cast(_e)) {} // Need () or GCC 4.8 false warning constexpr operator en() const VL_MT_SAFE { return m_e; } }; -constexpr bool operator==(const VNType& lhs, const VNType& rhs) VL_MT_SAFE { +constexpr bool operator==(const VNType& lhs, const VNType& rhs) VL_PURE { return lhs.m_e == rhs.m_e; } -constexpr bool operator==(const VNType& lhs, VNType::en rhs) { return lhs.m_e == rhs; } -constexpr bool operator==(VNType::en lhs, const VNType& rhs) { return lhs == rhs.m_e; } +constexpr bool operator==(const VNType& lhs, VNType::en rhs) VL_PURE { return lhs.m_e == rhs; } +constexpr bool operator==(VNType::en lhs, const VNType& rhs) VL_PURE { return lhs == rhs.m_e; } inline std::ostream& operator<<(std::ostream& os, const VNType& rhs) { return os << rhs.ascii(); } // ###################################################################### @@ -470,7 +470,7 @@ public: _ENUM_MAX }; enum en m_e; - const char* ascii() const { + const char* ascii() const VL_MT_SAFE { static const char* const names[] = {"%E-unk", "bit", "byte", @@ -1097,21 +1097,21 @@ public: } // VNumRange() = default; - VNumRange(int hi, int lo, bool littleEndian) { init(hi, lo, littleEndian); } + VNumRange(int hi, int lo, bool ascending) { init(hi, lo, ascending); } VNumRange(int left, int right) : m_left{left} , m_right{right} , m_ranged{true} {} ~VNumRange() = default; // MEMBERS - void init(int hi, int lo, bool littleEndian) { + void init(int hi, int lo, bool ascending) { if (lo > hi) { const int t = hi; hi = lo; lo = t; } - m_left = littleEndian ? lo : hi; - m_right = littleEndian ? hi : lo; + m_left = ascending ? lo : hi; + m_right = ascending ? hi : lo; m_ranged = true; } int left() const { return m_left; } @@ -1122,10 +1122,10 @@ public: int lo() const VL_MT_SAFE { return m_left > m_right ? m_right : m_left; } // How to show a declaration - int leftToRightInc() const { return littleEndian() ? 1 : -1; } + int leftToRightInc() const { return ascending() ? 1 : -1; } int elements() const VL_MT_SAFE { return hi() - lo() + 1; } bool ranged() const { return m_ranged; } - bool littleEndian() const { return m_left < m_right; } + bool ascending() const { return m_left < m_right; } int hiMaxSelect() const { return (lo() < 0 ? hi() - lo() : hi()); } // Maximum value a [] select may index @@ -1373,13 +1373,38 @@ public: virtual ~VNDeleter() { doDeletes(); } }; +//###################################################################### +// VNVisitorConst -- Allows new functions to be called on each node +// type without changing the base classes. See "Modern C++ Design". +// This only has the constant fuctions for non-modifying visitors. +// For more typical usage see VNVisitor + +class VNVisitorConst VL_NOT_FINAL : public VNDeleter { + friend class AstNode; + +public: + /// Call visit()s on nodep + inline void iterateConst(AstNode* nodep); + /// Call visit()s on nodep + inline void iterateConstNull(AstNode* nodep); + /// Call visit()s on const nodep's children + inline void iterateChildrenConst(AstNode* nodep); + /// Call visit()s on nodep's children in backp() order + inline void iterateChildrenBackwardsConst(AstNode* nodep); + /// Call visit()s on const nodep (maybe nullptr) and nodep's nextp() list + inline void iterateAndNextConstNull(AstNode* nodep); + /// Call visit()s on const nodep (maybe nullptr) and nodep's nextp() list, in reverse order + inline void iterateAndNextConstNullBackwards(AstNode* nodep); + + virtual void visit(AstNode* nodep) = 0; +#include "V3Ast__gen_visitor_decls.h" // From ./astgen +}; + //###################################################################### // VNVisitor -- Allows new functions to be called on each node // type without changing the base classes. See "Modern C++ Design". -class VNVisitor VL_NOT_FINAL : public VNDeleter { - friend class AstNode; - +class VNVisitor VL_NOT_FINAL : public VNVisitorConst { public: /// Call visit()s on nodep inline void iterate(AstNode* nodep); @@ -1387,21 +1412,10 @@ public: inline void iterateNull(AstNode* nodep); /// Call visit()s on nodep's children inline void iterateChildren(AstNode* nodep); - /// Call visit()s on nodep's children in backp() order - inline void iterateChildrenBackwards(AstNode* nodep); - /// Call visit()s on const nodep's children - inline void iterateChildrenConst(AstNode* nodep); /// Call visit()s on nodep (maybe nullptr) and nodep's nextp() list inline void iterateAndNextNull(AstNode* nodep); - /// Call visit()s on const nodep (maybe nullptr) and nodep's nextp() list - inline void iterateAndNextConstNull(AstNode* nodep); - /// Call visit()s on const nodep (maybe nullptr) and nodep's nextp() list, in reverse order - inline void iterateAndNextConstNullBackwards(AstNode* nodep); /// Return edited nodep; see comments in V3Ast.cpp inline AstNode* iterateSubtreeReturnEdits(AstNode* nodep); - - virtual void visit(AstNode* nodep) = 0; -#include "V3Ast__gen_visitor_decls.h" // From ./astgen }; //###################################################################### @@ -1426,6 +1440,13 @@ protected: public: VNRelinker() = default; + ~VNRelinker() { + // Relink is needed so m_iterpp's get restored, e.g. can't have: + // ->unlinkFrBack(relinker); + // if (only_sometimes) relinker.relink(newp); + UDEBUGONLY( + UASSERT_STATIC(!m_backp, "Active linker must be relink()ed before destruction");); + } inline void relink(AstNode* newp); AstNode* oldp() const { return m_oldp; } void dump(std::ostream& str = std::cout) const; @@ -1545,7 +1566,7 @@ class AstNode VL_NOT_FINAL { private: AstNode* cloneTreeIter(); AstNode* cloneTreeIterList(); - void checkTreeIter(const AstNode* prevBackp) const VL_MT_SAFE; + void checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE; bool gateTreeIter() const; static bool sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ignNext, bool gateOnly); @@ -1601,14 +1622,14 @@ public: // ACCESSORS VNType type() const VL_MT_SAFE { return m_type; } const char* typeName() const VL_MT_SAFE { return type().ascii(); } // See also prettyTypeName - AstNode* nextp() const VL_MT_SAFE { return m_nextp; } - AstNode* backp() const VL_MT_SAFE { return m_backp; } + AstNode* nextp() const VL_MT_STABLE { return m_nextp; } + AstNode* backp() const VL_MT_STABLE { return m_backp; } AstNode* abovep() const; // Parent node above, only when no nextp() as otherwise slow - AstNode* op1p() const VL_MT_SAFE { return m_op1p; } - AstNode* op2p() const VL_MT_SAFE { return m_op2p; } - AstNode* op3p() const VL_MT_SAFE { return m_op3p; } - AstNode* op4p() const VL_MT_SAFE { return m_op4p; } - AstNodeDType* dtypep() const VL_MT_SAFE { return m_dtypep; } + AstNode* op1p() const VL_MT_STABLE { return m_op1p; } + AstNode* op2p() const VL_MT_STABLE { return m_op2p; } + AstNode* op3p() const VL_MT_STABLE { return m_op3p; } + AstNode* op4p() const VL_MT_STABLE { return m_op4p; } + AstNodeDType* dtypep() const VL_MT_STABLE { return m_dtypep; } AstNode* clonep() const { return ((m_cloneCnt == s_cloneCntGbl) ? m_clonep : nullptr); } AstNode* firstAbovep() const { // Returns nullptr when second or later in list return ((backp() && backp()->nextp() != this) ? backp() : nullptr); @@ -1655,7 +1676,7 @@ public: static constexpr int INSTR_COUNT_PLI = 20; // PLI routines // ACCESSORS - virtual string name() const VL_MT_SAFE { return ""; } + virtual string name() const VL_MT_STABLE { return ""; } virtual string origName() const { return ""; } virtual void name(const string& name) { this->v3fatalSrc("name() called on object without name() method"); @@ -1663,22 +1684,23 @@ public: virtual void tag(const string& text) {} virtual string tag() const { return ""; } virtual string verilogKwd() const { return ""; } - string nameProtect() const VL_MT_SAFE; // Name with --protect-id applied + string nameProtect() const VL_MT_STABLE; // Name with --protect-id applied string origNameProtect() const; // origName with --protect-id applied string shortName() const; // Name with __PVT__ removed for concatenating scopes static string dedotName(const string& namein); // Name with dots removed - static string prettyName(const string& namein); // Name for printing out to the user + static string prettyName(const string& namein) VL_PURE; // Name for printing out to the user static string vpiName(const string& namein); // Name for vpi access static string prettyNameQ(const string& namein) { // Quoted pretty name (for errors) return std::string{"'"} + prettyName(namein) + "'"; } - static string - encodeName(const string& namein); // Encode user name into internal C representation + // Encode user name into internal C representation + static string encodeName(const string& namein); static string encodeNumber(int64_t num); // Encode number into internal C representation static string vcdName(const string& namein); // Name for printing out to vcd files - string prettyName() const VL_MT_SAFE { return prettyName(name()); } + string prettyName() const VL_MT_STABLE { return prettyName(name()); } string prettyNameQ() const { return prettyNameQ(name()); } - string prettyTypeName() const; // "VARREF" for error messages (NOT dtype's pretty name) + // "VARREF" for error messages (NOT dtype's pretty name) + string prettyTypeName() const VL_MT_STABLE; virtual string prettyOperatorName() const { return "operator " + prettyTypeName(); } FileLine* fileline() const VL_MT_SAFE { return m_fileline; } void fileline(FileLine* fl) { m_fileline = fl; } @@ -1697,25 +1719,25 @@ public: void protect(bool flag) { m_flags.protect = flag; } // TODO stomp these width functions out, and call via dtypep() instead - inline int width() const VL_MT_SAFE; + inline int width() const VL_MT_STABLE; inline int widthMin() const; int widthMinV() const { return v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH ? widthMin() : width(); } int widthWords() const { return VL_WORDS_I(width()); } - bool isQuad() const VL_MT_SAFE { return (width() > VL_IDATASIZE && width() <= VL_QUADSIZE); } - bool isWide() const VL_MT_SAFE { return (width() > VL_QUADSIZE); } - inline bool isDouble() const; - inline bool isSigned() const; - inline bool isString() const; + bool isQuad() const VL_MT_STABLE { return (width() > VL_IDATASIZE && width() <= VL_QUADSIZE); } + bool isWide() const VL_MT_STABLE { return (width() > VL_QUADSIZE); } + inline bool isDouble() const VL_MT_STABLE; + inline bool isSigned() const VL_MT_STABLE; + inline bool isString() const VL_MT_STABLE; // clang-format off - VNUser user1u() const VL_MT_SAFE { + VNUser user1u() const VL_MT_STABLE { // Slows things down measurably, so disabled by default //UASSERT_STATIC(VNUser1InUse::s_userBusy, "userp set w/o busy"); return ((m_user1Cnt==VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{0}); } - AstNode* user1p() const VL_MT_SAFE { return user1u().toNodep(); } + AstNode* user1p() const VL_MT_STABLE { return user1u().toNodep(); } void user1u(const VNUser& user) { m_user1u=user; m_user1Cnt=VNUser1InUse::s_userCntGbl; } void user1p(void* userp) { user1u(VNUser{userp}); } int user1() const { return user1u().toInt(); } @@ -1724,12 +1746,12 @@ public: int user1SetOnce() { int v=user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc() static void user1ClearTree() { VNUser1InUse::clear(); } // Clear userp()'s across the entire tree - VNUser user2u() const VL_MT_SAFE { + VNUser user2u() const VL_MT_STABLE { // Slows things down measurably, so disabled by default //UASSERT_STATIC(VNUser2InUse::s_userBusy, "userp set w/o busy"); return ((m_user2Cnt==VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{0}); } - AstNode* user2p() const VL_MT_SAFE { return user2u().toNodep(); } + AstNode* user2p() const VL_MT_STABLE { return user2u().toNodep(); } void user2u(const VNUser& user) { m_user2u=user; m_user2Cnt=VNUser2InUse::s_userCntGbl; } void user2p(void* userp) { user2u(VNUser{userp}); } int user2() const { return user2u().toInt(); } @@ -1738,12 +1760,12 @@ public: int user2SetOnce() { int v=user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc() static void user2ClearTree() { VNUser2InUse::clear(); } // Clear userp()'s across the entire tree - VNUser user3u() const VL_MT_SAFE { + VNUser user3u() const VL_MT_STABLE { // Slows things down measurably, so disabled by default //UASSERT_STATIC(VNUser3InUse::s_userBusy, "userp set w/o busy"); return ((m_user3Cnt==VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{0}); } - AstNode* user3p() const VL_MT_SAFE { return user3u().toNodep(); } + AstNode* user3p() const VL_MT_STABLE { return user3u().toNodep(); } void user3u(const VNUser& user) { m_user3u=user; m_user3Cnt=VNUser3InUse::s_userCntGbl; } void user3p(void* userp) { user3u(VNUser{userp}); } int user3() const { return user3u().toInt(); } @@ -1752,12 +1774,12 @@ public: int user3SetOnce() { int v=user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc() static void user3ClearTree() { VNUser3InUse::clear(); } // Clear userp()'s across the entire tree - VNUser user4u() const VL_MT_SAFE { + VNUser user4u() const VL_MT_STABLE { // Slows things down measurably, so disabled by default //UASSERT_STATIC(VNUser4InUse::s_userBusy, "userp set w/o busy"); return ((m_user4Cnt==VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{0}); } - AstNode* user4p() const VL_MT_SAFE { return user4u().toNodep(); } + AstNode* user4p() const VL_MT_STABLE { return user4u().toNodep(); } void user4u(const VNUser& user) { m_user4u=user; m_user4Cnt=VNUser4InUse::s_userCntGbl; } void user4p(void* userp) { user4u(VNUser{userp}); } int user4() const { return user4u().toInt(); } @@ -1766,12 +1788,12 @@ public: int user4SetOnce() { int v=user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc() static void user4ClearTree() { VNUser4InUse::clear(); } // Clear userp()'s across the entire tree - VNUser user5u() const VL_MT_SAFE { + VNUser user5u() const VL_MT_STABLE { // Slows things down measurably, so disabled by default //UASSERT_STATIC(VNUser5InUse::s_userBusy, "userp set w/o busy"); return ((m_user5Cnt==VNUser5InUse::s_userCntGbl) ? m_user5u : VNUser{0}); } - AstNode* user5p() const VL_MT_SAFE { return user5u().toNodep(); } + AstNode* user5p() const VL_MT_STABLE { return user5u().toNodep(); } void user5u(const VNUser& user) { m_user5u=user; m_user5Cnt=VNUser5InUse::s_userCntGbl; } void user5p(void* userp) { user5u(VNUser{userp}); } int user5() const { return user5u().toInt(); } @@ -1907,7 +1929,7 @@ public: // Does tree of this == node2p?, not allowing non-isGateOptimizable inline bool sameGateTree(const AstNode* node2p) const; void deleteTree(); // Always deletes the next link - void checkTree() const VL_MT_SAFE { + void checkTree() const VL_MT_STABLE { if (v3Global.opt.debugCheck()) checkTreeIter(backp()); } void checkIter() const; @@ -1964,26 +1986,27 @@ public: virtual const char* broken() const { return nullptr; } // INVOKERS - virtual void accept(VNVisitor& v) = 0; + virtual void accept(VNVisitorConst& v) = 0; protected: // All VNVisitor related functions are called as methods off the visitor friend class VNVisitor; + friend class VNVisitorConst; // Use instead VNVisitor::iterateChildren void iterateChildren(VNVisitor& v); - // Use instead VNVisitor::iterateChildrenBackwards - void iterateChildrenBackwards(VNVisitor& v); + // Use instead VNVisitor::iterateChildrenBackwardsConst + void iterateChildrenBackwardsConst(VNVisitorConst& v); // Use instead VNVisitor::iterateChildrenConst - void iterateChildrenConst(VNVisitor& v); + void iterateChildrenConst(VNVisitorConst& v); // Use instead VNVisitor::iterateAndNextNull void iterateAndNext(VNVisitor& v); // Use instead VNVisitor::iterateAndNextConstNull - void iterateAndNextConst(VNVisitor& v); + void iterateAndNextConst(VNVisitorConst& v); // Use instead VNVisitor::iterateSubtreeReturnEdits AstNode* iterateSubtreeReturnEdits(VNVisitor& v); private: - void iterateListBackwards(VNVisitor& v); + void iterateListBackwardsConst(VNVisitorConst& v); // For internal use only. // Note: specializations for particular node types are provided by 'astgen' @@ -1992,7 +2015,7 @@ private: // For internal use only. template - constexpr static bool uselessCast() { + constexpr static bool uselessCast() VL_PURE { using NonRef = typename std::remove_reference::type; using NonPtr = typename std::remove_pointer::type; using NonCV = typename std::remove_cv::type; @@ -2001,7 +2024,7 @@ private: // For internal use only. template - constexpr static bool impossibleCast() { + constexpr static bool impossibleCast() VL_PURE { using NonRef = typename std::remove_reference::type; using NonPtr = typename std::remove_pointer::type; using NonCV = typename std::remove_cv::type; @@ -2035,7 +2058,7 @@ public: // For use via the VN_AS macro only template - static T* privateAs(AstNode* nodep) VL_MT_SAFE { + static T* privateAs(AstNode* nodep) VL_PURE { static_assert(!uselessCast(), "Unnecessary VN_AS, node known to have target type."); static_assert(!impossibleCast(), "Unnecessary VN_AS, node cannot be this type."); UASSERT_OBJ(!nodep || privateTypeTest(nodep), nodep, @@ -2044,7 +2067,7 @@ public: return reinterpret_cast(nodep); } template - static const T* privateAs(const AstNode* nodep) VL_MT_SAFE { + static const T* privateAs(const AstNode* nodep) VL_PURE { static_assert(!uselessCast(), "Unnecessary VN_AS, node known to have target type."); static_assert(!impossibleCast(), "Unnecessary VN_AS, node cannot be this type."); UASSERT_OBJ(!nodep || privateTypeTest(nodep), nodep, @@ -2483,24 +2506,29 @@ struct std::equal_to> final { //###################################################################### // Inline VNVisitor METHODS +void VNVisitorConst::iterateConst(AstNode* nodep) { nodep->accept(*this); } +void VNVisitorConst::iterateConstNull(AstNode* nodep) { + if (VL_LIKELY(nodep)) nodep->accept(*this); +} +void VNVisitorConst::iterateChildrenConst(AstNode* nodep) { nodep->iterateChildrenConst(*this); } +void VNVisitorConst::iterateChildrenBackwardsConst(AstNode* nodep) { + nodep->iterateChildrenBackwardsConst(*this); +} +void VNVisitorConst::iterateAndNextConstNullBackwards(AstNode* nodep) { + if (VL_LIKELY(nodep)) nodep->iterateListBackwardsConst(*this); +} +void VNVisitorConst::iterateAndNextConstNull(AstNode* nodep) { + if (VL_LIKELY(nodep)) nodep->iterateAndNextConst(*this); +} + void VNVisitor::iterate(AstNode* nodep) { nodep->accept(*this); } void VNVisitor::iterateNull(AstNode* nodep) { if (VL_LIKELY(nodep)) nodep->accept(*this); } void VNVisitor::iterateChildren(AstNode* nodep) { nodep->iterateChildren(*this); } -void VNVisitor::iterateChildrenBackwards(AstNode* nodep) { - nodep->iterateChildrenBackwards(*this); -} -void VNVisitor::iterateChildrenConst(AstNode* nodep) { nodep->iterateChildrenConst(*this); } void VNVisitor::iterateAndNextNull(AstNode* nodep) { if (VL_LIKELY(nodep)) nodep->iterateAndNext(*this); } -void VNVisitor::iterateAndNextConstNullBackwards(AstNode* nodep) { - if (VL_LIKELY(nodep)) nodep->iterateListBackwards(*this); -} -void VNVisitor::iterateAndNextConstNull(AstNode* nodep) { - if (VL_LIKELY(nodep)) nodep->iterateAndNextConst(*this); -} AstNode* VNVisitor::iterateSubtreeReturnEdits(AstNode* nodep) { return nodep->iterateSubtreeReturnEdits(*this); } diff --git a/src/V3AstInlines.h b/src/V3AstInlines.h index 79023761b..9ca54f137 100644 --- a/src/V3AstInlines.h +++ b/src/V3AstInlines.h @@ -25,7 +25,7 @@ //###################################################################### // Inline METHODS -int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; } +int AstNode::width() const VL_MT_STABLE { return dtypep() ? dtypep()->width() : 0; } int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; } bool AstNode::width1() const { // V3Const uses to know it can optimize return dtypep() && dtypep()->width() == 1; @@ -33,13 +33,13 @@ bool AstNode::width1() const { // V3Const uses to know it can optimize int AstNode::widthInstrs() const { return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1)); } -bool AstNode::isDouble() const VL_MT_SAFE { +bool AstNode::isDouble() const VL_MT_STABLE { return dtypep() && VN_IS(dtypep(), BasicDType) && VN_AS(dtypep(), BasicDType)->isDouble(); } -bool AstNode::isString() const VL_MT_SAFE { +bool AstNode::isString() const VL_MT_STABLE { return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString(); } -bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); } +bool AstNode::isSigned() const VL_MT_STABLE { return dtypep() && dtypep()->isSigned(); } bool AstNode::isZero() const { return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqZero()); @@ -61,12 +61,12 @@ bool AstNode::sameGateTree(const AstNode* node2p) const { return sameTreeIter(this, node2p, true, true); } -int AstNodeArrayDType::left() const VL_MT_SAFE { return rangep()->leftConst(); } -int AstNodeArrayDType::right() const VL_MT_SAFE { return rangep()->rightConst(); } -int AstNodeArrayDType::hi() const VL_MT_SAFE { return rangep()->hiConst(); } -int AstNodeArrayDType::lo() const VL_MT_SAFE { return rangep()->loConst(); } -int AstNodeArrayDType::elementsConst() const VL_MT_SAFE { return rangep()->elementsConst(); } -VNumRange AstNodeArrayDType::declRange() const VL_MT_SAFE { return VNumRange{left(), right()}; } +int AstNodeArrayDType::left() const VL_MT_STABLE { return rangep()->leftConst(); } +int AstNodeArrayDType::right() const VL_MT_STABLE { return rangep()->rightConst(); } +int AstNodeArrayDType::hi() const VL_MT_STABLE { return rangep()->hiConst(); } +int AstNodeArrayDType::lo() const VL_MT_STABLE { return rangep()->loConst(); } +int AstNodeArrayDType::elementsConst() const VL_MT_STABLE { return rangep()->elementsConst(); } +VNumRange AstNodeArrayDType::declRange() const VL_MT_STABLE { return VNumRange{left(), right()}; } AstRange::AstRange(FileLine* fl, int left, int right) : ASTGEN_SUPER_Range(fl) { @@ -78,16 +78,16 @@ AstRange::AstRange(FileLine* fl, const VNumRange& range) leftp(new AstConst{fl, static_cast(range.left())}); rightp(new AstConst{fl, static_cast(range.right())}); } -int AstRange::leftConst() const { +int AstRange::leftConst() const VL_MT_STABLE { AstConst* const constp = VN_CAST(leftp(), Const); return (constp ? constp->toSInt() : 0); } -int AstRange::rightConst() const { +int AstRange::rightConst() const VL_MT_STABLE { AstConst* const constp = VN_CAST(rightp(), Const); return (constp ? constp->toSInt() : 0); } -int AstQueueDType::boundConst() const VL_MT_SAFE { +int AstQueueDType::boundConst() const VL_MT_STABLE { AstConst* const constp = VN_CAST(boundp(), Const); return (constp ? constp->toSInt() : 0); } @@ -123,8 +123,8 @@ int AstBasicDType::lo() const { return (rangep() ? rangep()->loConst() : m.m_nra int AstBasicDType::elements() const { return (rangep() ? rangep()->elementsConst() : m.m_nrange.elements()); } -bool AstBasicDType::littleEndian() const { - return (rangep() ? rangep()->littleEndian() : m.m_nrange.littleEndian()); +bool AstBasicDType::ascending() const { + return (rangep() ? rangep()->ascending() : m.m_nrange.ascending()); } bool AstActive::hasClocked() const { return m_sensesp->hasClocked(); } diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index fd652f595..10d90c33f 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -63,9 +63,9 @@ public: // Integral or packed, allowed inside an unpacked union/struct virtual bool isIntegralOrPacked() const { return !isCompound(); } // (Slow) recurse down to find basic data type - virtual AstBasicDType* basicp() const = 0; + virtual AstBasicDType* basicp() const VL_MT_STABLE = 0; // recurses over typedefs/const/enum to next non-typeref type - virtual AstNodeDType* skipRefp() const = 0; + virtual AstNodeDType* skipRefp() const VL_MT_STABLE = 0; // recurses over typedefs to next non-typeref-or-const type virtual AstNodeDType* skipRefToConstp() const = 0; // recurses over typedefs/const to next non-typeref-or-enum/struct type @@ -86,7 +86,7 @@ public: // Assignable equivalence. Call skipRefp() on this and samep before calling virtual bool similarDType(const AstNodeDType* samep) const = 0; // Iff has a non-null subDTypep(), as generic node function - virtual AstNodeDType* subDTypep() const { return nullptr; } + virtual AstNodeDType* subDTypep() const VL_MT_SAFE { return nullptr; } virtual bool isFourstate() const; // Ideally an IEEE $typename virtual string prettyDTypeName() const { return prettyTypeName(); } @@ -126,12 +126,13 @@ public: const char* charIQWN() const { return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I"); } - string cType(const string& name, bool forFunc, bool isRef) const VL_MT_SAFE; - bool isLiteralType() const VL_MT_SAFE; // Represents a C++ LiteralType? (can be constexpr) + string cType(const string& name, bool forFunc, bool isRef) const VL_MT_STABLE; + // Represents a C++ LiteralType? (can be constexpr) + bool isLiteralType() const VL_MT_STABLE; private: class CTypeRecursed; - CTypeRecursed cTypeRecurse(bool compound) const VL_MT_SAFE; + CTypeRecursed cTypeRecurse(bool compound) const VL_MT_STABLE; }; class AstNodeArrayDType VL_NOT_FINAL : public AstNodeDType { // Array data type, ie "some_dtype var_name [2:0]" @@ -170,29 +171,29 @@ public: && subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp())); } AstNodeDType* getChildDTypep() const override { return childDTypep(); } - AstNodeDType* subDTypep() const override VL_MT_SAFE { + AstNodeDType* subDTypep() const override VL_MT_STABLE { return m_refDTypep ? m_refDTypep : childDTypep(); } void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } AstNodeDType* virtRefDTypep() const override { return m_refDTypep; } void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } // METHODS - AstBasicDType* basicp() const override VL_MT_SAFE { + AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type - AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } int widthTotalBytes() const override { return elementsConst() * subDTypep()->widthTotalBytes(); } - inline int left() const; - inline int right() const; - inline int hi() const; - inline int lo() const; - inline int elementsConst() const; - inline VNumRange declRange() const; + inline int left() const VL_MT_STABLE; + inline int right() const VL_MT_STABLE; + inline int hi() const VL_MT_STABLE; + inline int lo() const VL_MT_STABLE; + inline int elementsConst() const VL_MT_STABLE; + inline VNumRange declRange() const VL_MT_STABLE; }; class AstNodeUOrStructDType VL_NOT_FINAL : public AstNodeDType { // A struct or union; common handling @@ -231,7 +232,7 @@ public: : VN_AS(findBitRangeDType(VNumRange{width() - 1, 0}, width(), numeric()), BasicDType)); } - AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) @@ -241,14 +242,14 @@ public: bool similarDType(const AstNodeDType* samep) const override { return this == samep; // We don't compare members, require exact equivalence } - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } void name(const string& flag) override { m_name = flag; } bool packed() const VL_MT_SAFE { return m_packed; } void packed(bool flag) { m_packed = flag; } // packed() but as don't support unpacked, presently all structs static bool packedUnsup() { return true; } void isFourstate(bool flag) { m_isFourstate = flag; } - bool isFourstate() const override { return m_isFourstate; } + bool isFourstate() const override VL_MT_SAFE { return m_isFourstate; } void clearCache() { m_members.clear(); } void repairMemberCache(); AstMemberDType* findMember(const string& name) const { @@ -280,7 +281,7 @@ public: this->valuep(valuep); } ASTGEN_MEMBERS_AstEnumItem; - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } bool maybePointedTo() const override { return true; } bool hasDType() const override { return true; } void name(const string& flag) override { m_name = flag; } @@ -336,7 +337,7 @@ public: void dumpSmall(std::ostream& str) const override; AstNodeDType* getChildDTypep() const override { return childDTypep(); } AstNodeDType* getChild2DTypep() const override { return keyChildDTypep(); } - AstNodeDType* subDTypep() const override VL_MT_SAFE { + AstNodeDType* subDTypep() const override VL_MT_STABLE { return m_refDTypep ? m_refDTypep : childDTypep(); } void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } @@ -345,13 +346,13 @@ public: AstNodeDType* virtRefDType2p() const override { return m_keyDTypep; } void virtRefDType2p(AstNodeDType* nodep) override { keyDTypep(nodep); } // - AstNodeDType* keyDTypep() const VL_MT_SAFE { + AstNodeDType* keyDTypep() const VL_MT_STABLE { return m_keyDTypep ? m_keyDTypep : keyChildDTypep(); } void keyDTypep(AstNodeDType* nodep) { m_keyDTypep = nodep; } // METHODS - AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; } - AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; } + AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } @@ -401,14 +402,11 @@ public: ASTGEN_MEMBERS_AstBasicDType; void dump(std::ostream& str) const override; // width/widthMin/numeric compared elsewhere - bool same(const AstNode* samep) const override { - const AstBasicDType* const sp = static_cast(samep); - return m == sp->m; - } + bool same(const AstNode* samep) const override; bool similarDType(const AstNodeDType* samep) const override { return type() == samep->type() && same(samep); } - string name() const override { return m.m_keyword.ascii(); } + string name() const override VL_MT_STABLE { return m.m_keyword.ascii(); } string prettyDTypeName() const override; const char* broken() const override { BROKEN_RTN(dtypep() != this); @@ -423,8 +421,8 @@ public: } } // METHODS - AstBasicDType* basicp() const override VL_MT_SAFE { return (AstBasicDType*)this; } - AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; } + AstBasicDType* basicp() const override VL_MT_STABLE { return (AstBasicDType*)this; } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) @@ -467,9 +465,9 @@ public: inline int hi() const; inline int lo() const; inline int elements() const; - int left() const { return littleEndian() ? lo() : hi(); } // How to show a declaration - int right() const { return littleEndian() ? hi() : lo(); } - inline bool littleEndian() const; + int left() const { return ascending() ? lo() : hi(); } // How to show a declaration + int right() const { return ascending() ? hi() : lo(); } + inline bool ascending() const; bool implicit() const { return keyword() == VBasicDTypeKwd::LOGIC_IMPLICIT; } bool untyped() const { return keyword() == VBasicDTypeKwd::UNTYPED; } VNumRange declRange() const { return isRanged() ? VNumRange{left(), right()} : VNumRange{}; } @@ -491,13 +489,13 @@ public: } ASTGEN_MEMBERS_AstBracketArrayDType; bool similarDType(const AstNodeDType* samep) const override { V3ERROR_NA_RETURN(false); } - AstNodeDType* subDTypep() const override { return childDTypep(); } + AstNodeDType* subDTypep() const override VL_MT_STABLE { return childDTypep(); } // METHODS // Will be removed in V3Width, which relies on this // being a child not a dtype pointed node bool maybePointedTo() const override { return false; } - AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; } - AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; } + AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } int widthAlignBytes() const override { V3ERROR_NA_RETURN(0); } @@ -530,19 +528,19 @@ public: } void dump(std::ostream& str = std::cout) const override; void dumpSmall(std::ostream& str) const override; - string name() const override; - AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; } - AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; } + string name() const override VL_MT_STABLE; + AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } int widthAlignBytes() const override { return 0; } int widthTotalBytes() const override { return 0; } AstNodeDType* virtRefDTypep() const override { return nullptr; } void virtRefDTypep(AstNodeDType* nodep) override {} - AstNodeDType* subDTypep() const override { return nullptr; } + AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; } AstNodeModule* classOrPackagep() const { return m_classOrPackagep; } void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; } - AstClass* classp() const { return m_classp; } + AstClass* classp() const VL_MT_STABLE { return m_classp; } void classp(AstClass* nodep) { m_classp = nodep; } bool isCompound() const override { return true; } }; @@ -578,13 +576,15 @@ public: return skipRefp()->similarDType(samep->skipRefp()); } AstNodeDType* getChildDTypep() const override { return childDTypep(); } - AstNodeDType* subDTypep() const override { return m_refDTypep ? m_refDTypep : childDTypep(); } + AstNodeDType* subDTypep() const override VL_MT_STABLE { + return m_refDTypep ? m_refDTypep : childDTypep(); + } void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } AstNodeDType* virtRefDTypep() const override { return m_refDTypep; } void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } // METHODS - AstBasicDType* basicp() const override VL_MT_SAFE { return subDTypep()->basicp(); } - AstNodeDType* skipRefp() const override VL_MT_SAFE { return subDTypep()->skipRefp(); } + AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return subDTypep()->skipRefp(); } AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } AstNodeDType* skipRefToEnump() const override { return subDTypep()->skipRefToEnump(); } int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } @@ -625,18 +625,20 @@ public: return type() == samep->type() && same(samep); } AstNodeDType* getChildDTypep() const override { return childDTypep(); } - AstNodeDType* subDTypep() const override { return dtypep() ? dtypep() : childDTypep(); } + AstNodeDType* subDTypep() const override VL_MT_STABLE { + return dtypep() ? dtypep() : childDTypep(); + } void* containerp() const { return m_containerp; } // METHODS // op1 = Range of variable AstNodeDType* dtypeSkipRefp() const { return dtypep()->skipRefp(); } - AstBasicDType* basicp() const override VL_MT_SAFE { return subDTypep()->basicp(); } - AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; } + AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } int widthAlignBytes() const override { return dtypep()->widthAlignBytes(); } int widthTotalBytes() const override { return dtypep()->widthTotalBytes(); } - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } void name(const string& flag) override { m_name = flag; } bool isCompound() const override { return false; } }; @@ -679,15 +681,15 @@ public: string prettyDTypeName() const override; void dumpSmall(std::ostream& str) const override; AstNodeDType* getChildDTypep() const override { return childDTypep(); } - AstNodeDType* subDTypep() const override VL_MT_SAFE { + AstNodeDType* subDTypep() const override VL_MT_STABLE { return m_refDTypep ? m_refDTypep : childDTypep(); } void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } AstNodeDType* virtRefDTypep() const override { return m_refDTypep; } void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } // METHODS - AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; } - AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; } + AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } @@ -706,13 +708,13 @@ public: bool hasDType() const override { return true; } bool maybePointedTo() const override { return true; } bool undead() const override { return true; } - AstNodeDType* subDTypep() const override { return nullptr; } + AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; } AstNodeDType* virtRefDTypep() const override { return nullptr; } void virtRefDTypep(AstNodeDType* nodep) override {} bool similarDType(const AstNodeDType* samep) const override { return this == samep; } - AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; } + AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } // cppcheck-suppress csyleCast - AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } // cppcheck-suppress csyleCast AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } // cppcheck-suppress csyleCast @@ -756,17 +758,19 @@ public: } bool similarDType(const AstNodeDType* samep) const override { return this == samep; } AstNodeDType* getChildDTypep() const override { return childDTypep(); } - AstNodeDType* subDTypep() const override { return m_refDTypep ? m_refDTypep : childDTypep(); } + AstNodeDType* subDTypep() const override VL_MT_STABLE { + return m_refDTypep ? m_refDTypep : childDTypep(); + } void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } AstNodeDType* virtRefDTypep() const override { return m_refDTypep; } void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } void name(const string& flag) override { m_name = flag; } void dump(std::ostream& str = std::cout) const override; void dumpSmall(std::ostream& str) const override; // METHODS - AstBasicDType* basicp() const override VL_MT_SAFE { return subDTypep()->basicp(); } - AstNodeDType* skipRefp() const override VL_MT_SAFE { return subDTypep()->skipRefp(); } + AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return subDTypep()->skipRefp(); } AstNodeDType* skipRefToConstp() const override { return subDTypep()->skipRefToConstp(); } // cppcheck-suppress csyleCast AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } @@ -818,8 +822,8 @@ public: void dump(std::ostream& str = std::cout) const override; void dumpSmall(std::ostream& str) const override; void cloneRelink() override; - AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; } - AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; } + AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } bool similarDType(const AstNodeDType* samep) const override { return this == samep; } @@ -872,7 +876,7 @@ public: } ASTGEN_MEMBERS_AstMemberDType; void dumpSmall(std::ostream& str) const override; - string name() const override { return m_name; } // * = Var name + string name() const override VL_MT_STABLE { return m_name; } // * = Var name bool hasDType() const override { return true; } bool maybePointedTo() const override { return true; } const char* broken() const override { @@ -883,7 +887,9 @@ public: if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep(); } AstNodeDType* getChildDTypep() const override { return childDTypep(); } - AstNodeDType* subDTypep() const override { return m_refDTypep ? m_refDTypep : childDTypep(); } + AstNodeDType* subDTypep() const override VL_MT_STABLE { + return m_refDTypep ? m_refDTypep : childDTypep(); + } void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } AstNodeDType* virtRefDTypep() const override { return m_refDTypep; } void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } @@ -891,10 +897,10 @@ public: // // (Slow) recurse down to find basic data type (Note don't need virtual - // AstVar isn't a NodeDType) - AstBasicDType* basicp() const override VL_MT_SAFE { return subDTypep()->basicp(); } + AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); } // op1 = Range of variable (Note don't need virtual - AstVar isn't a NodeDType) AstNodeDType* dtypeSkipRefp() const { return subDTypep()->skipRefp(); } - AstNodeDType* skipRefp() const override VL_MT_SAFE { return subDTypep()->skipRefp(); } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return subDTypep()->skipRefp(); } AstNodeDType* skipRefToConstp() const override { return subDTypep()->skipRefToConstp(); } AstNodeDType* skipRefToEnump() const override { return subDTypep()->skipRefToEnump(); } // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) @@ -931,9 +937,11 @@ public: ASTGEN_MEMBERS_AstParamTypeDType; void dump(std::ostream& str = std::cout) const override; AstNodeDType* getChildDTypep() const override { return childDTypep(); } - AstNodeDType* subDTypep() const override { return dtypep() ? dtypep() : childDTypep(); } - AstBasicDType* basicp() const override VL_MT_SAFE { return subDTypep()->basicp(); } - AstNodeDType* skipRefp() const override VL_MT_SAFE { return subDTypep()->skipRefp(); } + AstNodeDType* subDTypep() const override VL_MT_STABLE { + return dtypep() ? dtypep() : childDTypep(); + } + AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return subDTypep()->skipRefp(); } AstNodeDType* skipRefToConstp() const override { return subDTypep()->skipRefToConstp(); } AstNodeDType* skipRefToEnump() const override { return subDTypep()->skipRefToEnump(); } bool similarDType(const AstNodeDType* samep) const override { @@ -944,7 +952,7 @@ public: int widthAlignBytes() const override { return dtypep()->widthAlignBytes(); } int widthTotalBytes() const override { return dtypep()->widthTotalBytes(); } // METHODS - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } bool maybePointedTo() const override { return true; } bool hasDType() const override { return true; } void name(const string& flag) override { m_name = flag; } @@ -967,8 +975,8 @@ public: AstNodeDType* dtypep() const { return nullptr; } // METHODS bool similarDType(const AstNodeDType* samep) const override { return this == samep; } - AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; } - AstNodeDType* skipRefp() const override VL_MT_SAFE { return nullptr; } + AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return nullptr; } // cppcheck-suppress csyleCast AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } // cppcheck-suppress csyleCast @@ -1022,17 +1030,17 @@ public: void dumpSmall(std::ostream& str) const override; string prettyDTypeName() const override; AstNodeDType* getChildDTypep() const override { return childDTypep(); } - AstNodeDType* subDTypep() const override VL_MT_SAFE { + AstNodeDType* subDTypep() const override VL_MT_STABLE { return m_refDTypep ? m_refDTypep : childDTypep(); } void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } - inline int boundConst() const; + inline int boundConst() const VL_MT_STABLE; AstNodeDType* virtRefDTypep() const override { return m_refDTypep; } void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } // METHODS - AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; } + AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } // cppcheck-suppress csyleCast - AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } // cppcheck-suppress csyleCast AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } // cppcheck-suppress csyleCast @@ -1082,15 +1090,15 @@ public: } void dump(std::ostream& str = std::cout) const override; void dumpSmall(std::ostream& str) const override; - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } string prettyDTypeName() const override { return subDTypep() ? prettyName(subDTypep()->name()) : prettyName(); } - AstBasicDType* basicp() const override VL_MT_SAFE { + AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep() ? subDTypep()->basicp() : nullptr; } - AstNodeDType* subDTypep() const override; - AstNodeDType* skipRefp() const override VL_MT_SAFE { + AstNodeDType* subDTypep() const override VL_MT_STABLE; + AstNodeDType* skipRefp() const override VL_MT_STABLE { // Skip past both the Ref and the Typedef if (subDTypep()) { return subDTypep()->skipRefp(); @@ -1119,9 +1127,9 @@ public: int widthTotalBytes() const override { return dtypeSkipRefp()->widthTotalBytes(); } void name(const string& flag) override { m_name = flag; } AstNodeDType* dtypeSkipRefp() const { return subDTypep()->skipRefp(); } - AstTypedef* typedefp() const { return m_typedefp; } + AstTypedef* typedefp() const VL_MT_SAFE { return m_typedefp; } void typedefp(AstTypedef* nodep) { m_typedefp = nodep; } - AstNodeDType* refDTypep() const { return m_refDTypep; } + AstNodeDType* refDTypep() const VL_MT_SAFE { return m_refDTypep; } void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } AstNodeDType* virtRefDTypep() const override { return refDTypep(); } void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } @@ -1163,13 +1171,15 @@ public: void dumpSmall(std::ostream& str) const override; AstNodeDType* getChildDTypep() const override { return childDTypep(); } // op1 = Range of variable - AstNodeDType* subDTypep() const override { return m_refDTypep ? m_refDTypep : childDTypep(); } + AstNodeDType* subDTypep() const override VL_MT_STABLE { + return m_refDTypep ? m_refDTypep : childDTypep(); + } void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } AstNodeDType* virtRefDTypep() const override { return m_refDTypep; } void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } // METHODS - AstBasicDType* basicp() const override { return subDTypep()->basicp(); } - AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; } + AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } int widthAlignBytes() const override { return sizeof(std::map); } @@ -1201,13 +1211,15 @@ public: bool similarDType(const AstNodeDType* samep) const override; void dumpSmall(std::ostream& str) const override; AstNodeDType* getChildDTypep() const override { return childDTypep(); } - AstNodeDType* subDTypep() const override { return m_refDTypep ? m_refDTypep : childDTypep(); } + AstNodeDType* subDTypep() const override VL_MT_STABLE { + return m_refDTypep ? m_refDTypep : childDTypep(); + } void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } AstNodeDType* virtRefDTypep() const override { return m_refDTypep; } void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } // METHODS - AstBasicDType* basicp() const override VL_MT_SAFE { return subDTypep()->basicp(); } - AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; } + AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } @@ -1226,13 +1238,13 @@ public: bool hasDType() const override { return true; } bool maybePointedTo() const override { return true; } bool undead() const override { return true; } - AstNodeDType* subDTypep() const override { return nullptr; } + AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; } AstNodeDType* virtRefDTypep() const override { return nullptr; } void virtRefDTypep(AstNodeDType* nodep) override {} bool similarDType(const AstNodeDType* samep) const override { return this == samep; } - AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; } + AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } // cppcheck-suppress csyleCast - AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } // cppcheck-suppress csyleCast AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } // cppcheck-suppress csyleCast @@ -1266,15 +1278,15 @@ public: bool similarDType(const AstNodeDType* samep) const override; void dumpSmall(std::ostream& str) const override; AstNodeDType* getChildDTypep() const override { return childDTypep(); } - AstNodeDType* subDTypep() const override VL_MT_SAFE { + AstNodeDType* subDTypep() const override VL_MT_STABLE { return m_refDTypep ? m_refDTypep : childDTypep(); } void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } AstNodeDType* virtRefDTypep() const override { return m_refDTypep; } void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } // METHODS - AstBasicDType* basicp() const override VL_MT_SAFE { return subDTypep()->basicp(); } - AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; } + AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); } + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } int widthAlignBytes() const override { return sizeof(std::map); } diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 3ccc3a36f..635b9edb4 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -236,7 +236,7 @@ public: const char* broken() const override; void cloneRelink() override; void dump(std::ostream& str = std::cout) const override; - string name() const override { return m_name; } // * = Var name + string name() const override VL_MT_STABLE { return m_name; } // * = Var name bool isGateOptimizable() const override; string dotted() const { return m_dotted; } // * = Scope name or "" string inlinedDots() const { return m_inlinedDots; } @@ -322,7 +322,7 @@ public: ASTGEN_MEMBERS_AstNodeTermop; // Know no children, and hot function, so skip iterator for speed // cppcheck-suppress functionConst - void iterateChildren(VNVisitor& v) {} + void iterateChildren(VNVisitorConst& v) {} void dump(std::ostream& str) const override; }; class AstNodeTriop VL_NOT_FINAL : public AstNodeExpr { @@ -474,7 +474,7 @@ public: const char* broken() const override; int instrCount() const override { return widthInstrs(); } void cloneRelink() override; - string name() const override { return m_name; } // * = Var name + string name() const override VL_MT_STABLE { return m_name; } // * = Var name void name(const string& name) override { m_name = name; } VAccess access() const { return m_access; } void access(const VAccess& flag) { m_access = flag; } // Avoid using this; Set in constructor @@ -492,7 +492,7 @@ public: void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; } // Know no children, and hot function, so skip iterator for speed // cppcheck-suppress functionConst - void iterateChildren(VNVisitor& v) {} + void iterateChildren(VNVisitorConst& v) {} }; // === Concrete node types ===================================================== @@ -532,7 +532,7 @@ public: } ASTGEN_MEMBERS_AstArg; bool hasDType() const override { return false; } - string name() const override { return m_name; } // * = Pin name, ""=go by number + string name() const override VL_MT_STABLE { return m_name; } // * = Pin name, ""=go by number void name(const string& name) override { m_name = name; } bool emptyConnectNoNext() const { return !exprp() && name() == "" && !nextp(); } @@ -610,7 +610,7 @@ public: this->addPinsp(pinsp); } ASTGEN_MEMBERS_AstCMethodHard; - string name() const override { return m_name; } // * = Var name + string name() const override VL_MT_STABLE { return m_name; } // * = Var name void name(const string& name) override { m_name = name; } bool same(const AstNode* samep) const override { const AstCMethodHard* asamep = static_cast(samep); @@ -644,7 +644,7 @@ public: string emitC() override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { V3ERROR_NA_RETURN(true); } AstNodeDType* getChildDTypep() const override { return childDTypep(); } - AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); } + AstNodeDType* subDTypep() const VL_MT_STABLE { return dtypep() ? dtypep() : childDTypep(); } }; class AstCastParse final : public AstNodeExpr { // Cast to appropriate type, where we haven't determined yet what the data type is @@ -689,7 +689,7 @@ public: } ASTGEN_MEMBERS_AstCellArrayRef; // ACCESSORS - string name() const override { return m_name; } // * = Array name + string name() const override VL_MT_STABLE { return m_name; } // * = Array name string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } @@ -710,7 +710,7 @@ public: } ASTGEN_MEMBERS_AstCellRef; // ACCESSORS - string name() const override { return m_name; } // * = Array name + string name() const override VL_MT_STABLE { return m_name; } // * = Array name string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } @@ -746,7 +746,7 @@ public: == static_cast(samep)->m_classOrPackageNodep); } void dump(std::ostream& str = std::cout) const override; - string name() const override { return m_name; } // * = Var name + string name() const override VL_MT_STABLE { return m_name; } // * = Var name AstNode* classOrPackageNodep() const { return m_classOrPackageNodep; } void classOrPackageNodep(AstNode* nodep) { m_classOrPackageNodep = nodep; } AstNodeModule* classOrPackagep() const; @@ -990,7 +990,7 @@ public: initWithNumber(); } ASTGEN_MEMBERS_AstConst; - string name() const override { return num().ascii(); } // * = Value + string name() const override VL_MT_STABLE { return num().ascii(); } // * = Value const V3Number& num() const VL_MT_SAFE { return m_num; } // * = Value V3Number& num() { return m_num; } // * = Value uint32_t toUInt() const { return num().toUInt(); } @@ -1058,7 +1058,7 @@ public: } ASTGEN_MEMBERS_AstEnumItemRef; void dump(std::ostream& str) const override; - string name() const override { return itemp()->name(); } + string name() const override VL_MT_STABLE { return itemp()->name(); } int instrCount() const override { return 0; } const char* broken() const override; void cloneRelink() override { @@ -1068,7 +1068,7 @@ public: const AstEnumItemRef* const sp = static_cast(samep); return itemp() == sp->itemp(); } - AstEnumItem* itemp() const { return m_itemp; } + AstEnumItem* itemp() const VL_MT_STABLE { return m_itemp; } string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { return true; } @@ -1109,9 +1109,50 @@ public: string emitC() override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { return true; } int instrCount() const override { return widthInstrs() * 64; } + bool isPredictOptimizable() const override { return false; } bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering bool same(const AstNode* /*samep*/) const override { return true; } }; +class AstFOpen final : public AstNodeExpr { + // @astgen op2 := filenamep : AstNodeExpr + // @astgen op3 := modep : AstNodeExpr +public: + AstFOpen(FileLine* fl, AstNodeExpr* filenamep, AstNodeExpr* modep) + : ASTGEN_SUPER_FOpen(fl) { + this->filenamep(filenamep); + this->modep(modep); + } + ASTGEN_MEMBERS_AstFOpen; + string emitVerilog() override { V3ERROR_NA_RETURN(""); } + string emitC() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { return true; } + string verilogKwd() const override { return "$fopen"; } + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isPure() const override { return false; } + bool isOutputter() const override { return true; } + bool isUnlikely() const override { return true; } + bool same(const AstNode* /*samep*/) const override { return true; } +}; +class AstFOpenMcd final : public AstNodeExpr { + // @astgen op2 := filenamep : AstNodeExpr +public: + AstFOpenMcd(FileLine* fl, AstNodeExpr* filenamep) + : ASTGEN_SUPER_FOpenMcd(fl) { + this->filenamep(filenamep); + } + ASTGEN_MEMBERS_AstFOpenMcd; + string emitVerilog() override { V3ERROR_NA_RETURN(""); } + string emitC() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { return true; } + string verilogKwd() const override { return "$fopen"; } + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool isPure() const override { return false; } + bool isOutputter() const override { return true; } + bool isUnlikely() const override { return true; } + bool same(const AstNode* /*samep*/) const override { return true; } +}; class AstFRead final : public AstNodeExpr { // @astgen op1 := memp : AstNode // VarRef for result // @astgen op2 := filep : AstNode // file (must be a VarRef) @@ -1168,7 +1209,7 @@ public: this->filep(filep); } ASTGEN_MEMBERS_AstFScanF; - string name() const override { return m_text; } + string name() const override VL_MT_STABLE { return m_text; } string verilogKwd() const override { return "$fscanf"; } string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } @@ -1365,7 +1406,7 @@ public: string emitC() override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { return true; } int instrCount() const override { return widthInstrs(); } - string name() const override { return m_name; } // * = Var name + string name() const override VL_MT_STABLE { return m_name; } // * = Var name void name(const string& name) override { m_name = name; } bool index() const { return m_index; } }; @@ -1391,7 +1432,7 @@ public: void cloneRelink() override; const char* broken() const override; void dump(std::ostream& str) const override; - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } void name(const string& name) override { m_name = name; } string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } @@ -1456,7 +1497,7 @@ public: } ASTGEN_MEMBERS_AstParseRef; void dump(std::ostream& str) const override; - string name() const override { return m_name; } // * = Var name + string name() const override VL_MT_STABLE { return m_name; } // * = Var name bool same(const AstNode* samep) const override { const AstParseRef* const asamep = static_cast(samep); return (expect() == asamep->expect() && m_name == asamep->m_name); @@ -1533,7 +1574,7 @@ public: bool cleanOut() const override { V3ERROR_NA_RETURN(""); } int instrCount() const override { return widthInstrs(); } AstNodeDType* getChildDTypep() const override { return childDTypep(); } - AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); } + AstNodeDType* subDTypep() const VL_MT_STABLE { return dtypep() ? dtypep() : childDTypep(); } }; class AstRand final : public AstNodeExpr { // $random/$random(seed) or $urandom/$urandom(seed) @@ -1579,6 +1620,26 @@ public: bool reset() const { return m_reset; } bool urandom() const { return m_urandom; } }; +class AstRandRNG final : public AstNodeExpr { + // Random used in a class using VlRNG + // Return a random number, based upon width() +public: + AstRandRNG(FileLine* fl, AstNodeDType* dtp) + : ASTGEN_SUPER_RandRNG(fl) { + dtypep(dtp); + } + ASTGEN_MEMBERS_AstRandRNG; + string emitVerilog() override { return "%f$rngrandom()"; } + string emitC() override { + return isWide() ? "VL_RANDOM_RNG_%nq(__Vm_rng, %nw, %P)" // + : "VL_RANDOM_RNG_%nq(__Vm_rng)"; + } + bool cleanOut() const override { return false; } + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + int instrCount() const override { return INSTR_COUNT_PLI; } + bool same(const AstNode* /*samep*/) const override { return true; } +}; class AstRose final : public AstNodeExpr { // Verilog $rose // @astgen op1 := exprp : AstNodeExpr @@ -1630,7 +1691,7 @@ public: addExprsp(exprsp); } ASTGEN_MEMBERS_AstSFormatF; - string name() const override { return m_text; } + string name() const override VL_MT_STABLE { return m_text; } int instrCount() const override { return INSTR_COUNT_PLI; } bool same(const AstNode* samep) const override { return text() == static_cast(samep)->text(); @@ -1665,7 +1726,7 @@ public: this->fromp(fromp); } ASTGEN_MEMBERS_AstSScanF; - string name() const override { return m_text; } + string name() const override VL_MT_STABLE { return m_text; } string verilogKwd() const override { return "$sscanf"; } string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } @@ -1850,7 +1911,7 @@ public: dtypep(nullptr); // V3Width will resolve } ASTGEN_MEMBERS_AstStructSel; - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } void name(const string& name) override { m_name = name; } string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } @@ -1931,7 +1992,7 @@ public: bool same(const AstNode* /*samep*/) const override { return true; } bool cleanOut() const override { return true; } AstNodeDType* getChildDTypep() const override { return childDTypep(); } - AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); } + AstNodeDType* subDTypep() const VL_MT_STABLE { return dtypep() ? dtypep() : childDTypep(); } }; class AstTimePrecision final : public AstNodeExpr { // Verilog $timeprecision @@ -2159,7 +2220,7 @@ public: void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override { out.opCompareNN(lhs, rhs, m_ignoreCase); } - string name() const override { return m_ignoreCase ? "icompare" : "compare"; } + string name() const override VL_MT_STABLE { return m_ignoreCase ? "icompare" : "compare"; } string emitVerilog() override { return m_ignoreCase ? "%k(%l.icompare(%r))" : "%k(%l.compare(%r))"; } @@ -2364,6 +2425,7 @@ public: bool sizeMattersLhs() const override { return false; } bool sizeMattersRhs() const override { return false; } int instrCount() const override { return widthInstrs() * 64; } + bool isPredictOptimizable() const override { return false; } bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering AstNode* filep() const { return lhsp(); } AstNode* charp() const { return rhsp(); } @@ -2382,7 +2444,7 @@ public: void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override { out.opGetcN(lhs, rhs); } - string name() const override { return "getc"; } + string name() const override VL_MT_STABLE { return "getc"; } string emitVerilog() override { return "%k(%l.getc(%r))"; } string emitC() override { return "VL_GETC_N(%li,%ri)"; } string emitSimpleOperator() override { return ""; } @@ -4012,7 +4074,6 @@ public: BROKEN_RTN(!fromp()); return nullptr; } - void dump(std::ostream& str) const override; }; class AstNew final : public AstNodeFTaskRef { // New as constructor @@ -4263,7 +4324,7 @@ public: const V3Number& ths) override { out.opPutcN(lhs, rhs, ths); } - string name() const override { return "putc"; } + string name() const override VL_MT_STABLE { return "putc"; } string emitVerilog() override { return "%k(%l.putc(%r,%t))"; } string emitC() override { return "VL_PUTC_N(%li,%ri,%ti)"; } string emitSimpleOperator() override { return ""; } @@ -4370,7 +4431,7 @@ public: const V3Number& ths) override { out.opSubstrN(lhs, rhs, ths); } - string name() const override { return "substr"; } + string name() const override VL_MT_STABLE { return "substr"; } string emitVerilog() override { return "%k(%l.substr(%r,%t))"; } string emitC() override { return "VL_SUBSTR_N(%li,%ri,%ti)"; } string emitSimpleOperator() override { return ""; } @@ -4447,7 +4508,7 @@ public: } ASTGEN_MEMBERS_AstAtoN; void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAtoN(lhs, m_fmt); } - string name() const override { + string name() const override VL_MT_STABLE { switch (m_fmt) { case ATOI: return "atoi"; case ATOHEX: return "atohex"; diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 87bad25ba..fc998872b 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -48,7 +48,7 @@ protected: public: ASTGEN_MEMBERS_AstNodeBlock; void dump(std::ostream& str) const override; - string name() const override { return m_name; } // * = Block name + string name() const override VL_MT_STABLE { return m_name; } // * = Block name void name(const string& name) override { m_name = name; } bool unnamed() const { return m_unnamed; } bool isFirstInMyListOfStatements(AstNode* nodep) const override { return nodep == stmtsp(); } @@ -118,7 +118,7 @@ protected: public: ASTGEN_MEMBERS_AstNodeFTask; void dump(std::ostream& str = std::cout) const override; - string name() const override { return m_name; } // * = Var name + string name() const override VL_MT_STABLE { return m_name; } // * = Var name bool maybePointedTo() const override { return true; } bool isGateOptimizable() const override { return !((m_dpiExport || m_dpiImport) && !m_pure); } // {AstFunc only} op1 = Range output variable @@ -186,7 +186,7 @@ public: , m_name{name} {} ASTGEN_MEMBERS_AstNodeFile; void dump(std::ostream& str) const override; - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } bool same(const AstNode* /*samep*/) const override { return true; } }; class AstNodeModule VL_NOT_FINAL : public AstNode { @@ -234,7 +234,7 @@ public: ASTGEN_MEMBERS_AstNodeModule; void dump(std::ostream& str) const override; bool maybePointedTo() const override { return true; } - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } virtual bool timescaleMatters() const = 0; // ACCESSORS void name(const string& name) override { m_name = name; } @@ -373,7 +373,7 @@ public: this->addPasssp(passsp); } ASTGEN_MEMBERS_AstNodeCoverOrAssert; - string name() const override { return m_name; } // * = Var name + string name() const override VL_MT_STABLE { return m_name; } // * = Var name bool same(const AstNode* samep) const override { return samep->name() == name(); } void name(const string& name) override { m_name = name; } void dump(std::ostream& str = std::cout) const override; @@ -512,7 +512,7 @@ public: } ASTGEN_MEMBERS_AstActive; void dump(std::ostream& str = std::cout) const override; - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } const char* broken() const override; void cloneRelink() override; // Statements are broken into pieces, as some must come before others. @@ -537,7 +537,7 @@ public: } ASTGEN_MEMBERS_AstBind; // ACCESSORS - string name() const override { return m_name; } // * = Bind Target name + string name() const override VL_MT_STABLE { return m_name; } // * = Bind Target name void name(const string& name) override { m_name = name; } }; class AstCFunc final : public AstNode { @@ -607,7 +607,7 @@ public: m_dpiTraceInit = false; } ASTGEN_MEMBERS_AstCFunc; - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } const char* broken() const override; void cloneRelink() override; bool maybePointedTo() const override { return true; } @@ -702,7 +702,7 @@ public: , m_useType{useType} {} ASTGEN_MEMBERS_AstCUse; void dump(std::ostream& str = std::cout) const override; - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } VUseType useType() const { return m_useType; } }; class AstCaseItem final : public AstNode { @@ -755,7 +755,7 @@ public: const char* broken() const override; bool maybePointedTo() const override { return true; } // ACCESSORS - string name() const override { return m_name; } // * = Cell name + string name() const override VL_MT_STABLE { return m_name; } // * = Cell name void name(const string& name) override { m_name = name; } string origName() const override { return m_origName; } // * = Original name void origName(const string& name) { m_origName = name; } @@ -794,7 +794,7 @@ public: void dump(std::ostream& str) const override; const char* broken() const override; // ACCESSORS - string name() const override { return m_name; } // * = Cell name + string name() const override VL_MT_STABLE { return m_name; } // * = Cell name string origModName() const { return m_origModName; } // * = modp()->origName() before inlining void name(const string& name) override { m_name = name; } void scopep(AstScope* scp) { m_scopep = scp; } @@ -845,7 +845,7 @@ public: } ASTGEN_MEMBERS_AstClocking; void dump(std::ostream& str) const override; - std::string name() const override { return m_name; } + std::string name() const override VL_MT_STABLE { return m_name; } bool isDefault() const { return m_isDefault; } bool isGlobal() const { return m_isGlobal; } }; @@ -863,8 +863,8 @@ public: : ASTGEN_SUPER_ClockingItem(fl) { m_direction = direction; this->skewp(skewp); - if (AstAssign* const assignp = VN_CAST(clockingDeclp, Assign)) { - this->assignp(assignp); + if (AstAssign* const clkAssignp = VN_CAST(clockingDeclp, Assign)) { + this->assignp(clkAssignp); } else { exprp(VN_AS(clockingDeclp, NodeExpr)); } @@ -916,7 +916,7 @@ public: , m_path{path} { this->rhsp(rhsp); } - string name() const override { return m_name; } // * = Scope name + string name() const override VL_MT_STABLE { return m_name; } // * = Scope name ASTGEN_MEMBERS_AstDefParam; bool same(const AstNode*) const override { return true; } string path() const { return m_path; } @@ -934,7 +934,7 @@ public: , m_name{vname} , m_cname{cname} {} ASTGEN_MEMBERS_AstDpiExport; - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } void name(const string& name) override { m_name = name; } string cname() const { return m_cname; } void cname(const string& cname) { m_cname = cname; } @@ -952,7 +952,7 @@ public: BROKEN_RTN(!fmtp()); return nullptr; } - string verilogKwd() const override { return (string("$") + string(displayType().ascii())); } + string verilogKwd() const override { return string{"$"} + string{displayType().ascii()}; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering @@ -995,7 +995,7 @@ public: BROKEN_RTN(!m_depGraphp); return nullptr; } - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } V3Graph* depGraphp() { return m_depGraphp; } const V3Graph* depGraphp() const { return m_depGraphp; } }; @@ -1033,7 +1033,7 @@ public: AstIntfRef(FileLine* fl, const string& name) : ASTGEN_SUPER_IntfRef(fl) , m_name{name} {} - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } ASTGEN_MEMBERS_AstIntfRef; }; class AstMTaskBody final : public AstNode { @@ -1070,7 +1070,7 @@ public: , m_name{name} { this->addVarsp(varsp); } - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } bool maybePointedTo() const override { return true; } ASTGEN_MEMBERS_AstModport; }; @@ -1092,7 +1092,7 @@ public: const char* broken() const override; void cloneRelink() override; void dump(std::ostream& str) const override; - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } bool isImport() const { return !m_export; } bool isExport() const { return m_export; } AstNodeFTask* ftaskp() const { return m_ftaskp; } // [After Link] Pointer to variable @@ -1115,7 +1115,7 @@ public: const char* broken() const override; void cloneRelink() override; void dump(std::ostream& str) const override; - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } void direction(const VDirection& flag) { m_direction = flag; } VDirection direction() const { return m_direction; } AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable @@ -1149,9 +1149,9 @@ public: ASTGEN_MEMBERS_AstNetlist; const char* broken() const override; void cloneRelink() override { V3ERROR_NA; } - string name() const override { return "$root"; } + string name() const override VL_MT_STABLE { return "$root"; } void dump(std::ostream& str) const override; - AstNodeModule* topModulep() const { // Top module in hierarchy + AstNodeModule* topModulep() const VL_MT_STABLE { // Top module in hierarchy return modulesp(); // First one in the list, for now } AstTypeTable* typeTablep() { return m_typeTablep; } @@ -1198,7 +1198,7 @@ public: const char* broken() const override; void cloneRelink() override; void dump(std::ostream& str) const override; - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } AstPackage* packagep() const { return m_packagep; } void packagep(AstPackage* nodep) { m_packagep = nodep; } }; @@ -1224,7 +1224,7 @@ public: const char* broken() const override; void cloneRelink() override; void dump(std::ostream& str) const override; - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } AstPackage* packagep() const { return m_packagep; } void packagep(AstPackage* nodep) { m_packagep = nodep; } }; @@ -1250,7 +1250,7 @@ public: ASTGEN_MEMBERS_AstPin; void dump(std::ostream& str) const override; const char* broken() const override; - string name() const override { return m_name; } // * = Pin name, ""=go by number + string name() const override VL_MT_STABLE { return m_name; } // * = Pin name, ""=go by number void name(const string& name) override { m_name = name; } string prettyOperatorName() const override; bool dotStar() const { return name() == ".*"; } // Fake name for .* connections until linked @@ -1278,7 +1278,7 @@ public: , m_pinNum{pinnum} , m_name{name} {} ASTGEN_MEMBERS_AstPort; - string name() const override { return m_name; } // * = Port name + string name() const override VL_MT_STABLE { return m_name; } // * = Port name int pinNum() const { return m_pinNum; } // * = Pin number, for order based instantiation }; class AstPragma final : public AstNode { @@ -1356,11 +1356,11 @@ public: void cloneRelink() override; const char* broken() const override; bool maybePointedTo() const override { return true; } - string name() const override { return m_name; } // * = Scope name + string name() const override VL_MT_STABLE { return m_name; } // * = Scope name void name(const string& name) override { m_name = name; } void dump(std::ostream& str) const override; string nameDotless() const; - string nameVlSym() const { return ((string("vlSymsp->")) + nameDotless()); } + string nameVlSym() const { return string{"vlSymsp->"} + nameDotless(); } AstNodeModule* modp() const { return m_modp; } // AstScope* aboveScopep() const VL_MT_SAFE { return m_aboveScopep; } @@ -1544,9 +1544,11 @@ public: ASTGEN_MEMBERS_AstTypedef; void dump(std::ostream& str) const override; AstNodeDType* getChildDTypep() const override { return childDTypep(); } - virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); } + virtual AstNodeDType* subDTypep() const VL_MT_STABLE { + return dtypep() ? dtypep() : childDTypep(); + } // METHODS - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } bool maybePointedTo() const override { return true; } bool hasDType() const override { return true; } void name(const string& flag) override { m_name = flag; } @@ -1566,7 +1568,7 @@ public: , m_name{name} {} ASTGEN_MEMBERS_AstTypedefFwd; // METHODS - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } bool maybePointedTo() const override { return true; } }; class AstUdpTable final : public AstNode { @@ -1586,7 +1588,7 @@ public: : ASTGEN_SUPER_UdpTableLine(fl) , m_text{text} {} ASTGEN_MEMBERS_AstUdpTableLine; - string name() const override { return m_text; } + string name() const override VL_MT_STABLE { return m_text; } string text() const { return m_text; } }; class AstVar final : public AstNode { @@ -1756,7 +1758,7 @@ public: } ASTGEN_MEMBERS_AstVar; void dump(std::ostream& str) const override; - string name() const override VL_MT_SAFE { return m_name; } // * = Var name + string name() const override VL_MT_STABLE VL_MT_SAFE { return m_name; } // * = Var name bool hasDType() const override { return true; } bool maybePointedTo() const override { return true; } string origName() const override { return m_origName; } // * = Original name @@ -1787,17 +1789,17 @@ public: string dpiTmpVarType(const string& varName) const; // Return Verilator internal type for argument: CData, SData, IData, WData string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "", - bool asRef = false) const VL_MT_SAFE; + bool asRef = false) const VL_MT_STABLE; string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration void combineType(VVarType type); AstNodeDType* getChildDTypep() const override { return childDTypep(); } - AstNodeDType* dtypeSkipRefp() const VL_MT_SAFE { return subDTypep()->skipRefp(); } + AstNodeDType* dtypeSkipRefp() const VL_MT_STABLE { return subDTypep()->skipRefp(); } // (Slow) recurse down to find basic data type (Note don't need virtual - // AstVar isn't a NodeDType) - AstBasicDType* basicp() const VL_MT_SAFE { return subDTypep()->basicp(); } - virtual AstNodeDType* subDTypep() const VL_MT_SAFE { + AstBasicDType* basicp() const VL_MT_STABLE { return subDTypep()->basicp(); } + virtual AstNodeDType* subDTypep() const VL_MT_STABLE { return dtypep() ? dtypep() : childDTypep(); } void ansi(bool flag) { m_ansi = flag; } @@ -1996,11 +1998,11 @@ public: return nullptr; } bool maybePointedTo() const override { return true; } - string name() const override { return scopep()->name() + "->" + varp()->name(); } + string name() const override VL_MT_STABLE { return scopep()->name() + "->" + varp()->name(); } void dump(std::ostream& str) const override; bool hasDType() const override { return true; } - AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable - AstScope* scopep() const { return m_scopep; } // Pointer to scope it's under + AstVar* varp() const VL_MT_STABLE { return m_varp; } // [After Link] Pointer to variable + AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under void scopep(AstScope* nodep) { m_scopep = nodep; } bool isTrace() const { return m_trace; } void trace(bool flag) { m_trace = flag; } @@ -2114,7 +2116,9 @@ class AstClass final : public AstNodeModule { AstClassPackage* m_classOrPackagep = nullptr; // Class package this is under bool m_extended = false; // Is extension or extended by other classes bool m_interfaceClass = false; // Interface class + bool m_needRNG = false; // Need RNG, uses srandom/randomize bool m_virtual = false; // Virtual class + bool m_parameterized = false; // Parameterized class void insertCache(AstNode* nodep); public: @@ -2146,9 +2150,15 @@ public: void isInterfaceClass(bool flag) { m_interfaceClass = flag; } bool isVirtual() const { return m_virtual; } void isVirtual(bool flag) { m_virtual = flag; } + bool needRNG() const { return m_needRNG; } + void needRNG(bool flag) { m_needRNG = flag; } + bool isParameterized() const { return m_parameterized; } + void isParameterized(bool flag) { m_parameterized = flag; } // Return true if this class is an extension of base class (SLOW) // Accepts nullptrs static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp); + // Return the lowest class extended from, or this class + AstClass* baseMostClassp(); }; class AstClassPackage final : public AstNodeModule { // The static information portion of a class (treated similarly to a package) @@ -2334,20 +2344,20 @@ public: inline AstRange(FileLine* fl, int left, int right); inline AstRange(FileLine* fl, const VNumRange& range); ASTGEN_MEMBERS_AstRange; - inline int leftConst() const VL_MT_SAFE; - inline int rightConst() const VL_MT_SAFE; - int hiConst() const VL_MT_SAFE { + inline int leftConst() const VL_MT_STABLE; + inline int rightConst() const VL_MT_STABLE; + int hiConst() const VL_MT_STABLE { const int l = leftConst(); const int r = rightConst(); return l > r ? l : r; } - int loConst() const VL_MT_SAFE { + int loConst() const VL_MT_STABLE { const int l = leftConst(); const int r = rightConst(); return l > r ? r : l; } - int elementsConst() const VL_MT_SAFE { return hiConst() - loConst() + 1; } - bool littleEndian() const { return leftConst() < rightConst(); } + int elementsConst() const VL_MT_STABLE { return hiConst() - loConst() + 1; } + bool ascending() const { return leftConst() < rightConst(); } void dump(std::ostream& str) const override; virtual string emitC() { V3ERROR_NA_RETURN(""); } bool same(const AstNode* /*samep*/) const override { return true; } @@ -2450,7 +2460,7 @@ public: , m_name{name} , m_showAt{showAt} {} ASTGEN_MEMBERS_AstComment; - string name() const override { return m_name; } // * = Text + string name() const override VL_MT_STABLE { return m_name; } // * = Text bool same(const AstNode* samep) const override { return true; } // Ignore name in comments virtual bool showAt() const { return m_showAt; } }; @@ -2591,7 +2601,7 @@ public: : ASTGEN_SUPER_Disable(fl) , m_name{name} {} ASTGEN_MEMBERS_AstDisable; - string name() const override { return m_name; } // * = Block name + string name() const override VL_MT_STABLE { return m_name; } // * = Block name void name(const string& flag) override { m_name = flag; } bool isBrancher() const override { return true; // SPECIAL: We don't process code after breaks @@ -2633,8 +2643,8 @@ public: return nullptr; } string verilogKwd() const override { - return (filep() ? string("$f") + string(displayType().ascii()) - : string("$") + string(displayType().ascii())); + return (filep() ? string{"$f"} + string{displayType().ascii()} + : string{"$"} + string{displayType().ascii()}); } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } @@ -2651,17 +2661,13 @@ public: bool addNewline() const { return displayType().addNewline(); } }; class AstDoWhile final : public AstNodeStmt { - // @astgen op1 := precondsp : List[AstNode] - // @astgen op2 := condp : AstNodeExpr - // @astgen op3 := stmtsp : List[AstNode] - // @astgen op4 := incsp : List[AstNode] + // @astgen op1 := condp : AstNodeExpr + // @astgen op2 := stmtsp : List[AstNode] public: - AstDoWhile(FileLine* fl, AstNodeExpr* conditionp, AstNode* stmtsp = nullptr, - AstNode* incsp = nullptr) + AstDoWhile(FileLine* fl, AstNodeExpr* conditionp, AstNode* stmtsp = nullptr) : ASTGEN_SUPER_DoWhile(fl) { condp(conditionp); addStmtsp(stmtsp); - addIncsp(incsp); } ASTGEN_MEMBERS_AstDoWhile; bool isGateOptimizable() const override { return false; } @@ -2739,46 +2745,6 @@ public: bool isUnlikely() const override { return true; } bool same(const AstNode* /*samep*/) const override { return true; } }; -class AstFOpen final : public AstNodeStmt { - // Although a system function in IEEE, here a statement which sets the file pointer (MCD) - // @astgen op1 := filep : AstNodeExpr - // @astgen op2 := filenamep : AstNodeExpr - // @astgen op3 := modep : AstNodeExpr -public: - AstFOpen(FileLine* fl, AstNodeExpr* filep, AstNodeExpr* filenamep, AstNodeExpr* modep) - : ASTGEN_SUPER_FOpen(fl) { - this->filep(filep); - this->filenamep(filenamep); - this->modep(modep); - } - ASTGEN_MEMBERS_AstFOpen; - string verilogKwd() const override { return "$fopen"; } - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } - bool isOutputter() const override { return true; } - bool isUnlikely() const override { return true; } - bool same(const AstNode* /*samep*/) const override { return true; } -}; -class AstFOpenMcd final : public AstNodeStmt { - // Although a system function in IEEE, here a statement which sets the file pointer (MCD) - // @astgen op1 := filep : AstNodeExpr - // @astgen op2 := filenamep : AstNodeExpr -public: - AstFOpenMcd(FileLine* fl, AstNodeExpr* filep, AstNodeExpr* filenamep) - : ASTGEN_SUPER_FOpenMcd(fl) { - this->filep(filep); - this->filenamep(filenamep); - } - ASTGEN_MEMBERS_AstFOpenMcd; - string verilogKwd() const override { return "$fopen"; } - bool isGateOptimizable() const override { return false; } - bool isPredictOptimizable() const override { return false; } - bool isPure() const override { return false; } - bool isOutputter() const override { return true; } - bool isUnlikely() const override { return true; } - bool same(const AstNode* /*samep*/) const override { return true; } -}; class AstFinish final : public AstNodeStmt { public: explicit AstFinish(FileLine* fl) @@ -2821,7 +2787,7 @@ public: bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } }; class AstJumpBlock final : public AstNodeStmt { - // Block of code including a JumpGo and JumpLabel + // Block of code including a single JumpLabel, and 0+ JumpGo's to that label // Parents: {statement list} // Children: {statement list, with JumpGo and JumpLabel below} // @astgen op1 := stmtsp : List[AstNode] @@ -2926,7 +2892,7 @@ public: : ASTGEN_SUPER_PrintTimeScale(fl) {} ASTGEN_MEMBERS_AstPrintTimeScale; void name(const string& name) override { m_name = name; } - string name() const override { return m_name; } // * = Var name + string name() const override VL_MT_STABLE { return m_name; } // * = Var name void dump(std::ostream& str) const override; string verilogKwd() const override { return "$printtimescale"; } bool isGateOptimizable() const override { return false; } @@ -3145,7 +3111,7 @@ public: void dump(std::ostream& str) const override; int instrCount() const override { return 100; } // Large... ASTGEN_MEMBERS_AstTraceDecl; - string name() const override { return m_showname; } + string name() const override VL_MT_STABLE { return m_showname; } bool maybePointedTo() const override { return true; } bool hasDType() const override { return true; } bool same(const AstNode* samep) const override { return false; } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 135674029..6c300058f 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -115,6 +115,7 @@ void AstNodeUOrStructDType::repairMemberCache() { } const char* AstNodeUOrStructDType::broken() const { + BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists()); std::unordered_set exists; for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) { exists.insert(itemp); @@ -232,6 +233,15 @@ int AstBasicDType::widthTotalBytes() const { } } +bool AstBasicDType::same(const AstNode* samep) const { + const AstBasicDType* const sp = static_cast(samep); + if (!rangep() && !sp->rangep() && m == sp->m) { + return true; + } else { + return m == sp->m && rangep() && rangep()->sameTree(sp->rangep()); + } +} + int AstNodeUOrStructDType::widthTotalBytes() const { if (width() <= 8) { return 1; @@ -387,7 +397,7 @@ string AstVar::verilogKwd() const { } string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc, - bool asRef) const VL_MT_SAFE { + bool asRef) const VL_MT_STABLE { UASSERT_OBJ(!forReturn, this, "Internal data is never passed as return, but as first argument"); string ostatic; @@ -573,7 +583,7 @@ string AstVar::dpiArgType(bool named, bool forReturn) const { } else { class converter final : public dpiTypesToStringConverter { string bitLogicVector(const AstVar* varp, bool isBit) const override { - return string(varp->isReadOnly() ? "const " : "") + return string{varp->isReadOnly() ? "const " : ""} + dpiTypesToStringConverter::bitLogicVector(varp, isBit) + '*'; } string primitive(const AstVar* varp) const override { @@ -636,13 +646,13 @@ string AstVar::dpiTmpVarType(const string& varName) const { string AstVar::scType() const { if (isScBigUint()) { - return (string("sc_biguint<") + cvtToStr(widthMin()) + return (string{"sc_biguint<"} + cvtToStr(widthMin()) + "> "); // Keep the space so don't get >> } else if (isScUint()) { - return (string("sc_uint<") + cvtToStr(widthMin()) + return (string{"sc_uint<"} + cvtToStr(widthMin()) + "> "); // Keep the space so don't get >> } else if (isScBv()) { - return (string("sc_bv<") + cvtToStr(widthMin()) + "> "); // Keep the space so don't get >> + return (string{"sc_bv<"} + cvtToStr(widthMin()) + "> "); // Keep the space so don't get >> } else if (widthMin() == 1) { return "bool"; } else if (widthMin() <= VL_IDATASIZE) { @@ -704,12 +714,12 @@ public: } }; -string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const VL_MT_SAFE { +string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const VL_MT_STABLE { const CTypeRecursed info = cTypeRecurse(false); return info.render(name, isRef); } -AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const { +AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const VL_MT_STABLE { // Legacy compound argument currently just passed through and unused CTypeRecursed info; @@ -734,9 +744,9 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const { const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true); info.m_type = "VlSampleQueue<" + sub.m_type + ">"; } else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) { - info.m_type = "VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(adtypep) + ">"; + info.m_type = "VlClassRef<" + EmitCBase::prefixNameProtect(adtypep) + ">"; } else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) { - info.m_type = EmitCBaseVisitor::prefixNameProtect(adtypep->ifaceViaCellp()) + "*"; + info.m_type = EmitCBase::prefixNameProtect(adtypep->ifaceViaCellp()) + "*"; } else if (const auto* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) { if (adtypep->isCompound()) compound = true; const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound); @@ -745,7 +755,7 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const { info.m_type += ">"; } else if (VN_IS(dtypep, NodeUOrStructDType) && !VN_AS(dtypep, NodeUOrStructDType)->packed()) { const auto* const sdtypep = VN_AS(dtypep, NodeUOrStructDType); - info.m_type = EmitCBaseVisitor::prefixNameProtect(sdtypep); + info.m_type = EmitCBase::prefixNameProtect(sdtypep); } else if (const AstBasicDType* const bdtypep = dtypep->basicp()) { // We don't print msb()/lsb() as multidim packed would require recursion, // and may confuse users as C++ data is stored always with bit 0 used @@ -846,7 +856,7 @@ int AstNodeDType::widthPow2() const { return 1; } -bool AstNodeDType::isLiteralType() const VL_MT_SAFE { +bool AstNodeDType::isLiteralType() const VL_MT_STABLE { if (const auto* const dtypep = VN_CAST(skipRefp(), BasicDType)) { return dtypep->keyword().isLiteralType(); } else if (const auto* const dtypep = VN_CAST(skipRefp(), UnpackArrayDType)) { @@ -916,10 +926,10 @@ void AstScope::cloneRelink() { } } string AstScope::nameDotless() const { - string out = shortName(); + string result = shortName(); string::size_type pos; - while ((pos = out.find('.')) != string::npos) out.replace(pos, 1, "__"); - return out; + while ((pos = result.find('.')) != string::npos) result.replace(pos, 1, "__"); + return result; } AstVarScope* AstScope::createTemp(const string& name, unsigned width) { @@ -1454,6 +1464,13 @@ void AstClass::repairCache() { } } } +AstClass* AstClass::baseMostClassp() { + AstClass* basep = this; + while (basep->extendsp() && basep->extendsp()->classp()) { + basep = basep->extendsp()->classp(); + } + return basep; +} bool AstClass::isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp) { // TAIL RECURSIVE if (!refClassp || !baseClassp) return false; @@ -1672,15 +1689,6 @@ const char* AstMemberSel::broken() const { BROKEN_RTN(m_varp && !m_varp->brokeExists()); return nullptr; } -void AstMethodCall::dump(std::ostream& str) const { - this->AstNodeFTaskRef::dump(str); - str << " -> "; - if (taskp()) { - taskp()->dump(str); - } else { - str << " -> UNLINKED"; - } -} void AstModportFTaskRef::dump(std::ostream& str) const { this->AstNode::dump(str); if (isExport()) str << " EXPORT"; @@ -1767,7 +1775,7 @@ void AstTypedef::dump(std::ostream& str) const { void AstNodeRange::dump(std::ostream& str) const { this->AstNode::dump(str); } void AstRange::dump(std::ostream& str) const { this->AstNodeRange::dump(str); - if (littleEndian()) str << " [LITTLE]"; + if (ascending()) str << " [ASCENDING]"; } void AstParamTypeDType::dump(std::ostream& str) const { this->AstNodeDType::dump(str); @@ -1813,7 +1821,7 @@ void AstRefDType::cloneRelink() { m_classOrPackagep = m_classOrPackagep->clonep(); } } -AstNodeDType* AstRefDType::subDTypep() const { +AstNodeDType* AstRefDType::subDTypep() const VL_MT_STABLE { if (typedefp()) return typedefp()->subDTypep(); return refDTypep(); // Maybe nullptr } @@ -2313,7 +2321,7 @@ int AstCMethodHard::instrCount() const { if (AstBasicDType* const basicp = fromp()->dtypep()->basicp()) { // TODO: add a more structured description of library methods, rather than using string // matching. See #3715. - if (basicp->isTriggerVec() && m_name == "at") { + if (basicp->isTriggerVec() && m_name == "word") { // This is an important special case for scheduling so we compute it precisely, // it is simply a load. return INSTR_COUNT_LD; diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index b936e33bb..b7707754d 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -144,9 +144,20 @@ private: // VISITORS void visit(AstFork* nodep) override { - // Keep this begin to group its statements together + // Keep begins in forks to group their statements together VL_RESTORER(m_keepBegins); m_keepBegins = true; + // If a statement is not a begin, wrap it in a begin. This fixes an issue when the + // statement is a task call that gets inlined later (or any other statement that gets + // replaced with multiple statements) + for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + if (!VN_IS(stmtp, Begin)) { + AstBegin* const beginp = new AstBegin{stmtp->fileline(), "", nullptr}; + stmtp->replaceWith(beginp); + beginp->addStmtsp(stmtp); + stmtp = beginp; + } + } dotNames(nodep, "__FORK__"); nodep->name(""); } @@ -288,7 +299,7 @@ private: // To keep correct visual order, must add before other Text's AstText* const afterp = nodep->scopeAttrp(); if (afterp) afterp->unlinkFrBackWithNext(); - nodep->addScopeAttrp(new AstText{nodep->fileline(), string("__DOT__") + scname}); + nodep->addScopeAttrp(new AstText{nodep->fileline(), string{"__DOT__"} + scname}); if (afterp) nodep->addScopeAttrp(afterp); } iterateChildren(nodep); diff --git a/src/V3Branch.cpp b/src/V3Branch.cpp index 003b0cb24..019f442c5 100644 --- a/src/V3Branch.cpp +++ b/src/V3Branch.cpp @@ -38,7 +38,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### // Branch state, as a visitor of each AstNode -class BranchVisitor final : public VNVisitor { +class BranchVisitor final : public VNVisitorConst { private: // NODE STATE // Entire netlist: @@ -71,12 +71,12 @@ private: { // Do if reset(); - iterateAndNextNull(nodep->thensp()); + iterateAndNextConstNull(nodep->thensp()); const int ifLikely = m_likely; const int ifUnlikely = m_unlikely; // Do else reset(); - iterateAndNextNull(nodep->elsesp()); + iterateAndNextConstNull(nodep->elsesp()); const int elseLikely = m_likely; const int elseUnlikely = m_unlikely; // Compute @@ -91,16 +91,16 @@ private: void visit(AstNodeCCall* nodep) override { checkUnlikely(nodep); nodep->funcp()->user1Inc(); - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstCFunc* nodep) override { checkUnlikely(nodep); m_cfuncsp.push_back(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstNode* nodep) override { checkUnlikely(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); } // METHODS @@ -114,7 +114,7 @@ public: // CONSTRUCTORS explicit BranchVisitor(AstNetlist* nodep) { reset(); - iterateChildren(nodep); + iterateChildrenConst(nodep); calc_tasks(); } ~BranchVisitor() override = default; diff --git a/src/V3Broken.cpp b/src/V3Broken.cpp index aa02b4dd3..14c01d029 100644 --- a/src/V3Broken.cpp +++ b/src/V3Broken.cpp @@ -62,6 +62,8 @@ public: } } s_brokenCntGlobal; +static bool s_brokenAllowMidvisitorCheck = false; + //###################################################################### // Table of allocated AstNode pointers @@ -144,7 +146,7 @@ bool V3Broken::isLinkable(const AstNode* nodep) { return s_linkableTable.isLinka //###################################################################### // Check every node in tree -class BrokenCheckVisitor final : public VNVisitor { +class BrokenCheckVisitor final : public VNVisitorConst { bool m_inScope = false; // Under AstScope // Constants for marking we are under/not under a node @@ -172,6 +174,7 @@ private: const char* const whyp = nodep->broken(); UASSERT_OBJ(!whyp, nodep, "Broken link in node (or something without maybePointedTo): " << whyp); + if (!s_brokenAllowMidvisitorCheck) nodep->checkIter(); if (nodep->dtypep()) { UASSERT_OBJ(nodep->dtypep()->brokeExists(), nodep, "Broken link in node->dtypep() to " << cvtToHex(nodep->dtypep())); @@ -312,7 +315,7 @@ private: public: // CONSTRUCTORS - explicit BrokenCheckVisitor(AstNetlist* nodep) { iterate(nodep); } + explicit BrokenCheckVisitor(AstNetlist* nodep) { iterateConstNull(nodep); } ~BrokenCheckVisitor() override = default; }; @@ -351,6 +354,8 @@ void V3Broken::brokenAll(AstNetlist* nodep) { } } +void V3Broken::allowMidvisitorCheck(bool flag) { s_brokenAllowMidvisitorCheck = flag; } + //###################################################################### // Self test diff --git a/src/V3Broken.h b/src/V3Broken.h index 6f4822642..d98add08b 100644 --- a/src/V3Broken.h +++ b/src/V3Broken.h @@ -32,6 +32,9 @@ public: static bool isLinkable(const AstNode* nodep); static void addNewed(const AstNode* nodep); static void deleted(const AstNode* nodep); + // Called on error to say may be inside visitor, + // Disables checks that may misfire if not called at stable point between visitors + static void allowMidvisitorCheck(bool flag); static void selfTest(); }; diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index c70e11b2a..a079006ec 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -70,7 +70,7 @@ private: funcp->slow(!m_type.isClass()); // Only classes construct on fast path string preventUnusedStmt; if (m_type.isClass()) { - funcp->argTypes(EmitCBaseVisitor::symClassVar()); + funcp->argTypes(EmitCBase::symClassVar()); preventUnusedStmt = "if (false && vlSymsp) {} // Prevent unused\n"; } else if (m_type.isCoverage()) { funcp->argTypes("bool first"); @@ -148,6 +148,7 @@ private: m_modp = nodep; V3CCtorsBuilder var_reset{nodep, "_ctor_var_reset", VN_IS(nodep, Class) ? VCtorType::CLASS : VCtorType::MODULE}; + // cppcheck-suppress danglingLifetime m_varResetp = &var_reset; iterateChildren(nodep); @@ -201,7 +202,7 @@ private: public: // CONSTRUCTORS - CCtorsVisitor(AstNode* nodep) { iterate(nodep); } + explicit CCtorsVisitor(AstNode* nodep) { iterate(nodep); } ~CCtorsVisitor() override = default; }; diff --git a/src/V3Case.cpp b/src/V3Case.cpp index 8a3857c17..4ae5969ad 100644 --- a/src/V3Case.cpp +++ b/src/V3Case.cpp @@ -53,7 +53,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### -class CaseLintVisitor final : public VNVisitor { +class CaseLintVisitor final : public VNVisitorConst { private: const AstNodeCase* m_caseExprp = nullptr; // Under a CASE value node, if so the relevant case statement @@ -79,10 +79,10 @@ private: // Check for X/Z in non-casex statements { m_caseExprp = nodep; - iterate(nodep->exprp()); + iterateConst(nodep->exprp()); for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp = VN_AS(itemp->nextp(), CaseItem)) { - iterateAndNextNull(itemp->condsp()); + iterateAndNextConstNull(itemp->condsp()); } m_caseExprp = nullptr; } @@ -108,11 +108,11 @@ private: } } } - void visit(AstNode* nodep) override { iterateChildren(nodep); } + void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } public: // CONSTRUCTORS - explicit CaseLintVisitor(AstNodeCase* nodep) { iterate(nodep); } + explicit CaseLintVisitor(AstNodeCase* nodep) { iterateConst(nodep); } ~CaseLintVisitor() override = default; }; diff --git a/src/V3Class.cpp b/src/V3Class.cpp index 61513782a..3c726a3a6 100644 --- a/src/V3Class.cpp +++ b/src/V3Class.cpp @@ -178,7 +178,7 @@ private: void setStructModulep(AstNodeUOrStructDType* const dtypep) { // Give it a pointer to its package and a final name - dtypep->classOrPackagep(m_modp); + dtypep->classOrPackagep(m_classPackagep ? m_classPackagep : m_modp); dtypep->name(dtypep->name() + (VN_IS(dtypep, UnionDType) ? "__union" : "__struct") + cvtToStr(dtypep->uniqueNum())); @@ -193,6 +193,7 @@ private: void visit(AstTypedef* nodep) override { if (nodep->user1SetOnce()) return; iterateChildren(nodep); + if (m_classPackagep) m_classPackagep->addStmtsp(nodep->unlinkFrBack()); AstNodeUOrStructDType* const dtypep = VN_CAST(nodep->dtypep(), NodeUOrStructDType); if (dtypep && !dtypep->packed()) { diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index f2a537555..bf374d2a3 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -95,7 +95,7 @@ private: if (vscp->user1p()) return VN_AS(vscp->user1p(), VarScope); const AstVar* const varp = vscp->varp(); const string newvarname - = string("__Vsampled__") + vscp->scopep()->nameDotless() + "__" + varp->name(); + = string{"__Vsampled__"} + vscp->scopep()->nameDotless() + "__" + varp->name(); FileLine* const flp = vscp->fileline(); AstVar* const newvarp = new AstVar{flp, VVarType::MODULETEMP, newvarname, varp->dtypep()}; newvarp->noReset(true); // Reset by below assign @@ -146,17 +146,19 @@ private: UASSERT_OBJ(nodep->hasClocked(), nodep, "Should have been converted by V3Sched"); UASSERT_OBJ(nodep->stmtsp(), nodep, "Should not have been created if empty"); - VNRelinker relinker; - nodep->unlinkFrBack(&relinker); AstNode* const stmtsp = nodep->stmtsp()->unlinkFrBackWithNext(); // Create 'if' statement, if needed if (!m_lastSenp || !nodep->sensesp()->sameTree(m_lastSenp)) { + VNRelinker relinker; + nodep->unlinkFrBack(&relinker); clearLastSen(); m_lastSenp = nodep->sensesp(); // Make a new if statement m_lastIfp = makeActiveIf(m_lastSenp); relinker.relink(m_lastIfp); + } else { + nodep->unlinkFrBack(); } // Move statements to if diff --git a/src/V3Common.cpp b/src/V3Common.cpp index 72f900eb1..b5f5a4c8c 100644 --- a/src/V3Common.cpp +++ b/src/V3Common.cpp @@ -37,7 +37,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; static void makeVlToString(AstClass* nodep) { AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"}; - funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep) + ">& obj"); + funcp->argTypes("const VlClassRef<" + EmitCBase::prefixNameProtect(nodep) + ">& obj"); funcp->isMethod(false); funcp->isConst(false); funcp->isStatic(false); @@ -51,7 +51,7 @@ static void makeVlToString(AstClass* nodep) { static void makeVlToString(AstIface* nodep) { AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"}; - funcp->argTypes("const " + EmitCBaseVisitor::prefixNameProtect(nodep) + "* obj"); + funcp->argTypes("const " + EmitCBase::prefixNameProtect(nodep) + "* obj"); funcp->isMethod(false); funcp->isConst(false); funcp->isStatic(false); @@ -65,7 +65,7 @@ static void makeVlToString(AstNodeUOrStructDType* nodep) { AstNodeModule* const modp = nodep->classOrPackagep(); AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"}; - funcp->argTypes("const " + EmitCBaseVisitor::prefixNameProtect(nodep) + "& obj"); + funcp->argTypes("const " + EmitCBase::prefixNameProtect(nodep) + "& obj"); funcp->isMethod(false); funcp->isConst(false); funcp->isStatic(false); @@ -137,7 +137,7 @@ static void makeToStringMiddle(AstClass* nodep) { string stmt = "out += "; if (!comma.empty()) stmt += "\", \"+ "; // comma = ", "; // Nothing further so not needed - stmt += EmitCBaseVisitor::prefixNameProtect(nodep->extendsp()->dtypep()); + stmt += EmitCBase::prefixNameProtect(nodep->extendsp()->dtypep()); stmt += "::to_string_middle();\n"; nodep->user1(true); // So what we extend dumps this funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt}); diff --git a/src/V3Config.cpp b/src/V3Config.cpp index b6ede32c0..7bcb4b72c 100644 --- a/src/V3Config.cpp +++ b/src/V3Config.cpp @@ -40,25 +40,31 @@ template class V3ConfigWildcardResolver final { using Map = std::map; - Map m_mapWildcard; // Wildcard strings to entities - Map m_mapResolved; // Resolved strings to converged entities + mutable V3Mutex m_mutex; // protects members + Map m_mapWildcard VL_GUARDED_BY(m_mutex); // Wildcard strings to entities + Map m_mapResolved VL_GUARDED_BY(m_mutex); // Resolved strings to converged entities public: V3ConfigWildcardResolver() = default; ~V3ConfigWildcardResolver() = default; /// Update into maps from other - void update(const V3ConfigWildcardResolver& other) { + void update(const V3ConfigWildcardResolver& other) VL_MT_SAFE_EXCLUDES(m_mutex) + VL_EXCLUDES(other.m_mutex) { + V3LockGuard lock{m_mutex}; + V3LockGuard otherLock{other.m_mutex}; for (const auto& itr : other.m_mapResolved) m_mapResolved[itr.first].update(itr.second); for (const auto& itr : other.m_mapWildcard) m_mapWildcard[itr.first].update(itr.second); } // Access and create a (wildcard) entity - T& at(const string& name) { + T& at(const string& name) VL_MT_SAFE_EXCLUDES(m_mutex) { + V3LockGuard lock{m_mutex}; // Don't store into wildcards if the name is not a wildcard string return m_mapWildcard[name]; } // Access an entity and resolve wildcards that match it - T* resolve(const string& name) { + T* resolve(const string& name) VL_MT_SAFE_EXCLUDES(m_mutex) { + V3LockGuard lock{m_mutex}; // Lookup if it was resolved before, typically not auto it = m_mapResolved.find(name); if (VL_UNLIKELY(it != m_mapResolved.end())) return &it->second; @@ -78,7 +84,10 @@ public: return newp; } // Flush on update - void flush() { m_mapResolved.clear(); } + void flush() VL_MT_SAFE_EXCLUDES(m_mutex) { + V3LockGuard lock{m_mutex}; + m_mapResolved.clear(); + } }; // Only public_flat_rw has the sensitity tree diff --git a/src/V3Config.h b/src/V3Config.h index eff00b846..2ae32ef0d 100644 --- a/src/V3Config.h +++ b/src/V3Config.h @@ -23,6 +23,7 @@ #include "V3Ast.h" #include "V3Error.h" #include "V3FileLine.h" +#include "V3Mutex.h" //###################################################################### diff --git a/src/V3Const.cpp b/src/V3Const.cpp index dc195e158..47f1a4b05 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -70,7 +70,7 @@ static int countTrailingZeroes(uint64_t val) { // This visitor can be used in the post-expanded Ast from V3Expand, where the Ast satisfies: // - Constants are 64 bit at most (because words are accessed via AstWordSel) // - Variables are scoped. -class ConstBitOpTreeVisitor final : public VNVisitor { +class ConstBitOpTreeVisitor final : public VNVisitorConst { // NODE STATE // AstVarRef::user4u -> Base index of m_varInfos that points VarInfo // AstVarScope::user4u -> Same as AstVarRef::user4 @@ -416,7 +416,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor { { VL_RESTORER(m_leafp); m_leafp = &info; - iterate(nodep); + iterateConst(nodep); } bool ok = !m_failed; @@ -431,7 +431,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor { // VISITORS void visit(AstNode* nodep) override { CONST_BITOP_SET_FAILED("Hit unexpected op", nodep); } void visit(AstCCast* nodep) override { - iterateChildren(nodep); + iterateChildrenConst(nodep); if (m_leafp) m_leafp->updateBitRange(nodep); } void visit(AstShiftR* nodep) override { @@ -440,7 +440,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor { CONST_BITOP_RETURN_IF(!constp, nodep->rhsp()); m_lsb += constp->toUInt(); incrOps(nodep, __LINE__); - iterate(nodep->lhsp()); + iterateConst(nodep->lhsp()); m_leafp->updateBitRange(nodep); m_lsb -= constp->toUInt(); } @@ -449,12 +449,10 @@ class ConstBitOpTreeVisitor final : public VNVisitor { AstNode* lhsp = nodep->lhsp(); AstCCast* const castp = VN_CAST(lhsp, CCast); if (castp) lhsp = castp->lhsp(); - CONST_BITOP_RETURN_IF(!VN_IS(lhsp, VarRef) && !VN_IS(lhsp, Xor) && !VN_IS(lhsp, RedXor) - && !VN_IS(lhsp, ShiftR), - lhsp); + CONST_BITOP_RETURN_IF(!isXorTree() && !VN_IS(lhsp, VarRef) && !VN_IS(lhsp, ShiftR), lhsp); incrOps(nodep, __LINE__); m_polarity = !m_polarity; - iterateChildren(nodep); + iterateChildrenConst(nodep); // Don't restore m_polarity for Xor as it counts parity of the entire tree if (!isXorTree()) m_polarity = !m_polarity; if (m_leafp && castp) m_leafp->updateBitRange(castp); @@ -465,7 +463,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor { CONST_BITOP_RETURN_IF(!constp, nodep->rhsp()); UASSERT_OBJ(m_leafp->wordIdx() == -1, nodep, "Unexpected nested WordSel"); m_leafp->wordIdx(constp->toSInt()); - iterate(nodep->fromp()); + iterateConst(nodep->fromp()); } void visit(AstVarRef* nodep) override { CONST_BITOP_RETURN_IF(!m_leafp, nodep); @@ -529,7 +527,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor { // Always reach past a plain making AND Restorer restorer{*this}; incrOps(nodep, __LINE__); - iterate(nodep->rhsp()); + iterateConst(nodep->rhsp()); CONST_BITOP_RETURN_IF(m_failed, nodep->rhsp()); restorer.disableRestore(); // Now all checks passed } else if (nodep->type() == m_rootp->type()) { // And, Or, Xor @@ -542,13 +540,14 @@ class ConstBitOpTreeVisitor final : public VNVisitor { m_leafp = &leafInfo; AstNodeExpr* opp = right ? nodep->rhsp() : nodep->lhsp(); const bool origFailed = m_failed; - iterate(opp); + iterateConst(opp); if (leafInfo.constp() || m_failed) { // Revert changes in leaf restorer.restoreNow(); // Reach past a cast then add to frozen nodes to be added to final reduction if (const AstCCast* const castp = VN_CAST(opp, CCast)) opp = castp->lhsp(); - m_frozenNodes.emplace_back(opp, FrozenNodeInfo{m_polarity, m_lsb}); + const bool pol = isXorTree() || m_polarity; // Only AND/OR tree needs polarity + m_frozenNodes.emplace_back(opp, FrozenNodeInfo{pol, m_lsb}); m_failed = origFailed; continue; } @@ -647,11 +646,11 @@ class ConstBitOpTreeVisitor final : public VNVisitor { m_varInfos.push_back(nullptr); CONST_BITOP_RETURN_IF(!isAndTree() && !isOrTree() && !isXorTree(), nodep); if (AstNodeBiop* const biopp = VN_CAST(nodep, NodeBiop)) { - iterate(biopp); + iterateConst(biopp); } else { UASSERT_OBJ(VN_IS(nodep, RedXor), nodep, "Must be RedXor"); incrOps(nodep, __LINE__); - iterateChildren(nodep); + iterateChildrenConst(nodep); } for (auto&& entry : m_bitPolarities) { getVarInfo(entry.m_info).setPolarity(entry.m_polarity, entry.m_bit); @@ -2265,7 +2264,7 @@ private: // VISITORS void visit(AstNetlist* nodep) override { // Iterate modules backwards, in bottom-up order. That's faster - iterateChildrenBackwards(nodep); + iterateChildrenBackwardsConst(nodep); } void visit(AstNodeModule* nodep) override { VL_RESTORER(m_modp); @@ -2747,52 +2746,74 @@ private: } } - struct SenItemCmp { - bool operator()(const AstSenItem* lhsp, const AstSenItem* rhsp) const { - if (lhsp->type() < rhsp->type()) return true; - if (lhsp->type() > rhsp->type()) return false; - // Looks visually better if we keep sorted by name - if (!lhsp->sensp() && rhsp->sensp()) return true; - if (lhsp->sensp() && !rhsp->sensp()) return false; - if (lhsp->varrefp() && !rhsp->varrefp()) return true; - if (!lhsp->varrefp() && rhsp->varrefp()) return false; - if (lhsp->varrefp() && rhsp->varrefp()) { - if (lhsp->varrefp()->name() < rhsp->varrefp()->name()) return true; - if (lhsp->varrefp()->name() > rhsp->varrefp()->name()) return false; + class SenItemCmp final { + static int cmp(const AstNodeExpr* ap, const AstNodeExpr* bp) { + const VNType aType = ap->type(); + const VNType bType = bp->type(); + if (aType != bType) return static_cast(bType) - static_cast(aType); + + if (const AstVarRef* const aRefp = VN_CAST(ap, VarRef)) { + const AstVarRef* const bRefp = VN_AS(bp, VarRef); + // Looks visually better if we keep sorted by name + if (aRefp->name() < bRefp->name()) return -1; + if (aRefp->name() > bRefp->name()) return 1; // But might be same name with different scopes - if (lhsp->varrefp()->varScopep() < rhsp->varrefp()->varScopep()) return true; - if (lhsp->varrefp()->varScopep() > rhsp->varrefp()->varScopep()) return false; + if (aRefp->varScopep() < bRefp->varScopep()) return -1; + if (aRefp->varScopep() > bRefp->varScopep()) return 1; // Or rarely, different data types - if (lhsp->varrefp()->dtypep() < rhsp->varrefp()->dtypep()) return true; - if (lhsp->varrefp()->dtypep() > rhsp->varrefp()->dtypep()) return false; - } else if (AstCMethodHard* const lp = VN_CAST(lhsp->sensp(), CMethodHard)) { - if (AstCMethodHard* const rp = VN_CAST(rhsp->sensp(), CMethodHard)) { - if (AstVarRef* const lRefp = VN_CAST(lp->fromp(), VarRef)) { - if (AstVarRef* const rRefp = VN_CAST(rp->fromp(), VarRef)) { - if (lRefp->name() < rRefp->name()) return true; - if (lRefp->name() > rRefp->name()) return false; - // But might be same name with different scopes - if (lRefp->varScopep() < rRefp->varScopep()) return true; - if (lRefp->varScopep() > rRefp->varScopep()) return false; - // Or rarely, different data types - if (lRefp->dtypep() < rRefp->dtypep()) return true; - if (lRefp->dtypep() > rRefp->dtypep()) return false; - } - } - if (AstConst* lConstp = VN_CAST(lp->pinsp(), Const)) { - if (AstConst* rConstp = VN_CAST(rp->pinsp(), Const)) { - if (lConstp->toUInt() < rConstp->toUInt()) return true; - if (lConstp->toUInt() > rConstp->toUInt()) return false; - } - } - } + if (aRefp->dtypep() < bRefp->dtypep()) return -1; + if (aRefp->dtypep() > bRefp->dtypep()) return 1; + return 0; } - // Sort by edge, AFTER variable, as we want multiple edges for same var adjacent. - // note the SenTree optimizer requires this order (more - // general first, less general last) - if (lhsp->edgeType() < rhsp->edgeType()) return true; - if (lhsp->edgeType() > rhsp->edgeType()) return false; - return false; + + if (const AstConst* const aConstp = VN_CAST(ap, Const)) { + const AstConst* const bConstp = VN_AS(bp, Const); + if (aConstp->toUQuad() < bConstp->toUQuad()) return -1; + if (aConstp->toUQuad() > bConstp->toUQuad()) return 1; + return 0; + } + + if (const AstNodeBiop* const aBiOpp = VN_CAST(ap, NodeBiop)) { + const AstNodeBiop* const bBiOpp = VN_AS(bp, NodeBiop); + // Compare RHSs first as LHS might be const, but the variable term should become + // adjacent for optimization if identical. + if (const int c = cmp(aBiOpp->rhsp(), bBiOpp->rhsp())) return c; + return cmp(aBiOpp->lhsp(), bBiOpp->lhsp()); + } + + if (const AstCMethodHard* const aCallp = VN_CAST(ap, CMethodHard)) { + const AstCMethodHard* const bCallp = VN_AS(bp, CMethodHard); + if (aCallp->name() < bCallp->name()) return -1; + if (aCallp->name() > bCallp->name()) return 1; + if (const int c = cmp(aCallp->fromp(), bCallp->fromp())) return c; + AstNodeExpr* aPinsp = aCallp->pinsp(); + AstNodeExpr* bPinsp = bCallp->pinsp(); + while (aPinsp && bPinsp) { + if (const int c = cmp(aPinsp, bPinsp)) return c; + aPinsp = VN_AS(aPinsp->nextp(), NodeExpr); + bPinsp = VN_AS(bPinsp->nextp(), NodeExpr); + } + return aPinsp ? -1 : bPinsp ? 1 : 0; + } + + return 0; + } + + public: + bool operator()(const AstSenItem* lhsp, const AstSenItem* rhsp) const { + AstNodeExpr* const lSensp = lhsp->sensp(); + AstNodeExpr* const rSensp = rhsp->sensp(); + if (lSensp && rSensp) { + // If both terms have sensitivity expressions, recursively compare them + if (const int c = cmp(lSensp, rSensp)) return c < 0; + } else if (lSensp || rSensp) { + // Terms with sensitivity expressions come after those without + return rSensp; + } + // Finally sort by edge, AFTER variable, as we want multiple edges for same var + // adjacent. note the SenTree optimizer requires this order (more general first, + // less general last) + return lhsp->edgeType() < rhsp->edgeType(); } }; @@ -2817,9 +2838,8 @@ private: } } - // Sort the sensitivity names so "posedge a or b" and "posedge b or a" end up together. - // Also, remove duplicate assignments, and fold POS&NEGs into ANYEDGEs - // Make things a little faster; check first if we need a sort + // Pass 1: Sort the sensitivity items so "posedge a or b" and "posedge b or a" and + // similar, optimizable expressions end up next to each other. for (AstSenItem *nextp, *senp = nodep->sensesp(); senp; senp = nextp) { nextp = VN_AS(senp->nextp(), SenItem); // cppcheck-suppress unassignedVariable // cppcheck bug @@ -2839,35 +2859,53 @@ private: } } - // Pass2, remove dup edges - for (AstSenItem *nextp, *senp = nodep->sensesp(); senp; senp = nextp) { + // Pass 2, remove duplicates and simplify adjacent terms if possible + for (AstSenItem *senp = nodep->sensesp(), *nextp; senp; senp = nextp) { nextp = VN_AS(senp->nextp(), SenItem); - AstSenItem* const litemp = senp; - AstSenItem* const ritemp = nextp; - if (ritemp) { - if ((litemp->sensp() && ritemp->sensp() - && litemp->sensp()->sameGateTree(ritemp->sensp())) - || (!litemp->sensp() && !ritemp->sensp())) { - // We've sorted in the order ANY, BOTH, POS, NEG, - // so we don't need to try opposite orders - if ((litemp->edgeType() == VEdgeType::ET_POSEDGE // POS or NEG -> BOTH - && ritemp->edgeType() == VEdgeType::ET_NEGEDGE) - || (litemp->edgeType() == ritemp->edgeType()) // Identical edges - ) { - // Fix edge of old node - if (litemp->edgeType() == VEdgeType::ET_POSEDGE - && ritemp->edgeType() == VEdgeType::ET_NEGEDGE) - litemp->edgeType(VEdgeType::ET_BOTHEDGE); - // Remove redundant node - VL_DO_DANGLING(ritemp->unlinkFrBack()->deleteTree(), ritemp); - VL_DANGLING(ritemp); - // Try to collapse again - nextp = litemp; + if (!nextp) break; + AstSenItem* const lItemp = senp; + AstSenItem* const rItemp = nextp; + AstNodeExpr* const lSenp = lItemp->sensp(); + AstNodeExpr* const rSenp = rItemp->sensp(); + if (!lSenp || !rSenp) continue; + + if (lSenp->sameGateTree(rSenp)) { + // POSEDGE or NEGEDGE -> BOTHEDGE. (We've sorted POSEDGE, before NEGEDGE, so we + // do not need to test for the opposite orders.) + if (lItemp->edgeType() == VEdgeType::ET_POSEDGE + && rItemp->edgeType() == VEdgeType::ET_NEGEDGE) { + // Make both terms BOTHEDGE, the second will be removed below + lItemp->edgeType(VEdgeType::ET_BOTHEDGE); + rItemp->edgeType(VEdgeType::ET_BOTHEDGE); + } + + // Remove identical expressions + if (lItemp->edgeType() == rItemp->edgeType()) { + VL_DO_DANGLING(rItemp->unlinkFrBack()->deleteTree(), rItemp); + nextp = lItemp; + } + + continue; + } + + // Not identical terms, check if they can be combined + if (lSenp->width() != rSenp->width()) continue; + if (AstAnd* const lAndp = VN_CAST(lSenp, And)) { + if (AstAnd* const rAndp = VN_CAST(rSenp, And)) { + if (AstConst* const lConstp = VN_CAST(lAndp->lhsp(), Const)) { + if (AstConst* const rConstp = VN_CAST(rAndp->lhsp(), Const)) { + if (lAndp->rhsp()->sameTree(rAndp->rhsp())) { + const V3Number lNum{lConstp->num()}; + lConstp->num().opOr(lNum, rConstp->num()); + // Remove redundant term + VL_DO_DANGLING(rItemp->unlinkFrBack()->deleteTree(), rItemp); + nextp = lItemp; + } + } } } } } - // nodep->dumpTree("- ssou: "); } } @@ -2975,6 +3013,7 @@ private: AstNode* const thensp = nodep->thensp()->unlinkFrBackWithNext(); AstNode* const elsesp = nodep->elsesp()->unlinkFrBackWithNext(); AstIf* const ifp = new AstIf{nodep->fileline(), condp, elsesp, thensp}; + ifp->isBoundsCheck(nodep->isBoundsCheck()); // Copy bounds check info ifp->branchPred(nodep->branchPred().invert()); nodep->replaceWith(ifp); VL_DO_DANGLING(nodep->deleteTree(), nodep); @@ -3188,7 +3227,7 @@ private: void visit(AstStmtExpr* nodep) override { iterateChildren(nodep); - if (!nodep->exprp()) { + if (!nodep->exprp() || VN_IS(nodep->exprp(), Const)) { VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); return; } @@ -3207,7 +3246,7 @@ private: void visit(AstJumpGo* nodep) override { iterateChildren(nodep); - // Jump to label where label immediately follows label is not useful + // Jump to label where label immediately follows this go is not useful if (nodep->labelp() == VN_CAST(nodep->nextp(), JumpLabel)) { VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); // Keep the label, might be other jumps pointing to it, gets cleaned later diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index 2c30c392e..1a491422d 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -262,8 +262,8 @@ private: if (VN_IS(dimp, Const)) { // bit = const, can just use it dimreadps.push_front(dimp); } else { - const string bitvarname = (string("__Vdlyvdim") + cvtToStr(dimension) + "__" - + oldvarp->shortName() + "__v" + cvtToStr(modVecNum)); + const string bitvarname = string{"__Vdlyvdim"} + cvtToStr(dimension) + "__" + + oldvarp->shortName() + "__v" + cvtToStr(modVecNum); AstVarScope* const bitvscp = createVarSc(varrefp->varScopep(), bitvarname, dimp->width(), nullptr); AstAssign* const bitassignp = new AstAssign{ @@ -282,8 +282,8 @@ private: // vlsb = constant, can just push constant into where we use it bitreadp = lsbvaluep; } else { - const string bitvarname = (string("__Vdlyvlsb__") + oldvarp->shortName() + "__v" - + cvtToStr(modVecNum)); + const string bitvarname + = string{"__Vdlyvlsb__"} + oldvarp->shortName() + "__v" + cvtToStr(modVecNum); AstVarScope* const bitvscp = createVarSc(varrefp->varScopep(), bitvarname, lsbvaluep->width(), nullptr); AstAssign* const bitassignp = new AstAssign{ @@ -301,7 +301,7 @@ private: valreadp = nodep->rhsp()->unlinkFrBack(); } else { const string valvarname - = (string("__Vdlyvval__") + oldvarp->shortName() + "__v" + cvtToStr(modVecNum)); + = string{"__Vdlyvval__"} + oldvarp->shortName() + "__v" + cvtToStr(modVecNum); AstVarScope* const valvscp = createVarSc(varrefp->varScopep(), valvarname, 0, nodep->rhsp()->dtypep()); newlhsp = new AstVarRef{nodep->fileline(), valvscp, VAccess::WRITE}; @@ -321,7 +321,7 @@ private: ++m_statSharedSet; } else { // Create new one const string setvarname - = (string("__Vdlyvset__") + oldvarp->shortName() + "__v" + cvtToStr(modVecNum)); + = string{"__Vdlyvset__"} + oldvarp->shortName() + "__v" + cvtToStr(modVecNum); setvscp = createVarSc(varrefp->varScopep(), setvarname, 1, nullptr); if (!m_inSuspendableOrFork) { // Suspendables reset __Vdlyvset__ in the AstAlwaysPost @@ -474,14 +474,16 @@ private: m_inSuspendableOrFork = true; iterateChildren(nodep); } - void visit(AstCAwait* nodep) override { m_timingDomains.insert(nodep->sensesp()); } + void visit(AstCAwait* nodep) override { + if (nodep->sensesp()) m_timingDomains.insert(nodep->sensesp()); + } void visit(AstFireEvent* nodep) override { UASSERT_OBJ(v3Global.hasEvents(), nodep, "Inconsistent"); FileLine* const flp = nodep->fileline(); if (nodep->isDelayed()) { AstVarRef* const vrefp = VN_AS(nodep->operandp(), VarRef); vrefp->unlinkFrBack(); - const string newvarname = (string("__Vdly__") + vrefp->varp()->shortName()); + const string newvarname = string{"__Vdly__"} + vrefp->varp()->shortName(); AstVarScope* const dlyvscp = createVarSc(vrefp->varScopep(), newvarname, 1, nullptr); const auto dlyRef = [=](VAccess access) { @@ -570,7 +572,7 @@ private: checkActivePost(nodep, oldactivep); } if (!dlyvscp) { // First use of this delayed variable - const string newvarname = (string("__Vdly__") + nodep->varp()->shortName()); + const string newvarname = string{"__Vdly__"} + nodep->varp()->shortName(); dlyvscp = createVarSc(oldvscp, newvarname, 0, nullptr); AstNodeAssign* const prep = new AstAssignPre{ nodep->fileline(), diff --git a/src/V3DepthBlock.cpp b/src/V3DepthBlock.cpp index f4e4ec5eb..889601de2 100644 --- a/src/V3DepthBlock.cpp +++ b/src/V3DepthBlock.cpp @@ -63,7 +63,7 @@ private: AstCCall* const callp = new AstCCall{nodep->fileline(), funcp}; callp->dtypeSetVoid(); if (VN_IS(m_modp, Class)) { - funcp->argTypes(EmitCBaseVisitor::symClassVar()); + funcp->argTypes(EmitCBase::symClassVar()); callp->argTypes("vlSymsp"); } UINFO(6, " New " << callp << endl); @@ -93,6 +93,7 @@ private: } } void visit(AstStmtExpr* nodep) override {} // Stop recursion after introducing new function + void visit(AstJumpBlock*) override {} // Stop recursion as can't break up across a jump void visit(AstNodeStmt* nodep) override { m_depth++; if (m_depth > v3Global.opt.compLimitBlocks()) { // Already done diff --git a/src/V3Descope.cpp b/src/V3Descope.cpp index fded54459..3c1eeb518 100644 --- a/src/V3Descope.cpp +++ b/src/V3Descope.cpp @@ -166,7 +166,7 @@ private: new AstEq{ funcp->fileline(), new AstCExpr{funcp->fileline(), "this", 64}, new AstCExpr{funcp->fileline(), - string("&(") + funcp->scopep()->nameVlSym() + ")", + string{"&("} + funcp->scopep()->nameVlSym() + ")", 64}}, returnp}; newfuncp->addStmtsp(ifp); @@ -178,7 +178,7 @@ private: // to come up with some return value // newfuncp->addStmtsp(new AstDisplay(newfuncp->fileline(), // VDisplayType::DT_WARNING, - // string("%%Error: ")+name+"() called with bad + // string{"%%Error: "}+name+"() called with bad // scope", nullptr)); // newfuncp->addStmtsp(new AstStop(newfuncp->fileline())); if (debug() >= 9) newfuncp->dumpTree("- newfunc: "); diff --git a/src/V3DfgDfgToAst.cpp b/src/V3DfgDfgToAst.cpp index 3932162be..6667ee35f 100644 --- a/src/V3DfgDfgToAst.cpp +++ b/src/V3DfgDfgToAst.cpp @@ -59,8 +59,7 @@ AstCountOnes* makeNode( // const DfgCountOnes* vtxp, AstNodeExpr* op1) { AstCountOnes* const nodep = new AstCountOnes{vtxp->fileline(), op1}; // Set dtype same as V3Width - const int selwidth = V3Number::log2b(nodep->lhsp()->width()) + 1; - nodep->dtypeSetLogicSized(selwidth, VSigning::UNSIGNED); + nodep->dtypeSetLogicSized(32, VSigning::UNSIGNED); return nodep; } diff --git a/src/V3EmitCBase.cpp b/src/V3EmitCBase.cpp index 884950da2..1bec37b9c 100644 --- a/src/V3EmitCBase.cpp +++ b/src/V3EmitCBase.cpp @@ -39,7 +39,7 @@ EmitCParentModule::EmitCParentModule() { //###################################################################### // EmitCBaseVisitor implementation -string EmitCBaseVisitor::funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp) { +string EmitCBaseVisitorConst::funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp) { modp = modp ? modp : EmitCParentModule::get(nodep); string name; if (nodep->isConstructor()) { @@ -57,15 +57,21 @@ string EmitCBaseVisitor::funcNameProtect(const AstCFunc* nodep, const AstNodeMod return name; } -AstCFile* EmitCBaseVisitor::newCFile(const string& filename, bool slow, bool source, bool add) { - AstCFile* const cfilep = new AstCFile{v3Global.rootp()->fileline(), filename}; - cfilep->slow(slow); - cfilep->source(source); - if (add) v3Global.rootp()->addFilesp(cfilep); +AstCFile* EmitCBaseVisitorConst::newCFile(const string& filename, bool slow, bool source) { + AstCFile* const cfilep = createCFile(filename, slow, source); + v3Global.rootp()->addFilesp(cfilep); return cfilep; } -string EmitCBaseVisitor::cFuncArgs(const AstCFunc* nodep) { +AstCFile* EmitCBaseVisitorConst::createCFile(const string& filename, bool slow, + bool source) VL_MT_SAFE { + AstCFile* const cfilep = new AstCFile{v3Global.rootp()->fileline(), filename}; + cfilep->slow(slow); + cfilep->source(source); + return cfilep; +} + +string EmitCBaseVisitorConst::cFuncArgs(const AstCFunc* nodep) { // Return argument list for given C function string args; if (nodep->isLoose() && !nodep->isStatic()) { @@ -95,8 +101,8 @@ string EmitCBaseVisitor::cFuncArgs(const AstCFunc* nodep) { return args; } -void EmitCBaseVisitor::emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, - bool withScope) { +void EmitCBaseVisitorConst::emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, + bool withScope) { if (funcp->slow()) puts("VL_ATTR_COLD "); if (!funcp->isConstructor() && !funcp->isDestructor()) { puts(funcp->rtnTypeVoid()); @@ -114,8 +120,8 @@ void EmitCBaseVisitor::emitCFuncHeader(const AstCFunc* funcp, const AstNodeModul if (funcp->isConst().trueKnown() && funcp->isProperMethod()) puts(" const"); } -void EmitCBaseVisitor::emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, - bool cLinkage) { +void EmitCBaseVisitorConst::emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, + bool cLinkage) { ensureNewLine(); if (!funcp->ifdef().empty()) puts("#ifdef " + funcp->ifdef() + "\n"); if (cLinkage) puts("extern \"C\" "); @@ -129,7 +135,7 @@ void EmitCBaseVisitor::emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* if (!funcp->ifdef().empty()) puts("#endif // " + funcp->ifdef() + "\n"); } -void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, bool asRef) { +void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) { const AstBasicDType* const basicp = nodep->basicp(); bool refNeedParens = VN_IS(nodep->dtypeSkipRefp(), UnpackArrayDType); @@ -217,7 +223,7 @@ void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, bool asRef) { } } -void EmitCBaseVisitor::emitModCUse(const AstNodeModule* modp, VUseType useType) { +void EmitCBaseVisitorConst::emitModCUse(const AstNodeModule* modp, VUseType useType) { string nl; for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) { if (AstCUse* const usep = VN_CAST(itemp, CUse)) { @@ -235,7 +241,7 @@ void EmitCBaseVisitor::emitModCUse(const AstNodeModule* modp, VUseType useType) puts(nl); } -void EmitCBaseVisitor::emitTextSection(const AstNodeModule* modp, VNType type) { +void EmitCBaseVisitorConst::emitTextSection(const AstNodeModule* modp, VNType type) { // Short circuit if nothing to do. This can save a lot of time on large designs as this // function needs to traverse the entire module linearly. if (!v3Global.hasSCTextSections()) return; diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index 39ff0e70e..5ae0074c4 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -40,7 +40,7 @@ public: EmitCParentModule(); VL_UNCOPYABLE(EmitCParentModule); - static const AstNodeModule* get(const AstNode* nodep) { + static const AstNodeModule* get(const AstNode* nodep) VL_MT_STABLE { return VN_AS(nodep->user4p(), NodeModule); } }; @@ -48,51 +48,23 @@ public: //###################################################################### // Base Visitor class -- holds output file pointer -class EmitCBaseVisitor VL_NOT_FINAL : public VNVisitor { +class EmitCBase VL_NOT_FINAL { public: - // STATE - V3OutCFile* m_ofp = nullptr; - bool m_trackText = false; // Always track AstText nodes - // METHODS - V3OutCFile* ofp() const VL_MT_SAFE { return m_ofp; } - void puts(const string& str) { ofp()->puts(str); } - void putbs(const string& str) { ofp()->putbs(str); } - void putsDecoration(const string& str) { - if (v3Global.opt.decoration()) puts(str); - } - void putsQuoted(const string& str) { ofp()->putsQuoted(str); } - void ensureNewLine() { ofp()->ensureNewLine(); } - bool optSystemC() { return v3Global.opt.systemC(); } - static string protect(const string& name) { return VIdProtect::protectIf(name, true); } - static string protectIf(const string& name, bool doIt) { - return VIdProtect::protectIf(name, doIt); - } - static string protectWordsIf(const string& name, bool doIt) { - return VIdProtect::protectWordsIf(name, doIt); - } - static string ifNoProtect(const string& in) { return v3Global.opt.protectIds() ? "" : in; } static string voidSelfAssign(const AstNodeModule* modp) { const string className = prefixNameProtect(modp); return className + "* const __restrict vlSelf VL_ATTR_UNUSED = static_cast<" + className + "*>(voidSelf);\n"; } - static string symClassName() { return v3Global.opt.prefix() + "_" + protect("_Syms"); } + static string symClassName() VL_MT_STABLE { + return v3Global.opt.prefix() + "_" + VIdProtect::protect("_Syms"); + } static string symClassVar() { return symClassName() + "* __restrict vlSymsp"; } static string symClassAssign() { return symClassName() + "* const __restrict vlSymsp VL_ATTR_UNUSED = vlSelf->vlSymsp;\n"; } - static string funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp = nullptr); - static string prefixNameProtect(const AstNode* nodep) { // C++ name with prefix - return v3Global.opt.modPrefix() + "_" + protect(nodep->name()); + static string prefixNameProtect(const AstNode* nodep) VL_MT_STABLE { // C++ name with prefix + return v3Global.opt.modPrefix() + "_" + VIdProtect::protect(nodep->name()); } - static string topClassName() { // Return name of top wrapper module - return v3Global.opt.prefix(); - } - - static bool isConstPoolMod(const AstNode* modp) { - return modp == v3Global.rootp()->constPoolp()->modp(); - } - static bool isAnonOk(const AstVar* varp) { return v3Global.opt.compLimitMembers() != 0 // Enabled && !varp->isStatic() // Not a static variable @@ -100,8 +72,43 @@ public: && !VN_IS(varp->dtypep()->skipRefp(), SampleQueueDType) // Aggregates can't be anon && (varp->basicp() && !varp->basicp()->isOpaque()); // Aggregates can't be anon } + static bool isConstPoolMod(const AstNode* modp) { + return modp == v3Global.rootp()->constPoolp()->modp(); + } +}; - static AstCFile* newCFile(const string& filename, bool slow, bool source, bool add = true); +class EmitCBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst, public EmitCBase { +public: + // STATE + V3OutCFile* m_ofp = nullptr; + bool m_trackText = false; // Always track AstText nodes + // METHODS + V3OutCFile* ofp() const VL_MT_SAFE { return m_ofp; } + void puts(const string& str) { ofp()->puts(str); } + void putsHeader() { ofp()->putsHeader(); } + void putbs(const string& str) { ofp()->putbs(str); } + void putsDecoration(const string& str) { + if (v3Global.opt.decoration()) puts(str); + } + void putsQuoted(const string& str) { ofp()->putsQuoted(str); } + void ensureNewLine() { ofp()->ensureNewLine(); } + bool optSystemC() { return v3Global.opt.systemC(); } + static string protect(const string& name) VL_MT_SAFE { return VIdProtect::protect(name); } + static string protectIf(const string& name, bool doIt) { + return VIdProtect::protectIf(name, doIt); + } + static string protectWordsIf(const string& name, bool doIt) { + return VIdProtect::protectWordsIf(name, doIt); + } + static string ifNoProtect(const string& in) VL_MT_SAFE { + return v3Global.opt.protectIds() ? "" : in; + } + static string funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp = nullptr); + static string topClassName() VL_MT_SAFE { // Return name of top wrapper module + return v3Global.opt.prefix(); + } + static AstCFile* newCFile(const string& filename, bool slow, bool source); + static AstCFile* createCFile(const string& filename, bool slow, bool source) VL_MT_SAFE; string cFuncArgs(const AstCFunc* nodep); void emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, bool withScope); void emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, bool cLinkage = false); @@ -110,8 +117,8 @@ public: void emitTextSection(const AstNodeModule* modp, VNType type); // CONSTRUCTORS - EmitCBaseVisitor() = default; - ~EmitCBaseVisitor() override = default; + EmitCBaseVisitorConst() = default; + ~EmitCBaseVisitorConst() override = default; }; #endif // guard diff --git a/src/V3EmitCConstInit.h b/src/V3EmitCConstInit.h index 9113348e5..22f8082a1 100644 --- a/src/V3EmitCConstInit.h +++ b/src/V3EmitCConstInit.h @@ -26,7 +26,7 @@ //###################################################################### // Emitter that can emit constant initializer expressions -class EmitCConstInit VL_NOT_FINAL : public EmitCBaseVisitor { +class EmitCConstInit VL_NOT_FINAL : public EmitCBaseVisitorConst { // MEMBERS uint32_t m_unpackedWord = 0; bool m_inUnpacked = false; @@ -64,7 +64,7 @@ protected: ofp()->printf("%" PRIx64 "ULL", itr.first); ofp()->putsNoTracking(":"); ofp()->putsNoTracking("{"); - iterate(nodep->getIndexValuep(itr.first)); + iterateConst(nodep->getIndexValuep(itr.first)); ofp()->putsNoTracking("}"); } puts("\n"); @@ -83,7 +83,7 @@ protected: for (uint64_t n = 0; n < size; ++n) { m_unpackedWord = n; if (n) puts((n % tabMod) ? ", " : ",\n"); - iterate(nodep->getIndexDefaultedValuep(n)); + iterateConst(nodep->getIndexDefaultedValuep(n)); } puts("\n"); puts("}"); diff --git a/src/V3EmitCConstPool.cpp b/src/V3EmitCConstPool.cpp index 6ba5c33bc..27503551c 100644 --- a/src/V3EmitCConstPool.cpp +++ b/src/V3EmitCConstPool.cpp @@ -87,7 +87,7 @@ class EmitCConstPool final : public EmitCConstInit { puts("extern const "); puts(varp->dtypep()->cType(nameProtect, false, false)); puts(" = "); - iterate(varp->valuep()); + iterateConst(varp->valuep()); puts(";\n"); // Keep track of stats if (VN_IS(varp->dtypep(), UnpackArrayDType)) { diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index 7f60b5371..9160e5068 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -138,7 +138,7 @@ void EmitCFunc::emitOpName(AstNode* nodep, const string& format, AstNode* lhsp, case 'i': COMMA; UASSERT_OBJ(detailp, nodep, "emitOperator() references undef node"); - iterateAndNextNull(detailp); + iterateAndNextConstNull(detailp); needComma = true; break; default: @@ -174,7 +174,7 @@ void EmitCFunc::displayEmit(AstNode* nodep, bool isScan) { if (const AstFScanF* const dispp = VN_CAST(nodep, FScanF)) { isStmt = false; puts("VL_FSCANF_IX("); - iterate(dispp->filep()); + iterateConst(dispp->filep()); puts(","); } else if (const AstSScanF* const dispp = VN_CAST(nodep, SScanF)) { isStmt = false; @@ -184,13 +184,13 @@ void EmitCFunc::displayEmit(AstNode* nodep, bool isScan) { puts("X("); puts(cvtToStr(dispp->fromp()->widthMin())); puts(","); - iterate(dispp->fromp()); + iterateConst(dispp->fromp()); puts(","); } else if (const AstDisplay* const dispp = VN_CAST(nodep, Display)) { isStmt = true; if (dispp->filep()) { puts("VL_FWRITEF("); - iterate(dispp->filep()); + iterateConst(dispp->filep()); puts(","); } else { puts("VL_WRITEF("); @@ -200,7 +200,7 @@ void EmitCFunc::displayEmit(AstNode* nodep, bool isScan) { puts("VL_SFORMAT_X("); puts(cvtToStr(dispp->lhsp()->widthMin())); putbs(","); - iterate(dispp->lhsp()); + iterateConst(dispp->lhsp()); putbs(","); } else if (VN_IS(nodep, SFormatF)) { isStmt = false; @@ -223,7 +223,7 @@ void EmitCFunc::displayEmit(AstNode* nodep, bool isScan) { } else if (argp) { const bool addrof = isScan || (fmt == '@'); if (addrof) puts("&("); - iterate(argp); + iterateConst(argp); if (!addrof) emitDatap(argp); if (addrof) puts(")"); } @@ -275,9 +275,9 @@ void EmitCFunc::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const double dchars = mantissabits / 3.321928094887362 + 1.0; if (fmtLetter == 'd') dchars++; // space for sign const int nchars = int(dchars); - pfmt = string("%") + cvtToStr(nchars) + fmtLetter; + pfmt = string{"%"} + cvtToStr(nchars) + fmtLetter; } else { - pfmt = string("%") + vfmt + fmtLetter; + pfmt = string{"%"} + vfmt + fmtLetter; } m_emitDispState.pushFormat(pfmt); if (!ignore) { @@ -452,16 +452,16 @@ void EmitCFunc::emitCvtPackStr(AstNode* nodep) { puts(cvtToStr(nodep->widthWords())); puts(", "); } - iterateAndNextNull(nodep); + iterateAndNextConstNull(nodep); puts(")"); } } void EmitCFunc::emitCvtWideArray(AstNode* nodep, AstNode* fromp) { putbs("VL_CVT_W_A("); - iterate(nodep); + iterateConst(nodep); puts(", "); - iterate(fromp); + iterateConst(fromp); putbs(".atDefault()"); // Not accessed; only to get the proper type of values puts(")"); } @@ -672,7 +672,7 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP } else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) { UASSERT_OBJ(adtypep->hi() >= adtypep->lo(), varp, "Should have swapped msb & lsb earlier."); - const string ivar = string("__Vi") + cvtToStr(depth); + const string ivar = string{"__Vi"} + cvtToStr(depth); const string pre = ("for (int " + ivar + " = " + cvtToStr(0) + "; " + ivar + " < " + cvtToStr(adtypep->elementsConst()) + "; ++" + ivar + ") {\n"); const string below = emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index c91112ef0..1149c728f 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -34,13 +34,13 @@ constexpr int EMITC_NUM_CONSTW = 8; //###################################################################### // Emit lazy forward declarations -class EmitCLazyDecls final : public VNVisitor { +class EmitCLazyDecls final : public VNVisitorConst { // NODE STATE/TYPES // None allowed to support threaded emitting // MEMBERS std::unordered_set m_emittedManually; // Set of names already declared manually. - EmitCBaseVisitor& m_emitter; // For access to file output + EmitCBaseVisitorConst& m_emitter; // For access to file output bool m_needsBlankLine = false; // Emit blank line if any declarations were emitted (cosmetic) std::set m_emitted; // -> in set. Already emitted decl for symbols. @@ -72,18 +72,18 @@ class EmitCLazyDecls final : public VNVisitor { // VISITORS void visit(AstNodeCCall* nodep) override { lazyDeclare(nodep->funcp()); - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstAddrOfCFunc* nodep) override { lazyDeclare(nodep->funcp()); - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstVarRef* nodep) override { AstVar* const varp = nodep->varp(); // Only constant pool symbols are lazy declared for now ... - if (EmitCBaseVisitor::isConstPoolMod(EmitCParentModule::get(varp))) { + if (EmitCBase::isConstPoolMod(EmitCParentModule::get(varp))) { lazyDeclareConstPoolVar(varp); } } @@ -91,7 +91,7 @@ class EmitCLazyDecls final : public VNVisitor { void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } public: - explicit EmitCLazyDecls(EmitCBaseVisitor& emitter) + explicit EmitCLazyDecls(EmitCBaseVisitorConst& emitter) : m_emitter(emitter) {} void emit(AstNode* nodep) { m_needsBlankLine = false; @@ -203,12 +203,12 @@ public: // variables. That way we could just invoke the appropriate emitter as needed. VL_RESTORER(m_emitConstInit); m_emitConstInit = true; - iterate(initp); + iterateConst(initp); } void putCommaIterateNext(AstNode* nodep, bool comma = false) { for (AstNode* subnodep = nodep; subnodep; subnodep = subnodep->nextp()) { if (comma) puts(", "); - iterate(subnodep); + iterateConst(subnodep); comma = true; } } @@ -273,17 +273,17 @@ public: if (nodep->initsp()) { putsDecoration("// Init\n"); - iterateAndNextNull(nodep->initsp()); + iterateAndNextConstNull(nodep->initsp()); } if (nodep->stmtsp()) { putsDecoration("// Body\n"); - iterateAndNextNull(nodep->stmtsp()); + iterateAndNextConstNull(nodep->stmtsp()); } if (nodep->finalsp()) { putsDecoration("// Final\n"); - iterateAndNextNull(nodep->finalsp()); + iterateAndNextConstNull(nodep->finalsp()); } puts("}\n"); @@ -309,9 +309,9 @@ public: } else { puts("I("); } - iterateAndNextNull(selp->lsbp()); + iterateAndNextConstNull(selp->lsbp()); puts(", "); - iterateAndNextNull(selp->fromp()); + iterateAndNextConstNull(selp->fromp()); if (rhs) puts(", "); } else { putbs("VL_ASSIGNSEL_"); @@ -320,18 +320,18 @@ public: puts("("); puts(cvtToStr(selp->fromp()->widthMin()) + ","); puts(cvtToStr(nodep->widthMin()) + ","); - iterateAndNextNull(selp->lsbp()); + iterateAndNextConstNull(selp->lsbp()); puts(", "); - iterateAndNextNull(selp->fromp()); + iterateAndNextConstNull(selp->fromp()); puts(", "); } } else if (const AstGetcRefN* const selp = VN_CAST(nodep->lhsp(), GetcRefN)) { - iterateAndNextNull(selp->lhsp()); + iterateAndNextConstNull(selp->lhsp()); puts(" = "); putbs("VL_PUTC_N("); - iterateAndNextNull(selp->lhsp()); + iterateAndNextConstNull(selp->lhsp()); puts(", "); - iterateAndNextNull(selp->rhsp()); + iterateAndNextConstNull(selp->rhsp()); puts(", "); } else if (AstVar* const varp = AstVar::scVarRecurse(nodep->lhsp())) { putbs("VL_ASSIGN_"); // Set a systemC variable @@ -339,7 +339,7 @@ public: emitIQW(nodep); puts("("); puts(cvtToStr(nodep->widthMin()) + ","); - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); puts(", "); } else if (AstVar* const varp = AstVar::scVarRecurse(nodep->rhsp())) { putbs("VL_ASSIGN_"); // Get a systemC variable @@ -347,7 +347,7 @@ public: emitScIQW(varp); puts("("); puts(cvtToStr(nodep->widthMin()) + ","); - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); puts(", "); } else if (nodep->isWide() && VN_IS(nodep->lhsp(), VarRef) // && !VN_IS(nodep->rhsp(), CExpr) // @@ -361,41 +361,41 @@ public: } else if (nodep->isWide() && !VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) { putbs("VL_ASSIGN_W("); puts(cvtToStr(nodep->widthMin()) + ","); - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); puts(", "); } else { paren = false; - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); puts(" "); ofp()->blockInc(); decind = true; if (!VN_IS(nodep->rhsp(), Const)) ofp()->putBreak(); puts("= "); } - if (rhs) iterateAndNextNull(nodep->rhsp()); + if (rhs) iterateAndNextConstNull(nodep->rhsp()); if (paren) puts(")"); if (decind) ofp()->blockDec(); puts(";\n"); } void visit(AstAlwaysPublic*) override {} void visit(AstAssocSel* nodep) override { - iterateAndNextNull(nodep->fromp()); + iterateAndNextConstNull(nodep->fromp()); putbs(".at("); AstAssocArrayDType* const adtypep = VN_AS(nodep->fromp()->dtypep(), AssocArrayDType); UASSERT_OBJ(adtypep, nodep, "Associative select on non-associative type"); if (adtypep->keyDTypep()->isWide()) { emitCvtWideArray(nodep->bitp(), nodep->fromp()); } else { - iterateAndNextNull(nodep->bitp()); + iterateAndNextConstNull(nodep->bitp()); } puts(")"); } void visit(AstWildcardSel* nodep) override { - iterateAndNextNull(nodep->fromp()); + iterateAndNextConstNull(nodep->fromp()); putbs(".at("); AstWildcardArrayDType* const adtypep = VN_AS(nodep->fromp()->dtypep(), WildcardArrayDType); UASSERT_OBJ(adtypep, nodep, "Wildcard select on non-wildcard-associative type"); - iterateAndNextNull(nodep->bitp()); + iterateAndNextConstNull(nodep->bitp()); puts(")"); } void visit(AstCCall* nodep) override { @@ -427,14 +427,14 @@ public: void visit(AstCMethodCall* nodep) override { const AstCFunc* const funcp = nodep->funcp(); UASSERT_OBJ(!funcp->isLoose(), nodep, "Loose method called via AstCMethodCall"); - iterate(nodep->fromp()); + iterateConst(nodep->fromp()); putbs("->"); puts(funcp->nameProtect()); emitCCallArgs(nodep, ""); } void visit(AstCAwait* nodep) override { puts("co_await "); - iterate(nodep->exprp()); + iterateConst(nodep->exprp()); } void visit(AstCNew* nodep) override { if (VN_IS(nodep->dtypep(), VoidDType)) { @@ -447,7 +447,7 @@ public: puts(")"); } void visit(AstCMethodHard* nodep) override { - iterate(nodep->fromp()); + iterateConst(nodep->fromp()); puts("."); puts(nodep->name()); puts("("); @@ -458,7 +458,7 @@ public: if (VN_IS(nodep->fromp()->dtypep(), QueueDType) && subnodep->dtypep()->isWide()) { emitCvtWideArray(subnodep, nodep->fromp()); } else { - iterate(subnodep); + iterateConst(subnodep); } comma = true; } @@ -477,7 +477,7 @@ public: } // Probably fragile, V3Task may need to convert to a AstCReturn puts(") { return "); - iterateAndNextNull(nodep->exprp()); + iterateAndNextConstNull(nodep->exprp()); puts("; }\n"); } void visit(AstNodeCase* nodep) override { // LCOV_EXCL_LINE @@ -492,9 +492,9 @@ public: if (!v3Global.opt.protectIds()) return; } if (!(nodep->protect() && v3Global.opt.protectIds())) { - putsDecoration(string("// ") + nodep->name() + at + "\n"); + putsDecoration(string{"// "} + nodep->name() + at + "\n"); } - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstCoverDecl* nodep) override { puts("vlSelf->__vlCoverInsert("); // As Declared in emitCoverageDecl @@ -537,7 +537,7 @@ public: } void visit(AstCReturn* nodep) override { puts("return ("); - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); puts(");\n"); } void visit(AstDisplay* nodep) override { @@ -615,7 +615,7 @@ public: emitCvtPackStr(nodep->searchp()); puts(", "); putbs(""); - iterateAndNextNull(nodep->outp()); + iterateAndNextConstNull(nodep->outp()); puts(")"); } void visit(AstTestPlusArgs* nodep) override { @@ -626,13 +626,13 @@ public: void visit(AstFError* nodep) override { puts("VL_FERROR_I"); puts(nodep->strp()->isString() ? "N(" : "W("); - iterateAndNextNull(nodep->filep()); + iterateAndNextConstNull(nodep->filep()); putbs(", "); if (nodep->strp()->isWide()) { puts(cvtToStr(nodep->strp()->widthWords())); putbs(", "); } - iterateAndNextNull(nodep->strp()); + iterateAndNextConstNull(nodep->strp()); puts(")"); } void visit(AstFGetS* nodep) override { @@ -649,8 +649,7 @@ public: } } void visit(AstFOpen* nodep) override { - iterateAndNextNull(nodep->filep()); - puts(" = VL_FOPEN_NN("); + puts("VL_FOPEN_NN("); emitCvtPackStr(nodep->filenamep()); putbs(", "); if (nodep->modep()->width() > 4 * 8) @@ -659,8 +658,7 @@ public: puts(");\n"); } void visit(AstFOpenMcd* nodep) override { - iterateAndNextNull(nodep->filep()); - puts(" = VL_FOPEN_MCD_N("); + puts("VL_FOPEN_MCD_N("); emitCvtPackStr(nodep->filenamep()); puts(");\n"); } @@ -696,18 +694,18 @@ public: { const bool need_ptr = !VN_IS(nodep->memp()->dtypep(), AssocArrayDType); if (need_ptr) puts(" &("); - iterateAndNextNull(nodep->memp()); + iterateAndNextConstNull(nodep->memp()); if (need_ptr) puts(")"); } putbs(", "); if (nodep->lsbp()) { - iterateAndNextNull(nodep->lsbp()); + iterateAndNextConstNull(nodep->lsbp()); } else { puts(cvtToStr(array_lo)); } putbs(", "); if (nodep->msbp()) { - iterateAndNextNull(nodep->msbp()); + iterateAndNextConstNull(nodep->msbp()); } else { puts("~0ULL"); } @@ -715,39 +713,37 @@ public: } void visit(AstFClose* nodep) override { puts("VL_FCLOSE_I("); - iterateAndNextNull(nodep->filep()); + iterateAndNextConstNull(nodep->filep()); puts("); "); - iterateAndNextNull(nodep->filep()); // For safety, so user doesn't later WRITE with it. - puts(" = 0;\n"); } void visit(AstFFlush* nodep) override { if (!nodep->filep()) { puts("Verilated::runFlushCallbacks();\n"); } else { puts("if ("); - iterateAndNextNull(nodep->filep()); + iterateAndNextConstNull(nodep->filep()); puts(") { VL_FFLUSH_I("); - iterateAndNextNull(nodep->filep()); + iterateAndNextConstNull(nodep->filep()); puts("); }\n"); } } void visit(AstFSeek* nodep) override { puts("(VL_FSEEK_I("); - iterateAndNextNull(nodep->filep()); + iterateAndNextConstNull(nodep->filep()); puts(","); - iterateAndNextNull(nodep->offset()); + iterateAndNextConstNull(nodep->offset()); puts(","); - iterateAndNextNull(nodep->operation()); + iterateAndNextConstNull(nodep->operation()); puts(") == -1 ? -1 : 0)"); } void visit(AstFTell* nodep) override { puts("VL_FTELL_I("); - iterateAndNextNull(nodep->filep()); + iterateAndNextConstNull(nodep->filep()); puts(")"); } void visit(AstFRewind* nodep) override { puts("(VL_FSEEK_I("); - iterateAndNextNull(nodep->filep()); + iterateAndNextConstNull(nodep->filep()); puts(", 0, 0) == -1 ? -1 : 0)"); } void visit(AstFRead* nodep) override { @@ -775,19 +771,19 @@ public: puts(cvtToStr(array_size)); putbs(", "); puts("&("); - iterateAndNextNull(nodep->memp()); + iterateAndNextConstNull(nodep->memp()); puts(")"); putbs(", "); - iterateAndNextNull(nodep->filep()); + iterateAndNextConstNull(nodep->filep()); putbs(", "); if (nodep->startp()) { - iterateAndNextNull(nodep->startp()); + iterateAndNextConstNull(nodep->startp()); } else { puts(cvtToStr(array_lo)); } putbs(", "); if (nodep->countp()) { - iterateAndNextNull(nodep->countp()); + iterateAndNextConstNull(nodep->countp()); } else { puts(cvtToStr(array_size)); } @@ -795,7 +791,7 @@ public: } void visit(AstSysFuncAsTask* nodep) override { if (!nodep->lhsp()->isWide()) puts("(void)"); - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); if (!nodep->lhsp()->isWide()) puts(";"); } void visit(AstStackTraceF* nodep) override { puts("VL_STACKTRACE_N()"); } @@ -809,7 +805,7 @@ public: putbs(", "); } checkMaxWords(nodep->lhsp()); - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); puts(");\n"); } void visit(AstSystemF* nodep) override { @@ -821,18 +817,18 @@ public: putbs(", "); } checkMaxWords(nodep->lhsp()); - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); puts(")"); } void visit(AstStmtExpr* node) override { - iterate(node->exprp()); + iterateConst(node->exprp()); puts(";\n"); } void visit(AstJumpBlock* nodep) override { nodep->labelNum(++m_labelNum); puts("{\n"); // Make it visually obvious label jumps outside these - iterateAndNextNull(nodep->stmtsp()); - iterateAndNextNull(nodep->endStmtsp()); + iterateAndNextConstNull(nodep->stmtsp()); + iterateAndNextConstNull(nodep->endStmtsp()); puts("}\n"); } void visit(AstJumpGo* nodep) override { @@ -842,13 +838,13 @@ public: puts("__Vlabel" + cvtToStr(nodep->blockp()->labelNum()) + ": ;\n"); } void visit(AstWhile* nodep) override { - iterateAndNextNull(nodep->precondsp()); + iterateAndNextConstNull(nodep->precondsp()); puts("while ("); - iterateAndNextNull(nodep->condp()); + iterateAndNextConstNull(nodep->condp()); puts(") {\n"); - iterateAndNextNull(nodep->stmtsp()); - iterateAndNextNull(nodep->incsp()); - iterateAndNextNull(nodep->precondsp()); // Need to recompute before next loop + iterateAndNextConstNull(nodep->stmtsp()); + iterateAndNextConstNull(nodep->incsp()); + iterateAndNextConstNull(nodep->precondsp()); // Need to recompute before next loop puts("}\n"); } void visit(AstNodeIf* nodep) override { @@ -857,20 +853,20 @@ public: puts(nodep->branchPred().ascii()); puts("("); } - iterateAndNextNull(nodep->condp()); + iterateAndNextConstNull(nodep->condp()); if (!nodep->branchPred().unknown()) puts(")"); puts(") {\n"); - iterateAndNextNull(nodep->thensp()); + iterateAndNextConstNull(nodep->thensp()); puts("}"); if (!nodep->elsesp()) { puts("\n"); } else { if (VN_IS(nodep->elsesp(), NodeIf) && !nodep->elsesp()->nextp()) { puts(" else "); - iterateAndNextNull(nodep->elsesp()); + iterateAndNextConstNull(nodep->elsesp()); } else { puts(" else {\n"); - iterateAndNextNull(nodep->elsesp()); + iterateAndNextConstNull(nodep->elsesp()); puts("}\n"); } } @@ -879,9 +875,9 @@ public: // GCC allows compound statements in expressions, but this is not standard. // So we use an immediate-evaluation lambda and comma operator putbs("([&]() {\n"); - iterateAndNextNull(nodep->stmtsp()); + iterateAndNextConstNull(nodep->stmtsp()); puts("}(), "); - iterateAndNextNull(nodep->resultp()); + iterateAndNextConstNull(nodep->resultp()); puts(")"); } void visit(AstStop* nodep) override { @@ -909,6 +905,9 @@ public: void visit(AstRand* nodep) override { emitOpName(nodep, nodep->emitC(), nodep->seedp(), nullptr, nullptr); } + void visit(AstRandRNG* nodep) override { + emitOpName(nodep, nodep->emitC(), nullptr, nullptr, nullptr); + } void visit(AstTime* nodep) override { puts("VL_TIME_UNITED_Q("); if (nodep->timeunit().isNone()) nodep->v3fatalSrc("$time has no units"); @@ -925,13 +924,13 @@ public: } void visit(AstTimeFormat* nodep) override { puts("VL_TIMEFORMAT_IINI("); - iterateAndNextNull(nodep->unitsp()); + iterateAndNextConstNull(nodep->unitsp()); puts(", "); - iterateAndNextNull(nodep->precisionp()); + iterateAndNextConstNull(nodep->precisionp()); puts(", "); emitCvtPackStr(nodep->suffixp()); puts(", "); - iterateAndNextNull(nodep->widthp()); + iterateAndNextConstNull(nodep->widthp()); puts(", vlSymsp->_vm_contextp__);\n"); } void visit(AstTimePrecision* nodep) override { @@ -950,23 +949,23 @@ public: void visit(AstTextBlock* nodep) override { visit(static_cast(nodep)); for (AstNode* childp = nodep->nodesp(); childp; childp = childp->nextp()) { - iterate(childp); + iterateConst(childp); if (nodep->commas() && childp->nextp()) puts(", "); } } void visit(AstCStmt* nodep) override { putbs(""); - iterateAndNextNull(nodep->exprsp()); + iterateAndNextConstNull(nodep->exprsp()); } void visit(AstCExpr* nodep) override { putbs(""); - iterateAndNextNull(nodep->exprsp()); + iterateAndNextConstNull(nodep->exprsp()); } void visit(AstUCStmt* nodep) override { VL_RESTORER(m_inUC); m_inUC = true; putsDecoration(ifNoProtect("// $c statement at " + nodep->fileline()->ascii() + "\n")); - iterateAndNextNull(nodep->exprsp()); + iterateAndNextConstNull(nodep->exprsp()); puts("\n"); } void visit(AstUCFunc* nodep) override { @@ -974,7 +973,7 @@ public: m_inUC = true; puts("\n"); putsDecoration(ifNoProtect("// $c function at " + nodep->fileline()->ascii() + "\n")); - iterateAndNextNull(nodep->exprsp()); + iterateAndNextConstNull(nodep->exprsp()); puts("\n"); } @@ -996,7 +995,7 @@ public: putbs("("); puts(nodep->emitSimpleOperator()); puts(" "); - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); puts(")"); } else { emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nullptr, nullptr); @@ -1012,11 +1011,11 @@ public: } if (emitSimpleOk(nodep)) { putbs("("); - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); puts(" "); putbs(nodep->emitSimpleOperator()); puts(" "); - iterateAndNextNull(nodep->rhsp()); + iterateAndNextConstNull(nodep->rhsp()); puts(")"); } else { emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nullptr); @@ -1038,7 +1037,7 @@ public: putbs("VL_REDXOR_"); puts(cvtToStr(widthPow2)); puts("("); - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); puts(")"); } } @@ -1049,7 +1048,7 @@ public: } else { puts("(QData)("); } - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); puts(")"); } void visit(AstNodeCond* nodep) override { @@ -1058,27 +1057,27 @@ public: emitOpName(nodep, nodep->emitC(), nodep->condp(), nodep->thenp(), nodep->elsep()); } else { putbs("("); - iterateAndNextNull(nodep->condp()); + iterateAndNextConstNull(nodep->condp()); putbs(" ? "); - iterateAndNextNull(nodep->thenp()); + iterateAndNextConstNull(nodep->thenp()); putbs(" : "); - iterateAndNextNull(nodep->elsep()); + iterateAndNextConstNull(nodep->elsep()); puts(")"); } } void visit(AstMemberSel* nodep) override { - iterateAndNextNull(nodep->fromp()); + iterateAndNextConstNull(nodep->fromp()); putbs("->"); puts(nodep->varp()->nameProtect()); } void visit(AstStructSel* nodep) override { - iterateAndNextNull(nodep->fromp()); + iterateAndNextConstNull(nodep->fromp()); putbs("."); puts(nodep->nameProtect()); } void visit(AstNullCheck* nodep) override { puts("VL_NULL_CHECK("); - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); puts(", "); putsQuoted(protect(nodep->fileline()->filename())); puts(", "); @@ -1088,7 +1087,7 @@ public: void visit(AstNewCopy* nodep) override { puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", "); puts("*"); // i.e. make into a reference - iterateAndNextNull(nodep->rhsp()); + iterateAndNextConstNull(nodep->rhsp()); puts(")"); } void visit(AstSel* nodep) override { @@ -1106,9 +1105,9 @@ public: puts("OI("); if (nodep->lhsp()) puts(cvtToStr(nodep->lhsp()->widthMin())); puts(","); - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); puts(", "); - iterateAndNextNull(nodep->rhsp()); + iterateAndNextConstNull(nodep->rhsp()); puts(")"); } else { emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nullptr); @@ -1126,7 +1125,7 @@ public: puts("I("); puts(cvtToStr(nodep->lhsp()->widthMin())); puts(", "); - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); puts(", "); const uint32_t rd_log2 = V3Number::log2b(VN_AS(nodep->rhsp(), Const)->toUInt()); puts(cvtToStr(rd_log2) + ")"); @@ -1138,9 +1137,9 @@ public: } void visit(AstCastDynamic* nodep) override { putbs("VL_CAST_DYNAMIC("); - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); puts(", "); - iterateAndNextNull(nodep->rhsp()); + iterateAndNextConstNull(nodep->rhsp()); puts(")"); } void visit(AstCountBits* nodep) override { @@ -1154,16 +1153,16 @@ public: // (which is always 32) puts(", "); } - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); puts(", "); - iterateAndNextNull(nodep->rhsp()); + iterateAndNextConstNull(nodep->rhsp()); puts(", "); - iterateAndNextNull(nodep->thsp()); + iterateAndNextConstNull(nodep->thsp()); puts(", "); - iterateAndNextNull(nodep->fhsp()); + iterateAndNextConstNull(nodep->fhsp()); puts(")"); } - void visit(AstInitItem* nodep) override { iterateChildren(nodep); } + void visit(AstInitItem* nodep) override { iterateChildrenConst(nodep); } // Terminals void visit(AstVarRef* nodep) override { const AstVar* const varp = nodep->varp(); @@ -1221,17 +1220,17 @@ public: puts("()"); if (nodep->defaultp()) { putbs(".setDefault("); - iterateAndNextNull(nodep->defaultp()); + iterateAndNextConstNull(nodep->defaultp()); puts(")"); } } void visit(AstSetAssoc* nodep) override { - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); putbs(".set("); - iterateAndNextNull(nodep->keyp()); + iterateAndNextConstNull(nodep->keyp()); puts(", "); putbs(""); - iterateAndNextNull(nodep->valuep()); + iterateAndNextConstNull(nodep->valuep()); puts(")"); } void visit(AstConsWildcard* nodep) override { @@ -1239,17 +1238,17 @@ public: puts("()"); if (nodep->defaultp()) { putbs(".setDefault("); - iterateAndNextNull(nodep->defaultp()); + iterateAndNextConstNull(nodep->defaultp()); puts(")"); } } void visit(AstSetWildcard* nodep) override { - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); putbs(".set("); - iterateAndNextNull(nodep->keyp()); + iterateAndNextConstNull(nodep->keyp()); puts(", "); putbs(""); - iterateAndNextNull(nodep->valuep()); + iterateAndNextConstNull(nodep->valuep()); puts(")"); } void visit(AstConsDynArray* nodep) override { @@ -1258,12 +1257,12 @@ public: puts("()"); } else { puts("::cons("); - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); if (nodep->rhsp()) { puts(", "); putbs(""); } - iterateAndNextNull(nodep->rhsp()); + iterateAndNextConstNull(nodep->rhsp()); puts(")"); } } @@ -1271,7 +1270,7 @@ public: putbs(nodep->dtypep()->cType("", false, false)); puts("{"); for (AstNode* memberp = nodep->membersp(); memberp; memberp = memberp->nextp()) { - iterate(memberp); + iterateConst(memberp); if (memberp->nextp()) { puts(", "); } } puts("}"); @@ -1281,7 +1280,7 @@ public: putbs("."); puts(vdtypep->name()); puts(" = "); - iterate(nodep->rhsp()); + iterateConst(nodep->rhsp()); } void visit(AstConsQueue* nodep) override { putbs(nodep->dtypep()->cType("", false, false)); @@ -1289,12 +1288,12 @@ public: puts("()"); } else { puts("::cons("); - iterateAndNextNull(nodep->lhsp()); + iterateAndNextConstNull(nodep->lhsp()); if (nodep->rhsp()) { puts(", "); putbs(""); } - iterateAndNextNull(nodep->rhsp()); + iterateAndNextConstNull(nodep->rhsp()); puts(")"); } } @@ -1311,8 +1310,8 @@ public: // Default void visit(AstNode* nodep) override { - puts(string("\n???? // ") + nodep->prettyTypeName() + "\n"); - iterateChildren(nodep); + puts(string{"\n???? // "} + nodep->prettyTypeName() + "\n"); + iterateChildrenConst(nodep); // LCOV_EXCL_START if (!v3Global.opt.lintOnly()) { // An internal problem, so suppress nodep->v3fatalSrc("Unknown node type reached emitter: " << nodep->prettyTypeName()); @@ -1326,7 +1325,7 @@ public: : EmitCFunc{} { m_ofp = ofp; m_trackText = trackText; - iterate(nodep); + iterateConst(nodep); } ~EmitCFunc() override = default; }; diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index eadea126f..63a06a956 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -117,7 +117,12 @@ class EmitCHeader final : public EmitCConstInit { emitCurrentList(); } void emitInternalVarDecls(const AstNodeModule* modp) { - if (!VN_IS(modp, Class)) { + if (const AstClass* const classp = VN_CAST(modp, Class)) { + if (classp->needRNG()) { + putsDecoration("\n// INTERNAL VARIABLES\n"); + puts("VlRNG __Vm_rng;\n"); + } + } else { // not class putsDecoration("\n// INTERNAL VARIABLES\n"); puts(symClassName() + "* const vlSymsp;\n"); } @@ -136,7 +141,7 @@ class EmitCHeader final : public EmitCConstInit { puts(varp->dtypep()->cType(varp->nameProtect(), false, false)); if (canBeConstexpr) { puts(" = "); - iterate(varp->valuep()); + iterateConst(varp->valuep()); } puts(";\n"); } @@ -200,7 +205,7 @@ class EmitCHeader final : public EmitCConstInit { } puts(itemp->nameProtect()); puts(" = "); - iterate(itemp->valuep()); + iterateConst(itemp->valuep()); if (VN_IS(itemp->nextp(), EnumItem)) puts(","); puts("\n"); } @@ -224,7 +229,7 @@ class EmitCHeader final : public EmitCConstInit { } } puts(sdtypep->verilogKwd()); // "struct"/"union" - puts(" " + EmitCBaseVisitor::prefixNameProtect(sdtypep) + " {\n"); + puts(" " + EmitCBase::prefixNameProtect(sdtypep) + " {\n"); for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) { puts(itemp->dtypep()->cType(itemp->nameProtect(), false, false)); diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index 8bfc9fcfc..679f392ce 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -33,28 +33,27 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### // Visitor that gathers the headers required by an AstCFunc -class EmitCGatherDependencies final : VNVisitor { +class EmitCGatherDependencies final : VNVisitorConst { // Ordered set, as it is used as a key in another map. std::set m_dependencies; // Header names to be included in output C++ file // METHODS - void addSymsDependency() { m_dependencies.insert(EmitCBaseVisitor::symClassName()); } + void addSymsDependency() { m_dependencies.insert(EmitCBase::symClassName()); } void addModDependency(const AstNodeModule* modp) { if (const AstClass* const classp = VN_CAST(modp, Class)) { - m_dependencies.insert(EmitCBaseVisitor::prefixNameProtect(classp->classOrPackagep())); + m_dependencies.insert(EmitCBase::prefixNameProtect(classp->classOrPackagep())); } else { - m_dependencies.insert(EmitCBaseVisitor::prefixNameProtect(modp)); + m_dependencies.insert(EmitCBase::prefixNameProtect(modp)); } } void addDTypeDependency(const AstNodeDType* nodep) { if (const AstClassRefDType* const dtypep = VN_CAST(nodep, ClassRefDType)) { m_dependencies.insert( - EmitCBaseVisitor::prefixNameProtect(dtypep->classp()->classOrPackagep())); + EmitCBase::prefixNameProtect(dtypep->classp()->classOrPackagep())); } else if (const AstNodeUOrStructDType* const dtypep = VN_CAST(nodep, NodeUOrStructDType)) { if (!dtypep->packed()) { - m_dependencies.insert( - EmitCBaseVisitor::prefixNameProtect(dtypep->classOrPackagep())); + m_dependencies.insert(EmitCBase::prefixNameProtect(dtypep->classOrPackagep())); } } } @@ -142,7 +141,7 @@ class EmitCGatherDependencies final : VNVisitor { // declaration of the receiver class, but their body very likely includes at least one // relative reference, so we are probably not loosing much. addModDependency(EmitCParentModule::get(cfuncp)); - iterate(cfuncp); + iterateConst(cfuncp); } public: @@ -175,8 +174,7 @@ class EmitCImp final : EmitCFunc { // Unfortunately we have some lint checks here, so we can't just skip processing. // We should move them to a different stage. const string filename = VL_DEV_NULL; - m_cfilesr.push_back( - newCFile(filename, /* slow: */ m_slow, /* source: */ true, /* add */ false)); + m_cfilesr.push_back(createCFile(filename, /* slow: */ m_slow, /* source: */ true)); m_ofp = new V3OutCFile{filename}; } else { string filename = v3Global.opt.makeDir() + "/" + prefixNameProtect(m_fileModp); @@ -186,12 +184,11 @@ class EmitCImp final : EmitCFunc { } if (m_slow) filename += "__Slow"; filename += ".cpp"; - m_cfilesr.push_back( - newCFile(filename, /* slow: */ m_slow, /* source: */ true, /* add */ false)); + m_cfilesr.push_back(createCFile(filename, /* slow: */ m_slow, /* source: */ true)); m_ofp = v3Global.opt.systemC() ? new V3OutScFile{filename} : new V3OutCFile{filename}; } - ofp()->putsHeader(); + putsHeader(); puts("// DESCRIPTION: Verilator output: Design implementation internals\n"); puts("// See " + topClassName() + ".h for the primary calling header\n"); @@ -199,6 +196,7 @@ class EmitCImp final : EmitCFunc { puts("\n#include \"verilated.h\"\n"); if (v3Global.dpi()) puts("#include \"verilated_dpi.h\"\n"); puts("\n"); + puts("#include \"" + symClassName() + ".h\"\n"); for (const string& name : headers) puts("#include \"" + name + ".h\"\n"); emitTextSection(m_modp, VNType::atScImpHdr); @@ -263,7 +261,7 @@ class EmitCImp final : EmitCFunc { puts(", "); puts(varp->nameProtect()); puts("("); - iterate(varp->valuep()); + iterateConst(varp->valuep()); puts(")\n"); } else if (varp->isIO() && varp->isSc()) { puts(", "); @@ -402,7 +400,7 @@ class EmitCImp final : EmitCFunc { const int vecnum = vects++; UASSERT_OBJ(arrayp->hi() >= arrayp->lo(), varp, "Should have swapped msb & lsb earlier."); - const string ivar = string("__Vi") + cvtToStr(vecnum); + const string ivar = string{"__Vi"} + cvtToStr(vecnum); puts("for (int __Vi" + cvtToStr(vecnum) + " = " + cvtToStr(0)); puts("; " + ivar + " < " + cvtToStr(arrayp->elementsConst())); puts("; ++" + ivar + ") {\n"); @@ -416,7 +414,7 @@ class EmitCImp final : EmitCFunc { if (elementp->isWide() && !(basicp && basicp->keyword() == VBasicDTypeKwd::STRING)) { const int vecnum = vects++; - const string ivar = string("__Vi") + cvtToStr(vecnum); + const string ivar = string{"__Vi"} + cvtToStr(vecnum); puts("for (int __Vi" + cvtToStr(vecnum) + " = " + cvtToStr(0)); puts("; " + ivar + " < " + cvtToStr(elementp->widthWords())); puts("; ++" + ivar + ") {\n"); @@ -515,7 +513,7 @@ class EmitCImp final : EmitCFunc { // Compute the hash of the dependencies, so we can add it to the filenames to // disambiguate them V3Hash hash; - for (const string& name : *m_requiredHeadersp) { hash += name; } + for (const string& name : *m_requiredHeadersp) hash += name; m_subFileName = "DepSet_" + hash.toString(); // Open output file openNextOutputFile(*m_requiredHeadersp, m_subFileName); @@ -523,7 +521,7 @@ class EmitCImp final : EmitCFunc { for (AstCFunc* const funcp : pair.second) { VL_RESTORER(m_modp); m_modp = EmitCParentModule::get(funcp); - iterate(funcp); + iterateConst(funcp); } // Close output file VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); @@ -598,7 +596,7 @@ class EmitCTrace final : EmitCFunc { if (m_slow) filename += "__Slow"; filename += ".cpp"; - AstCFile* const cfilep = newCFile(filename, m_slow, true /*source*/, false /*add*/); + AstCFile* const cfilep = createCFile(filename, m_slow, true /*source*/); cfilep->support(true); m_cfilesr.push_back(cfilep); @@ -607,9 +605,9 @@ class EmitCTrace final : EmitCFunc { } else { m_ofp = new V3OutCFile{filename}; } - m_ofp->putsHeader(); - m_ofp->puts("// DESCR" - "IPTION: Verilator output: Tracing implementation internals\n"); + putsHeader(); + puts("// DESCR" + "IPTION: Verilator output: Tracing implementation internals\n"); // Includes puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n"); @@ -775,7 +773,7 @@ class EmitCTrace final : EmitCFunc { } void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) { - iterateAndNextNull(nodep->precondsp()); + iterateAndNextConstNull(nodep->precondsp()); const string func = nodep->full() ? "full" : "chg"; bool emitWidth = true; if (nodep->dtypep()->basicp()->isDouble()) { @@ -818,7 +816,7 @@ class EmitCTrace final : EmitCFunc { } else if (emitTraceIsScBv(nodep)) { puts("VL_SC_BV_DATAP("); } - iterate(varrefp); // Put var name out + iterateConst(varrefp); // Put var name out // Tracing only supports 1D arrays if (nodep->declp()->arrayRange().ranged()) { if (arrayindex == -2) { @@ -840,7 +838,7 @@ class EmitCTrace final : EmitCFunc { puts(")"); } else { puts("("); - iterate(nodep->valuep()); + iterateConst(nodep->valuep()); puts(")"); } } @@ -902,7 +900,7 @@ class EmitCTrace final : EmitCFunc { openNextOutputFile(); // Emit functions for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) { iterate(funcp); } + if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) { iterateConst(funcp); } } // Close output file VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); diff --git a/src/V3EmitCInlines.cpp b/src/V3EmitCInlines.cpp index 338b956ec..e2603371e 100644 --- a/src/V3EmitCInlines.cpp +++ b/src/V3EmitCInlines.cpp @@ -28,7 +28,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### -class EmitCInlines final : EmitCBaseVisitor { +class EmitCInlines final : EmitCBaseVisitorConst { // STATE // METHODS @@ -37,26 +37,26 @@ class EmitCInlines final : EmitCBaseVisitor { void visit(AstCNew* nodep) override { if (v3Global.opt.savable()) v3warn(E_UNSUPPORTED, "Unsupported: --savable with dynamic new"); - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstDumpCtl* nodep) override { if (v3Global.opt.trace()) v3Global.needTraceDumper(true); - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstNodeDistBiop* nodep) override { v3Global.setUsesProbDist(); - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstNodeDistTriop* nodep) override { v3Global.setUsesProbDist(); - iterateChildren(nodep); + iterateChildrenConst(nodep); } //--------------------------------------- - void visit(AstNode* nodep) override { iterateChildren(nodep); } + void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } public: - explicit EmitCInlines(AstNetlist* nodep) { iterate(nodep); } + explicit EmitCInlines(AstNetlist* nodep) { iterateConst(nodep); } }; //###################################################################### @@ -64,5 +64,5 @@ public: void V3EmitC::emitcInlines() { UINFO(2, __FUNCTION__ << ": " << endl); - EmitCInlines(v3Global.rootp()); + { EmitCInlines{v3Global.rootp()}; } } diff --git a/src/V3EmitCMain.cpp b/src/V3EmitCMain.cpp index 352e15603..10216025b 100644 --- a/src/V3EmitCMain.cpp +++ b/src/V3EmitCMain.cpp @@ -29,12 +29,12 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### -class EmitCMain final : EmitCBaseVisitor { +class EmitCMain final : EmitCBaseVisitorConst { // METHODS // VISITORS // This visitor doesn't really iterate, but exist to appease base class - void visit(AstNode* nodep) override { iterateChildren(nodep); } // LCOV_EXCL_LINE + void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } // LCOV_EXCL_LINE public: // CONSTRUCTORS diff --git a/src/V3EmitCModel.cpp b/src/V3EmitCModel.cpp index 94567f1a8..e7debc3cd 100644 --- a/src/V3EmitCModel.cpp +++ b/src/V3EmitCModel.cpp @@ -658,7 +658,7 @@ class EmitCModel final : public EmitCFunc { puts("\n"); } - iterate(funcp); + iterateConst(funcp); } if (m_ofp) { VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); } diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 2ae694b48..59b299d7b 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -32,7 +32,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### // Symbol table emitting -class EmitCSyms final : EmitCBaseVisitor { +class EmitCSyms final : EmitCBaseVisitorConst { // NODE STATE // Cleared on Netlist // AstNodeModule::user1() -> bool. Set true __Vconfigure called @@ -278,7 +278,7 @@ class EmitCSyms final : EmitCBaseVisitor { // VISITORS void visit(AstNetlist* nodep) override { // Collect list of scopes - iterateChildren(nodep); + iterateChildrenConst(nodep); varsExpand(); if (v3Global.opt.vpi()) buildVpiHierarchy(); @@ -304,7 +304,7 @@ class EmitCSyms final : EmitCBaseVisitor { VL_RESTORER(m_modp); { m_modp = nodep; - iterateChildren(nodep); + iterateChildrenConst(nodep); } } void visit(AstCellInline* nodep) override { @@ -356,7 +356,7 @@ class EmitCSyms final : EmitCBaseVisitor { } void visit(AstVar* nodep) override { nameCheck(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); if (nodep->isSigUserRdPublic() && !m_cfuncp) m_modVars.emplace_back(std::make_pair(m_modp, nodep)); } @@ -372,18 +372,18 @@ class EmitCSyms final : EmitCBaseVisitor { VL_RESTORER(m_cfuncp); { m_cfuncp = nodep; - iterateChildren(nodep); + iterateChildrenConst(nodep); } } //--------------------------------------- void visit(AstConst*) override {} - void visit(AstNode* nodep) override { iterateChildren(nodep); } + void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } public: explicit EmitCSyms(AstNetlist* nodep, bool dpiHdrOnly) : m_dpiHdrOnly{dpiHdrOnly} { - iterate(nodep); + iterateConst(nodep); } }; @@ -432,7 +432,8 @@ void EmitCSyms::emitSymHdr() { if (funcp->dpiExportImpl()) { const string cbtype = protect(v3Global.opt.prefix() + "__Vcb_" + funcp->cname() + "_t"); - types["using " + cbtype + " = void (*) (" + cFuncArgs(funcp) + ");\n"] = 1; + const string functype = funcp->rtnTypeVoid() + " (*) (" + cFuncArgs(funcp) + ")"; + types["using " + cbtype + " = " + functype + ";\n"] = 1; } } for (const auto& i : types) puts(i.first); @@ -516,7 +517,7 @@ void EmitCSyms::emitSymHdr() { puts("\n// CONSTRUCTORS\n"); puts(symClassName() + "(VerilatedContext* contextp, const char* namep, " + topClassName() + "* modelp);\n"); - puts(string("~") + symClassName() + "();\n"); + puts(string{"~"} + symClassName() + "();\n"); for (const auto& i : m_usesVfinal) { puts("void " + symClassName() + "_" + cvtToStr(i.first) + "("); diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index 86f325c2f..02696c342 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -156,9 +156,9 @@ public: of.puts("# Path to Verilator kit (from $VERILATOR_ROOT)\n"); of.puts("VERILATOR_ROOT = " + V3Options::getenvVERILATOR_ROOT() + "\n"); of.puts("# SystemC include directory with systemc.h (from $SYSTEMC_INCLUDE)\n"); - of.puts(string("SYSTEMC_INCLUDE ?= ") + V3Options::getenvSYSTEMC_INCLUDE() + "\n"); + of.puts(string{"SYSTEMC_INCLUDE ?= "} + V3Options::getenvSYSTEMC_INCLUDE() + "\n"); of.puts("# SystemC library directory with libsystemc.a (from $SYSTEMC_LIBDIR)\n"); - of.puts(string("SYSTEMC_LIBDIR ?= ") + V3Options::getenvSYSTEMC_LIBDIR() + "\n"); + of.puts(string{"SYSTEMC_LIBDIR ?= "} + V3Options::getenvSYSTEMC_LIBDIR() + "\n"); // Only check it if we really need the value if (v3Global.opt.systemC() && !V3Options::systemCFound()) { @@ -169,22 +169,22 @@ public: of.puts("\n### Switches...\n"); of.puts("# C++ code coverage 0/1 (from --prof-c)\n"); - of.puts(string("VM_PROFC = ") + ((v3Global.opt.profC()) ? "1" : "0") + "\n"); + of.puts(string{"VM_PROFC = "} + ((v3Global.opt.profC()) ? "1" : "0") + "\n"); of.puts("# SystemC output mode? 0/1 (from --sc)\n"); - of.puts(string("VM_SC = ") + ((v3Global.opt.systemC()) ? "1" : "0") + "\n"); + of.puts(string{"VM_SC = "} + ((v3Global.opt.systemC()) ? "1" : "0") + "\n"); of.puts("# Legacy or SystemC output mode? 0/1 (from --sc)\n"); - of.puts(string("VM_SP_OR_SC = $(VM_SC)\n")); + of.puts(string{"VM_SP_OR_SC = $(VM_SC)\n"}); of.puts("# Deprecated\n"); - of.puts(string("VM_PCLI = ") + (v3Global.opt.systemC() ? "0" : "1") + "\n"); + of.puts(string{"VM_PCLI = "} + (v3Global.opt.systemC() ? "0" : "1") + "\n"); of.puts( "# Deprecated: SystemC architecture to find link library path (from $SYSTEMC_ARCH)\n"); - of.puts(string("VM_SC_TARGET_ARCH = ") + V3Options::getenvSYSTEMC_ARCH() + "\n"); + of.puts(string{"VM_SC_TARGET_ARCH = "} + V3Options::getenvSYSTEMC_ARCH() + "\n"); of.puts("\n### Vars...\n"); of.puts("# Design prefix (from --prefix)\n"); - of.puts(string("VM_PREFIX = ") + v3Global.opt.prefix() + "\n"); + of.puts(string{"VM_PREFIX = "} + v3Global.opt.prefix() + "\n"); of.puts("# Module prefix (from --prefix)\n"); - of.puts(string("VM_MODPREFIX = ") + v3Global.opt.modPrefix() + "\n"); + of.puts(string{"VM_MODPREFIX = "} + v3Global.opt.modPrefix() + "\n"); of.puts("# User CFLAGS (from -CFLAGS on Verilator command line)\n"); of.puts("VM_USER_CFLAGS = \\\n"); diff --git a/src/V3EmitMk.h b/src/V3EmitMk.h index a23748fb0..0ddfdba66 100644 --- a/src/V3EmitMk.h +++ b/src/V3EmitMk.h @@ -26,6 +26,9 @@ class V3HierBlockPlan; class V3EmitMk final { public: + // Number of source files after which to use parallel compiles + static const size_t PARALLEL_FILE_CNT_THRESHOLD = 128; + static void emitmk(); static void emitHierVerilation(const V3HierBlockPlan* planp); }; diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index d9f05b302..67c0a274a 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -31,7 +31,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; // ###################################################################### // Emit statements and expressions -class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { +class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { // MEMBERS bool m_suppressSemi = false; const bool m_suppressUnknown = false; @@ -166,7 +166,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { // AstSenItem is called for dumping in isolation by V3Order putfs(nodep, "@("); for (AstNode* expp = nodep->sensesp(); expp; expp = expp->nextp()) { - iterate(expp); + iterateConst(expp); if (expp->nextp()) putqs(expp->nextp(), " or "); } puts(")"); @@ -382,7 +382,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { void visit(AstStop* nodep) override { putfs(nodep, "$stop;\n"); } void visit(AstFinish* nodep) override { putfs(nodep, "$finish;\n"); } void visit(AstStmtExpr* nodep) override { - iterate(nodep->exprp()); + iterateConst(nodep->exprp()); puts(";\n"); } void visit(AstNodeSimpleText* nodep) override { @@ -398,7 +398,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { VL_RESTORER(m_suppressSemi); m_suppressVarSemi = nodep->commas(); for (AstNode* childp = nodep->nodesp(); childp; childp = childp->nextp()) { - iterate(childp); + iterateConst(childp); if (nodep->commas() && childp->nextp()) puts(", "); } } @@ -426,11 +426,11 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { } void visit(AstCMethodHard* nodep) override { - iterate(nodep->fromp()); + iterateConst(nodep->fromp()); puts("." + nodep->name() + "("); for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) { if (pinp != nodep->pinsp()) puts(", "); - iterate(pinp); + iterateConst(pinp); } puts(")"); } @@ -506,7 +506,12 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { nodep->thsp()); } void visit(AstMemberSel* nodep) override { - iterate(nodep->fromp()); + iterateConst(nodep->fromp()); + puts("."); + puts(nodep->prettyName()); + } + void visit(AstStructSel* nodep) override { + iterateConst(nodep->fromp()); puts("."); puts(nodep->prettyName()); } @@ -528,7 +533,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { puts(cvtToStr(itr.first)); puts(":"); AstNode* const valuep = itr.second->valuep(); - iterate(valuep); + iterateConst(valuep); } puts("}"); } @@ -608,13 +613,13 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { } void visit(AstConstDType* nodep) override { putfs(nodep, "const "); - iterate(nodep->subDTypep()); + iterateConst(nodep->subDTypep()); } void visit(AstNodeArrayDType* nodep) override { - iterate(nodep->subDTypep()); + iterateConst(nodep->subDTypep()); iterateAndNextConstNull(nodep->rangep()); } - void visit(AstRefDType* nodep) override { iterate(nodep->skipRefp()); } + void visit(AstRefDType* nodep) override { iterateConst(nodep->skipRefp()); } void visit(AstNodeUOrStructDType* nodep) override { puts(nodep->verilogKwd() + " "); if (nodep->packed()) puts("packed "); @@ -622,13 +627,13 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { puts("{"); for (AstMemberDType* itemp = nodep->membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) { - iterate(itemp); + iterateConst(itemp); puts(";"); } puts("}"); } void visit(AstMemberDType* nodep) override { - iterate(nodep->subDTypep()); + iterateConst(nodep->subDTypep()); puts(" "); puts(nodep->name()); } @@ -693,7 +698,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { unpackps.push_back(unpackp); dtypep = unpackp->subDTypep(); } else { - iterate(dtypep); + iterateConst(dtypep); puts(" "); puts(nodep->prettyName()); dtypep = nullptr; @@ -735,16 +740,16 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { public: bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars - explicit EmitVBaseVisitor(bool suppressUnknown, AstSenTree* domainp) + explicit EmitVBaseVisitorConst(bool suppressUnknown, AstSenTree* domainp) : m_suppressUnknown{suppressUnknown} , m_sensesp{domainp} {} - ~EmitVBaseVisitor() override = default; + ~EmitVBaseVisitorConst() override = default; }; //###################################################################### // Emit to an output file -class EmitVFileVisitor final : public EmitVBaseVisitor { +class EmitVFileVisitor final : public EmitVBaseVisitorConst { // METHODS void puts(const string& str) override { ofp()->puts(str); } void putbs(const string& str) override { ofp()->putbs(str); } @@ -754,10 +759,10 @@ class EmitVFileVisitor final : public EmitVBaseVisitor { public: EmitVFileVisitor(AstNode* nodep, V3OutVFile* ofp, bool trackText, bool suppressUnknown) - : EmitVBaseVisitor{suppressUnknown, nullptr} { + : EmitVBaseVisitorConst{suppressUnknown, nullptr} { m_ofp = ofp; m_trackText = trackText; - iterate(nodep); + iterateConst(nodep); } ~EmitVFileVisitor() override = default; }; @@ -765,7 +770,7 @@ public: //###################################################################### // Emit to a stream (perhaps stringstream) -class EmitVStreamVisitor final : public EmitVBaseVisitor { +class EmitVStreamVisitor final : public EmitVBaseVisitorConst { // MEMBERS std::ostream& m_os; // METHODS @@ -777,9 +782,9 @@ class EmitVStreamVisitor final : public EmitVBaseVisitor { public: EmitVStreamVisitor(const AstNode* nodep, std::ostream& os) - : EmitVBaseVisitor{false, nullptr} + : EmitVBaseVisitorConst{false, nullptr} , m_os(os) { // Need () or GCC 4.8 false warning - iterate(const_cast(nodep)); + iterateConst(const_cast(nodep)); } ~EmitVStreamVisitor() override = default; }; @@ -832,7 +837,7 @@ public: } }; -class EmitVPrefixedVisitor final : public EmitVBaseVisitor { +class EmitVPrefixedVisitor final : public EmitVBaseVisitorConst { // MEMBERS EmitVPrefixedFormatter m_formatter; // Special verilog formatter (Way down the // inheritance is another unused V3OutFormatter) @@ -856,10 +861,10 @@ class EmitVPrefixedVisitor final : public EmitVBaseVisitor { public: EmitVPrefixedVisitor(const AstNode* nodep, std::ostream& os, const string& prefix, int flWidth, AstSenTree* domainp, bool user3mark) - : EmitVBaseVisitor{false, domainp} + : EmitVBaseVisitorConst{false, domainp} , m_formatter{os, prefix, flWidth} { if (user3mark) VNUser3InUse::check(); - iterate(const_cast(nodep)); + iterateConst(const_cast(nodep)); } ~EmitVPrefixedVisitor() override = default; }; diff --git a/src/V3EmitXml.cpp b/src/V3EmitXml.cpp index ef8071a20..5dc1ed39e 100644 --- a/src/V3EmitXml.cpp +++ b/src/V3EmitXml.cpp @@ -31,7 +31,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; // ###################################################################### // Emit statements and expressions -class EmitXmlFileVisitor final : public VNVisitor { +class EmitXmlFileVisitor final : public VNVisitorConst { // NODE STATE // Entire netlist: // AstNode::user1 -> uint64_t, number to connect crossrefs @@ -94,7 +94,7 @@ class EmitXmlFileVisitor final : public VNVisitor { if (tag == "") tag = VString::downcase(nodep->typeName()); if (nodep->op1p() || nodep->op2p() || nodep->op3p() || nodep->op4p()) { puts(">\n"); - iterateChildren(nodep); + iterateChildrenConst(nodep); puts("\n"); } else { puts("/>\n"); @@ -117,13 +117,13 @@ class EmitXmlFileVisitor final : public VNVisitor { void visit(AstNodeIf* nodep) override { outputTag(nodep, "if"); puts(">\n"); - iterateAndNextNull(nodep->condp()); + iterateAndNextConstNull(nodep->condp()); puts("\n"); - iterateAndNextNull(nodep->thensp()); + iterateAndNextConstNull(nodep->thensp()); puts("\n"); if (nodep->elsesp()) { puts("\n"); - iterateAndNextNull(nodep->elsesp()); + iterateAndNextConstNull(nodep->elsesp()); puts("\n"); } puts("\n"); @@ -132,34 +132,34 @@ class EmitXmlFileVisitor final : public VNVisitor { outputTag(nodep, "while"); puts(">\n"); puts("\n"); - iterateAndNextNull(nodep->precondsp()); + iterateAndNextConstNull(nodep->precondsp()); puts("\n"); if (nodep->condp()) { puts("\n"); - iterateAndNextNull(nodep->condp()); + iterateAndNextConstNull(nodep->condp()); puts("\n"); } if (nodep->stmtsp()) { puts("\n"); - iterateAndNextNull(nodep->stmtsp()); + iterateAndNextConstNull(nodep->stmtsp()); puts("\n"); } if (nodep->incsp()) { puts("\n"); - iterateAndNextNull(nodep->incsp()); + iterateAndNextConstNull(nodep->incsp()); puts("\n"); } puts("\n"); } void visit(AstNetlist* nodep) override { puts("\n"); - iterateChildren(nodep); + iterateChildrenConst(nodep); puts("\n"); } void visit(AstConstPool* nodep) override { if (!v3Global.opt.xmlOnly()) { puts("\n"); - iterateChildren(nodep); + iterateChildrenConst(nodep); puts("\n"); } } @@ -170,7 +170,7 @@ class EmitXmlFileVisitor final : public VNVisitor { puts("\n"); - iterateChildren(itr.second); + iterateChildrenConst(itr.second); puts("\n"); } puts("\n"); @@ -314,7 +314,7 @@ class EmitXmlFileVisitor final : public VNVisitor { public: EmitXmlFileVisitor(AstNode* nodep, V3OutFile* ofp) : m_ofp{ofp} { - iterate(nodep); + iterateConst(nodep); } ~EmitXmlFileVisitor() override = default; }; @@ -322,7 +322,7 @@ public: //###################################################################### // List of module files xml visitor -class ModuleFilesXmlVisitor final : public VNVisitor { +class ModuleFilesXmlVisitor final : public VNVisitorConst { private: // MEMBERS std::ostream& m_os; @@ -334,7 +334,7 @@ private: // VISITORS void visit(AstNetlist* nodep) override { // Children are iterated backwards to ensure correct compilation order - iterateChildrenBackwards(nodep); + iterateChildrenBackwardsConst(nodep); } void visit(AstNodeModule* nodep) override { // Only list modules and interfaces @@ -354,7 +354,7 @@ public: ModuleFilesXmlVisitor(AstNetlist* nodep, std::ostream& os) : m_os(os) { // Need () or GCC 4.8 false warning // Operate on whole netlist - nodep->accept(*this); + iterateConst(nodep); // Xml output m_os << "\n"; for (const FileLine* ifp : m_nodeModules) { @@ -369,7 +369,7 @@ public: //###################################################################### // Hierarchy of Cells visitor -class HierCellsXmlVisitor final : public VNVisitor { +class HierCellsXmlVisitor final : public VNVisitorConst { private: // MEMBERS std::ostream& m_os; @@ -391,7 +391,7 @@ private: << " hier=\"" << nodep->prettyName() << "\""; m_hier = nodep->prettyName() + "."; m_hasChildren = false; - iterateChildren(nodep); + iterateChildrenConst(nodep); if (m_hasChildren) { m_os << "\n"; } else { @@ -410,7 +410,7 @@ private: const std::string hier = m_hier; m_hier += nodep->name() + "."; m_hasChildren = false; - iterateChildren(nodep->modp()); + iterateChildrenConst(nodep->modp()); if (m_hasChildren) { m_os << "\n"; } else { @@ -419,15 +419,19 @@ private: m_hier = hier; m_hasChildren = true; } + void visit(AstBegin* nodep) override { + VL_RESTORER(m_hier); + if (nodep->name() != "") m_hier += nodep->name() + "."; + iterateChildrenConst(nodep); + } //----- - void visit(AstNode* nodep) override { iterateChildren(nodep); } + void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } public: // CONSTRUCTORS HierCellsXmlVisitor(AstNetlist* nodep, std::ostream& os) : m_os(os) { // Need () or GCC 4.8 false warning - // Operate on whole netlist - nodep->accept(*this); + iterateConst(nodep); } ~HierCellsXmlVisitor() override = default; }; diff --git a/src/V3Error.cpp b/src/V3Error.cpp index 2f1a01f20..6fc051edc 100644 --- a/src/V3Error.cpp +++ b/src/V3Error.cpp @@ -40,6 +40,7 @@ V3ErrorCode::V3ErrorCode(const char* msgp) { const V3ErrorCode code{codei}; if (0 == VL_STRCASECMP(msgp, code.ascii())) { m_e = code; + if (isRenamed()) m_e = renamedTo().m_e; return; } } @@ -220,6 +221,7 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra) } #ifndef V3ERROR_NO_GLOBAL_ if (dumpTree() || debug()) { + V3Broken::allowMidvisitorCheck(true); V3ThreadPool::s().requestExclusiveAccess([&]() VL_REQUIRES(m_mutex) { if (dumpTree()) { v3Global.rootp()->dumpTreeFile( @@ -255,7 +257,7 @@ void V3Error::init() { describedEachWarn(static_cast(i), false); pretendError(static_cast(i), V3ErrorCode{i}.pretendError()); } - if (VL_UNCOVERABLE(string(V3ErrorCode{V3ErrorCode::_ENUM_MAX}.ascii()) != " MAX")) { + if (VL_UNCOVERABLE(string{V3ErrorCode{V3ErrorCode::_ENUM_MAX}.ascii()} != " MAX")) { v3fatalSrc("Enum table in V3ErrorCode::EC_ascii() is munged"); } } diff --git a/src/V3Error.h b/src/V3Error.h index 05f865f13..01578fa0e 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -20,7 +20,7 @@ #include "config_build.h" #include "verilatedos.h" -#include "verilated_threads.h" +#include "V3Mutex.h" // Limited V3 headers here - this is a base class for Vlc etc #include "V3String.h" @@ -70,6 +70,7 @@ public: EC_FIRST_WARN, // Just a code so the program knows where to start warnings // ALWCOMBORDER, // Always_comb with unordered statements + ASCRANGE, // Ascending bit range vector ASSIGNDLY, // Assignment delays ASSIGNIN, // Assigning to input BADSTDPRAGMA, // Any error related to pragmas @@ -87,6 +88,7 @@ public: CMPCONST, // Comparison is constant due to limited range COLONPLUS, // :+ instead of +: COMBDLY, // Combinatorial delayed assignment + CONSTRAINTIGN, // Constraint ignored CONTASSREG, // Continuous assignment on reg DECLFILENAME, // Declaration doesn't match filename DEFPARAM, // Style: Defparam @@ -109,7 +111,7 @@ public: INITIALDLY, // Initial delayed statement INSECURE, // Insecure options LATCH, // Latch detected outside of always_latch block - LITENDIAN, // Little bit endian vector + LITENDIAN, // Little endian, renamed to ASCRANGE MINTYPMAXDLY, // Unsupported: min/typ/max delay expressions MODDUP, // Duplicate module MULTIDRIVEN, // Driven from multiple blocks @@ -131,6 +133,7 @@ public: SELRANGE, // Selection index out of range SHORTREAL, // Shortreal not supported SPLITVAR, // Cannot split the variable + STATICVAR, // Static variable declared in a loop with a declaration assignment STMTDLY, // Delayed statement SYMRSVDWORD, // Symbol is Reserved Word SYNCASYNCNET, // Mixed sync + async reset @@ -182,10 +185,10 @@ public: "PORTSHORT", "UNSUPPORTED", "TASKNSVAR", "NEEDTIMINGOPT", "NOTIMING", // Warnings " EC_FIRST_WARN", - "ALWCOMBORDER", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA", + "ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA", "BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE", "CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA", - "CMPCONST", "COLONPLUS", "COMBDLY", "CONTASSREG", + "CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", "DECLFILENAME", "DEFPARAM", "DEPRECATED", "ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK", "HIERBLOCK", "IFDEPTH", "IGNOREDRETURN", @@ -195,7 +198,7 @@ public: "MULTIDRIVEN", "MULTITOP", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY", "PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY", - "SELRANGE", "SHORTREAL", "SPLITVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", + "SELRANGE", "SHORTREAL", "SPLITVAR", "STATICVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", "TICKCOUNT", "TIMESCALEMOD", "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", "UNPACKED", "UNSIGNED", "UNUSEDGENVAR", "UNUSEDPARAM", "UNUSEDSIGNAL", @@ -219,18 +222,18 @@ public: || m_e == CONTASSREG || m_e == ENCAPSULATED || m_e == ENDLABEL || m_e == ENUMVALUE || m_e == IMPURE || m_e == PINNOTFOUND || m_e == PKGNODECL || m_e == PROCASSWIRE // Says IEEE - || m_e == ZERODLY); + ); } // Warnings to mention manual bool mentionManual() const VL_MT_SAFE { - return (m_e == EC_FATALSRC || m_e == SYMRSVDWORD || pretendError()); + return (m_e == EC_FATALSRC || m_e == SYMRSVDWORD || m_e == ZERODLY || pretendError()); } // Warnings that are lint only bool lintError() const VL_MT_SAFE { - return (m_e == ALWCOMBORDER || m_e == BSSPACE || m_e == CASEINCOMPLETE + return (m_e == ALWCOMBORDER || m_e == ASCRANGE || m_e == BSSPACE || m_e == CASEINCOMPLETE || m_e == CASEOVERLAP || m_e == CASEWITHX || m_e == CASEX || m_e == CASTCONST || m_e == CMPCONST || m_e == COLONPLUS || m_e == IMPLICIT || m_e == IMPLICITSTATIC - || m_e == LATCH || m_e == LITENDIAN || m_e == PINMISSING || m_e == REALCVT + || m_e == LATCH || m_e == PINMISSING || m_e == REALCVT || m_e == STATICVAR || m_e == UNSIGNED || m_e == WIDTH || m_e == WIDTHTRUNC || m_e == WIDTHEXPAND || m_e == WIDTHXZEXPAND); } @@ -248,12 +251,17 @@ public: return (m_e == UNUSEDGENVAR || m_e == UNUSEDPARAM || m_e == UNUSEDSIGNAL); } + V3ErrorCode renamedTo() const { + // Return a new error this error has been renamed to + if (m_e == LITENDIAN) return V3ErrorCode{ASCRANGE}; + return V3ErrorCode{EC_MIN}; // Not renamed; see isRenamed() + } + bool isRenamed() const { return renamedTo() != V3ErrorCode{EC_MIN}; } bool isUnder(V3ErrorCode other) { // backwards compatibility inheritance-like warnings if (m_e == other) { return true; } if (other == V3ErrorCode::WIDTH) { - return (m_e == WIDTH || m_e == WIDTHEXPAND || m_e == WIDTHTRUNC - || m_e == WIDTHXZEXPAND); + return (m_e == WIDTHEXPAND || m_e == WIDTHTRUNC || m_e == WIDTHXZEXPAND); } if (other == V3ErrorCode::I_UNUSED) { return (m_e == UNUSEDGENVAR || m_e == UNUSEDPARAM || m_e == UNUSEDSIGNAL); @@ -307,7 +315,7 @@ private: bool m_warnFatal VL_GUARDED_BY(m_mutex) = true; // Option: --warnFatal Warnings are fatal std::ostringstream m_errorStr VL_GUARDED_BY(m_mutex); // Error string being formed public: - VerilatedMutex m_mutex; // Make sure only single thread is in class + V3RecursiveMutex m_mutex; // Make sure only single thread is in class string msgPrefix() VL_REQUIRES(m_mutex); // returns %Error/%Warn string warnMore() VL_REQUIRES(m_mutex); @@ -400,57 +408,57 @@ public: static void debugDefault(int level) VL_MT_UNSAFE { s().debugDefault(level); } static int debugDefault() VL_MT_SAFE { return s().debugDefault(); } static void errorLimit(int level) VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; s().errorLimit(level); } static int errorLimit() VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; return s().errorLimit(); } static void warnFatal(bool flag) VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; s().warnFatal(flag); } static bool warnFatal() VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; return s().warnFatal(); } // returns %Error/%Warn static string msgPrefix() VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; return s().msgPrefix(); } static int errorCount() VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; return s().errorCount(); } static bool pretendError(int errorCode) VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; return s().pretendError(errorCode); } static int warnCount() VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; return s().warnCount(); } static bool errorContexted() VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; return s().errorContexted(); } static void errorContexted(bool flag) VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; s().errorContexted(flag); } static void describedEachWarn(V3ErrorCode code, bool flag) VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; s().describedEachWarn(code, flag); } // METHODS static void incErrors() VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; s().incErrors(); } static void incWarnings() VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; s().incWarnings(); } static void init(); @@ -460,20 +468,20 @@ public: static void abortIfWarnings(); // Suppress next %Warn if user has it off static void suppressThisWarning() VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; s().suppressThisWarning(); } static void pretendError(V3ErrorCode code, bool flag) VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; s().pretendError(code, flag); } static string lineStr(const char* filename, int lineno) VL_PURE; static V3ErrorCode errorCode() VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; return s().errorCode(); } static void errorExitCb(V3ErrorGuarded::ErrorExitCb cb) VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; s().errorExitCb(cb); } @@ -484,7 +492,7 @@ public: // streamed directly to cerr. // Use with caution as this function isn't MT_SAFE. static string warnMoreStandalone() VL_EXCLUDES(s().m_mutex) VL_MT_UNSAFE { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; return s().warnMore(); } // This function marks place in error message from which point message @@ -500,18 +508,18 @@ public: // Internals for v3error()/v3fatal() macros only // Error end takes the string stream to output, be careful to seek() as needed static void v3errorPrep(V3ErrorCode code) VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; s().v3errorPrep(code); } static std::ostringstream& v3errorStr() VL_MT_SAFE_EXCLUDES(s().m_mutex) { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; return s().v3errorStr(); } static void vlAbort(); // static, but often overridden in classes. static void v3errorEnd(std::ostringstream& sstr, const string& extra = "") VL_MT_SAFE_EXCLUDES(s().m_mutex) VL_MT_SAFE { - const VerilatedLockGuard guard{s().m_mutex}; + const V3RecursiveLockGuard guard{s().m_mutex}; s().v3errorEnd(sstr, extra); } // We can't call 's().v3errorEnd' directly in 'v3ErrorEnd'/'v3errorEndFatal', @@ -642,7 +650,7 @@ inline void v3errorEndFatal(std::ostringstream& sstr) // Helper macros for VL_DEFINE_DEBUG_FUNCTIONS // Takes an optional "name" (as __VA_ARGS__) #define VL_DEFINE_DEBUG(...) \ - VL_ATTR_UNUSED static int debug##__VA_ARGS__() { \ + VL_ATTR_UNUSED static int debug##__VA_ARGS__() VL_MT_SAFE { \ static int level = -1; \ if (VL_UNLIKELY(level < 0)) { \ std::string tag{VL_STRINGIFY(__VA_ARGS__)}; \ diff --git a/src/V3File.cpp b/src/V3File.cpp index 0921f7956..a197f6d6c 100644 --- a/src/V3File.cpp +++ b/src/V3File.cpp @@ -108,7 +108,8 @@ class V3FileDependImp final { }; // MEMBERS - std::set m_filenameSet; // Files generated (elim duplicates) + V3Mutex m_mutex; // Protects members + std::set m_filenameSet VL_GUARDED_BY(m_mutex); // Files generated (elim duplicates) std::set m_filenameList; // Files sourced/generated static string stripQuotes(const string& in) { @@ -121,7 +122,8 @@ class V3FileDependImp final { public: // ACCESSOR METHODS - void addSrcDepend(const string& filename) { + void addSrcDepend(const string& filename) VL_MT_SAFE_EXCLUDES(m_mutex) { + const V3LockGuard lock{m_mutex}; const auto itFoundPair = m_filenameSet.insert(filename); if (itFoundPair.second) { DependFile df{filename, false}; @@ -129,7 +131,8 @@ public: m_filenameList.insert(df); } } - void addTgtDepend(const string& filename) { + void addTgtDepend(const string& filename) VL_MT_SAFE_EXCLUDES(m_mutex) { + const V3LockGuard lock{m_mutex}; const auto itFoundPair = m_filenameSet.insert(filename); if (itFoundPair.second) m_filenameList.insert(DependFile{filename, true}); } @@ -297,8 +300,8 @@ bool V3FileDependImp::checkTimes(const string& filename, const string& cmdlineIn //###################################################################### // V3File -void V3File::addSrcDepend(const string& filename) { dependImp.addSrcDepend(filename); } -void V3File::addTgtDepend(const string& filename) { dependImp.addTgtDepend(filename); } +void V3File::addSrcDepend(const string& filename) VL_MT_SAFE { dependImp.addSrcDepend(filename); } +void V3File::addTgtDepend(const string& filename) VL_MT_SAFE { dependImp.addTgtDepend(filename); } void V3File::writeDepend(const string& filename) { dependImp.writeDepend(filename); } std::vector V3File::getAllDeps() { return dependImp.getAllDeps(); } void V3File::writeTimes(const string& filename, const string& cmdlineIn) { @@ -581,14 +584,14 @@ protected: return true; } static size_t listSize(const StrList& sl) { - size_t out = 0; - for (const string& i : sl) out += i.length(); - return out; + size_t result = 0; + for (const string& i : sl) result += i.length(); + return result; } static string listString(const StrList& sl) { - string out; - for (const string& i : sl) out += i; - return out; + string result; + for (const string& i : sl) result += i; + return result; } // CONSTRUCTORS explicit VInFilterImp(const string& command) { start(command); } @@ -853,7 +856,8 @@ void V3OutFormatter::putcNoTracking(char chr) { putcOutput(chr); } -string V3OutFormatter::quoteNameControls(const string& namein, V3OutFormatter::Language lang) { +string V3OutFormatter::quoteNameControls(const string& namein, + V3OutFormatter::Language lang) VL_PURE { // Encode control chars into output-appropriate escapes // Reverse is V3Parse::deQuote string out; @@ -933,7 +937,7 @@ V3OutFile::~V3OutFile() { void V3OutFile::putsForceIncs() { const V3StringList& forceIncs = v3Global.opt.forceIncs(); - for (const string& i : forceIncs) { puts("#include \"" + i + "\"\n"); } + for (const string& i : forceIncs) puts("#include \"" + i + "\"\n"); } void V3OutCFile::putsGuard() { @@ -953,12 +957,13 @@ void V3OutCFile::putsGuard() { class VIdProtectImp final { // MEMBERS + V3Mutex m_mutex; // Protects members std::map m_nameMap; // Map of old name into new name - std::unordered_set m_newIdSet; // Which new names exist + std::unordered_set m_newIdSet VL_GUARDED_BY(m_mutex); // Which new names exist protected: // CONSTRUCTORS friend class VIdProtect; - static VIdProtectImp& singleton() { + static VIdProtectImp& singleton() VL_MT_SAFE { static VIdProtectImp s; return s; } @@ -972,8 +977,9 @@ public: } ~VIdProtectImp() = default; // METHODS - string passthru(const string& old) { + string passthru(const string& old) VL_MT_SAFE_EXCLUDES(m_mutex) { if (!v3Global.opt.protectIds()) return old; + const V3LockGuard lock{m_mutex}; const auto it = m_nameMap.find(old); if (it != m_nameMap.end()) { // No way to go back and correct the older crypt name @@ -985,8 +991,9 @@ public: } return old; } - string protectIf(const string& old, bool doIt) { + string protectIf(const string& old, bool doIt) VL_MT_SAFE_EXCLUDES(m_mutex) { if (!v3Global.opt.protectIds() || old.empty() || !doIt) return old; + const V3LockGuard lock{m_mutex}; const auto it = m_nameMap.find(old); if (it != m_nameMap.end()) { return it->second; @@ -1016,7 +1023,7 @@ public: return out; } } - string protectWordsIf(const string& old, bool doIt) { + string protectWordsIf(const string& old, bool doIt) VL_MT_SAFE { // Split at " " (for traces), "." (for scopes), "->", "(", "&", ")" (for self pointers) if (!(doIt && v3Global.opt.protectIds())) return old; string out; @@ -1055,7 +1062,7 @@ public: private: void trySep(const string& old, string::size_type start, const string& trySep, - string::size_type& posr, string& separatorr) { + string::size_type& posr, string& separatorr) VL_PURE { const string::size_type trypos = old.find(trySep, start); if (trypos != string::npos) { if (posr == string::npos || (posr > trypos)) { @@ -1066,10 +1073,10 @@ private: } }; -string VIdProtect::protectIf(const string& old, bool doIt) { +string VIdProtect::protectIf(const string& old, bool doIt) VL_MT_SAFE { return VIdProtectImp::singleton().protectIf(old, doIt); } -string VIdProtect::protectWordsIf(const string& old, bool doIt) { +string VIdProtect::protectWordsIf(const string& old, bool doIt) VL_MT_SAFE { return VIdProtectImp::singleton().protectWordsIf(old, doIt); } void VIdProtect::writeMapFile(const string& filename) { diff --git a/src/V3File.h b/src/V3File.h index fcee430f8..3f1cbebc2 100644 --- a/src/V3File.h +++ b/src/V3File.h @@ -39,7 +39,7 @@ public: addSrcDepend(filename); return new_ifstream_nodepend(filename); } - static std::ifstream* new_ifstream_nodepend(const string& filename) { + static std::ifstream* new_ifstream_nodepend(const string& filename) VL_MT_SAFE { return new std::ifstream{filename.c_str()}; } static std::ofstream* new_ofstream(const string& filename, bool append = false) { @@ -61,8 +61,8 @@ public: } // Dependencies - static void addSrcDepend(const string& filename); - static void addTgtDepend(const string& filename); + static void addSrcDepend(const string& filename) VL_MT_SAFE; + static void addTgtDepend(const string& filename) VL_MT_SAFE; static void writeDepend(const string& filename); static std::vector getAllDeps(); static void writeTimes(const string& filename, const string& cmdlineIn); @@ -171,7 +171,7 @@ public: // STATIC METHODS static string indentSpaces(int num); // Add escaped characters to strings - static string quoteNameControls(const string& namein, Language lang = LA_C); + static string quoteNameControls(const string& namein, Language lang = LA_C) VL_PURE; static bool tokenMatch(const char* cp, const char* cmp); static bool tokenNotStart(const char* cp); // Import/export meaning no endfunction static bool tokenStart(const char* cp); @@ -318,10 +318,10 @@ class VIdProtect final { public: // METHODS // Rename to a new encoded string (unless earlier passthru'ed) - static string protect(const string& old) { return protectIf(old, true); } - static string protectIf(const string& old, bool doIt = true); + static string protect(const string& old) VL_MT_SAFE { return protectIf(old, true); } + static string protectIf(const string& old, bool doIt = true) VL_MT_SAFE; // Rename words to a new encoded string - static string protectWordsIf(const string& old, bool doIt = true); + static string protectWordsIf(const string& old, bool doIt = true) VL_MT_SAFE; // Write map of renames to output file static void writeMapFile(const string& filename); }; diff --git a/src/V3FileLine.cpp b/src/V3FileLine.cpp index 80c2d1a85..06421f12a 100644 --- a/src/V3FileLine.cpp +++ b/src/V3FileLine.cpp @@ -39,7 +39,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### // FileLineSingleton class functions -string FileLineSingleton::filenameLetters(fileNameIdx_t fileno) { +string FileLineSingleton::filenameLetters(fileNameIdx_t fileno) VL_PURE { constexpr int size = 1 + (64 / 4); // Each letter retires more than 4 bits of a > 64 bit number char out[size]; @@ -87,7 +87,9 @@ void FileLineSingleton::fileNameNumMapDumpXml(std::ostream& os) { os << "\n"; } -FileLineSingleton::msgEnSetIdx_t FileLineSingleton::addMsgEnBitSet(const MsgEnBitSet& bitSet) { +FileLineSingleton::msgEnSetIdx_t FileLineSingleton::addMsgEnBitSet(const MsgEnBitSet& bitSet) + VL_MT_SAFE_EXCLUDES(m_mutex) { + V3LockGuard lock{m_mutex}; const auto pair = m_internedMsgEnIdxs.emplace(bitSet, 0); msgEnSetIdx_t& idx = pair.first->second; if (pair.second) { @@ -100,7 +102,7 @@ FileLineSingleton::msgEnSetIdx_t FileLineSingleton::addMsgEnBitSet(const MsgEnBi return idx; } -FileLineSingleton::msgEnSetIdx_t FileLineSingleton::defaultMsgEnIndex() { +FileLineSingleton::msgEnSetIdx_t FileLineSingleton::defaultMsgEnIndex() VL_MT_SAFE { MsgEnBitSet msgEnBitSet; for (int i = V3ErrorCode::EC_MIN; i < V3ErrorCode::_ENUM_MAX; ++i) { msgEnBitSet.set(i, !V3ErrorCode{i}.defaultsOff()); @@ -416,7 +418,7 @@ string FileLine::warnOther() const VL_REQUIRES(V3Error::s().m_mutex) { } }; string FileLine::warnOtherStandalone() const VL_EXCLUDES(V3Error::s().m_mutex) VL_MT_UNSAFE { - const VerilatedLockGuard guard{V3Error::s().m_mutex}; + const V3RecursiveLockGuard guard{V3Error::s().m_mutex}; return warnOther(); } @@ -465,13 +467,13 @@ string FileLine::warnContext() const { } string FileLine::warnContextParent() const VL_REQUIRES(V3Error::s().m_mutex) { - string out; + string result; for (FileLine* parentFl = parent(); parentFl; parentFl = parentFl->parent()) { if (parentFl->filenameIsGlobal()) break; - out += parentFl->warnOther() + "... note: In file included from " - + parentFl->filebasename() + "\n"; + result += parentFl->warnOther() + "... note: In file included from " + + parentFl->filebasename() + "\n"; } - return out; + return result; } #ifdef VL_LEAK_CHECKS std::unordered_set fileLineLeakChecks; diff --git a/src/V3FileLine.h b/src/V3FileLine.h index 42c72f25b..8f1cf140b 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -22,6 +22,7 @@ #include "V3Error.h" #include "V3LangCode.h" +#include "V3Mutex.h" #include #include @@ -50,12 +51,13 @@ class FileLineSingleton final { using MsgEnBitSet = std::bitset; // MEMBERS + V3Mutex m_mutex; // protects members std::map m_namemap; // filenameno for each filename std::deque m_names; // filename text for each filenameno std::deque m_languages; // language for each filenameno // Map from flag set to the index in m_internedMsgEns for interning - std::unordered_map m_internedMsgEnIdxs; + std::unordered_map m_internedMsgEnIdxs VL_GUARDED_BY(m_mutex); // Interned message enablement flag sets std::vector m_internedMsgEns; @@ -64,7 +66,7 @@ class FileLineSingleton final { ~FileLineSingleton() = default; fileNameIdx_t nameToNumber(const string& filename); - string numberToName(fileNameIdx_t filenameno) const { return m_names[filenameno]; } + string numberToName(fileNameIdx_t filenameno) const VL_MT_SAFE { return m_names[filenameno]; } V3LangCode numberToLang(fileNameIdx_t filenameno) const { return m_languages[filenameno]; } void numberToLang(fileNameIdx_t filenameno, const V3LangCode& l) { m_languages[filenameno] = l; @@ -75,12 +77,12 @@ class FileLineSingleton final { m_languages.clear(); } void fileNameNumMapDumpXml(std::ostream& os); - static string filenameLetters(fileNameIdx_t fileno); + static string filenameLetters(fileNameIdx_t fileno) VL_PURE; // Add given bitset to the interned bitsets, return interned index - msgEnSetIdx_t addMsgEnBitSet(const MsgEnBitSet& bitSet); + msgEnSetIdx_t addMsgEnBitSet(const MsgEnBitSet& bitSet) VL_MT_SAFE_EXCLUDES(m_mutex); // Add index of default bitset - msgEnSetIdx_t defaultMsgEnIndex(); + msgEnSetIdx_t defaultMsgEnIndex() VL_MT_SAFE; // Set bitIdx to value in bitset at interned index setIdx, return interned index of result msgEnSetIdx_t msgEnSetBit(msgEnSetIdx_t setIdx, size_t bitIdx, bool value); // Return index to intersection set @@ -159,11 +161,11 @@ protected: private: // CONSTRUCTORS - static FileLineSingleton& singleton() { + static FileLineSingleton& singleton() VL_MT_SAFE { static FileLineSingleton s; return s; } - static FileLine& defaultFileLine() { + static FileLine& defaultFileLine() VL_MT_SAFE { static FileLine s; return s; } @@ -242,7 +244,7 @@ public: string prettySource() const VL_MT_SAFE; // Source, w/stripped unprintables and newlines FileLine* parent() const VL_MT_SAFE { return m_parent; } V3LangCode language() const { return singleton().numberToLang(filenameno()); } - string ascii() const; + string ascii() const VL_MT_SAFE; string asciiLineCol() const; int filenameno() const VL_MT_SAFE { return m_filenameno; } string filename() const VL_MT_SAFE { return singleton().numberToName(filenameno()); } @@ -270,7 +272,7 @@ public: } void warnOff(V3ErrorCode code, bool flag) { warnOn(code, !flag); } bool warnOff(const string& msg, bool flag); // Returns 1 if ok - bool warnIsOff(V3ErrorCode code) const; + bool warnIsOff(V3ErrorCode code) const VL_MT_SAFE; void warnLintOff(bool flag); void warnStyleOff(bool flag); void warnUnusedOff(bool flag); diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp index 3b450856f..640cade21 100644 --- a/src/V3Gate.cpp +++ b/src/V3Gate.cpp @@ -76,7 +76,7 @@ public: ~GateEitherVertex() override = default; // ACCESSORS string dotStyle() const override { return m_consumed ? "" : "dotted"; } - AstScope* scopep() const { return m_scopep; } + AstScope* scopep() const VL_MT_STABLE { return m_scopep; } bool reducible() const { return m_reducible; } bool dedupable() const { return m_dedupable; } void setConsumed(const char* /*consumedReason*/) { @@ -133,8 +133,10 @@ public: , m_varScp{varScp} {} ~GateVarVertex() override = default; // ACCESSORS - AstVarScope* varScp() const { return m_varScp; } - string name() const override { return (cvtToHex(m_varScp) + " " + varScp()->name()); } + AstVarScope* varScp() const VL_MT_STABLE { return m_varScp; } + string name() const override VL_MT_STABLE { + return (cvtToHex(m_varScp) + " " + varScp()->name()); + } string dotColor() const override { return "blue"; } bool isTop() const { return m_isTop; } void setIsTop() { m_isTop = true; } @@ -174,7 +176,9 @@ public: , m_slow{slow} {} ~GateLogicVertex() override = default; // ACCESSORS - string name() const override { return (cvtToHex(m_nodep) + "@" + scopep()->prettyName()); } + string name() const override VL_MT_STABLE { + return (cvtToHex(m_nodep) + "@" + scopep()->prettyName()); + } string dotColor() const override { return "purple"; } FileLine* fileline() const override { return nodep()->fileline(); } AstNode* nodep() const { return m_nodep; } @@ -188,7 +192,7 @@ public: // ###################################################################### // Is this a simple expression with a single input and single output? -class GateOkVisitor final : public VNVisitor { +class GateOkVisitor final : public VNVisitorConst { private: // RETURN STATE bool m_isSimple = true; // Set false when we know it isn't simple @@ -211,7 +215,7 @@ private: // VISITORS void visit(AstNodeVarRef* nodep) override { ++m_ops; - iterateChildren(nodep); + iterateChildrenConst(nodep); // We only allow a LHS ref for the var being set, and a RHS ref for // something else being read. if (nodep->varScopep()->varp()->isSc()) { @@ -242,7 +246,7 @@ private: } else if (nodep->isTimingControl()) { clearSimple("Timing control"); } else { - iterateChildren(nodep); + iterateChildrenConst(nodep); } // We don't push logic other then assignments/NOTs into SenItems // This avoids a mess in computing what exactly a POSEDGE is @@ -267,7 +271,7 @@ private: UINFO(5, "Non optimizable type: " << nodep << endl); clearSimple("Non optimizable type"); } else { - iterateChildren(nodep); + iterateChildrenConst(nodep); } } @@ -277,7 +281,7 @@ public: m_buffersOnly = buffersOnly; m_dedupe = dedupe; // Iterate - iterate(nodep); + iterateConst(nodep); // Check results if (!m_substTreep) clearSimple("No assignment found\n"); for (GateVarRefList::const_iterator it = m_rhsVarRefs.begin(); it != m_rhsVarRefs.end(); @@ -1288,7 +1292,7 @@ void GateVisitor::mergeAssigns() { //###################################################################### // Find a var's offset in a concatenation -class GateConcatVisitor final : public VNVisitor { +class GateConcatVisitor final : public VNVisitorConst { private: // STATE const AstVarScope* m_vscp = nullptr; // Varscope we're trying to find @@ -1311,11 +1315,11 @@ private: } void visit(AstConcat* nodep) override { UINFO(9, "CLK DECOMP Concat search (off = " << m_offset << ") - " << nodep << endl); - iterate(nodep->rhsp()); - iterate(nodep->lhsp()); + iterateConst(nodep->rhsp()); + iterateConst(nodep->lhsp()); } //-------------------- - void visit(AstNode* nodep) override { iterateChildren(nodep); } + void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } public: // CONSTRUCTORS @@ -1327,7 +1331,7 @@ public: m_offset = 0; m_found = false; // Iterate - iterate(concatp); + iterateConst(concatp); UINFO(9, "CLK DECOMP Concat Offset (found = " << m_found << ") (" << m_found_offset << ") - " << concatp << " : " << vscp << endl); diff --git a/src/V3Global.cpp b/src/V3Global.cpp index 81f24ade4..82dd6e584 100644 --- a/src/V3Global.cpp +++ b/src/V3Global.cpp @@ -52,7 +52,7 @@ void V3Global::readFiles() { VInFilter filter{v3Global.opt.pipeFilter()}; V3ParseSym parseSyms{v3Global.rootp()}; // Symbol table must be common across all parsing - V3Parse parser(v3Global.rootp(), &filter, &parseSyms); + V3Parse parser{v3Global.rootp(), &filter, &parseSyms}; // Parse the std package if (v3Global.opt.std()) { @@ -77,14 +77,6 @@ void V3Global::readFiles() { "Cannot find file containing library module: "); } - // Delete the std package if unused - if (!usesStdPackage()) { - if (AstNodeModule* stdp = v3Global.rootp()->stdPackagep()) { - v3Global.rootp()->stdPackagep(nullptr); - VL_DO_DANGLING(stdp->unlinkFrBack()->deleteTree(), stdp); - } - } - // v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("parse.tree")); V3Error::abortIfErrors(); @@ -94,6 +86,16 @@ void V3Global::readFiles() { } } +void V3Global::removeStd() { + // Delete the std package if unused + if (!usesStdPackage()) { + if (AstNodeModule* stdp = v3Global.rootp()->stdPackagep()) { + v3Global.rootp()->stdPackagep(nullptr); + VL_DO_DANGLING(stdp->unlinkFrBack()->deleteTree(), stdp); + } + } +} + string V3Global::debugFilename(const string& nameComment, int newNumber) { ++m_debugFileNumber; if (newNumber) m_debugFileNumber = newNumber; diff --git a/src/V3Global.h b/src/V3Global.h index 228440e3a..fcb4b7e20 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -98,7 +98,7 @@ class V3Global final { VWidthMinUsage m_widthMinUsage = VWidthMinUsage::LINT_WIDTH; // What AstNode::widthMin() is used for - int m_debugFileNumber = 0; // Number to append to debug files created + std::atomic_int m_debugFileNumber{0}; // Number to append to debug files created bool m_assertDTypesResolved = false; // Tree should have dtypep()'s bool m_assertScoped = false; // Tree is scoped bool m_constRemoveXs = false; // Const needs to strip any Xs @@ -135,6 +135,7 @@ public: // METHODS void readFiles(); + void removeStd(); void checkTree() const; static void dumpCheckGlobalTree(const string& stagename, int newNumber = 0, bool doDump = true); diff --git a/src/V3GraphTest.cpp b/src/V3GraphTest.cpp index cb48888a6..8a9637cdf 100644 --- a/src/V3GraphTest.cpp +++ b/src/V3GraphTest.cpp @@ -60,7 +60,7 @@ public: , m_name{name} {} ~V3GraphTestVertex() override = default; // ACCESSORS - string name() const override { return m_name; } + string name() const override VL_MT_STABLE { return m_name; } }; class V3GraphTestVarVertex final : public V3GraphTestVertex { diff --git a/src/V3Hash.cpp b/src/V3Hash.cpp index 5fd2d2751..bff44cdf1 100644 --- a/src/V3Hash.cpp +++ b/src/V3Hash.cpp @@ -24,7 +24,7 @@ V3Hash::V3Hash(const std::string& val) : m_value{static_cast(std::hash{}(val))} {} -std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) { +std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) VL_MT_SAFE { return os << 'h' << std::hex << std::setw(8) << std::setfill('0') << rhs.value(); } diff --git a/src/V3Hash.h b/src/V3Hash.h index 7cdfb8749..7e75045ef 100644 --- a/src/V3Hash.h +++ b/src/V3Hash.h @@ -69,7 +69,7 @@ public: } }; -std::ostream& operator<<(std::ostream& os, const V3Hash& rhs); +std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) VL_MT_SAFE; template <> struct std::hash { diff --git a/src/V3Hasher.cpp b/src/V3Hasher.cpp index 0dfdee7b3..c68f1167e 100644 --- a/src/V3Hasher.cpp +++ b/src/V3Hasher.cpp @@ -26,7 +26,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### // Visitor that computes node hashes -class HasherVisitor final : public VNVisitor { +class HasherVisitor final : public VNVisitorConst { private: // NODE STATE // AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal) @@ -48,7 +48,8 @@ private: // Reset accumulator m_hash = V3Hash{nodep->type()}; // Node type f(); // Node specific hash - if (hashDType && nodep != nodep->dtypep()) iterateNull(nodep->dtypep()); // Node dtype + if (hashDType && nodep != nodep->dtypep()) + iterateConstNull(nodep->dtypep()); // Node dtype if (hashChildren) iterateChildrenConst(nodep); // Children if (m_cacheInUser4) nodep->user4(m_hash.value()); return m_hash; @@ -92,7 +93,7 @@ private: // AstNodeDType void visit(AstNodeArrayDType* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { - iterateNull(nodep->virtRefDTypep()); + iterateConstNull(nodep->virtRefDTypep()); m_hash += nodep->left(); m_hash += nodep->right(); }); @@ -120,23 +121,23 @@ private: } void visit(AstAssocArrayDType* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { - iterateNull(nodep->virtRefDTypep()); - iterateNull(nodep->virtRefDType2p()); + iterateConstNull(nodep->virtRefDTypep()); + iterateConstNull(nodep->virtRefDType2p()); }); } void visit(AstDynArrayDType* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { // - iterateNull(nodep->virtRefDTypep()); + iterateConstNull(nodep->virtRefDTypep()); }); } void visit(AstUnsizedArrayDType* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { // - iterateNull(nodep->virtRefDTypep()); + iterateConstNull(nodep->virtRefDTypep()); }); } void visit(AstWildcardArrayDType* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { // - iterateNull(nodep->virtRefDTypep()); + iterateConstNull(nodep->virtRefDTypep()); }); } void visit(AstBasicDType* nodep) override { @@ -148,29 +149,29 @@ private: } void visit(AstConstDType* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { // - iterateNull(nodep->virtRefDTypep()); + iterateConstNull(nodep->virtRefDTypep()); }); } void visit(AstClassRefDType* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { // - iterateNull(nodep->classp()); + iterateConstNull(nodep->classp()); }); } void visit(AstIfaceRefDType* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { // - iterateNull(nodep->cellp()); + iterateConstNull(nodep->cellp()); }); } void visit(AstQueueDType* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { // - iterateNull(nodep->virtRefDTypep()); + iterateConstNull(nodep->virtRefDTypep()); }); } void visit(AstRefDType* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { m_hash += nodep->name(); - iterateNull(nodep->typedefp()); - iterateNull(nodep->refDTypep()); + iterateConstNull(nodep->typedefp()); + iterateConstNull(nodep->refDTypep()); }); } void visit(AstVoidDType* nodep) override { @@ -203,16 +204,16 @@ private: void visit(AstVarRef* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { if (nodep->varScopep()) { - iterateNull(nodep->varScopep()); + iterateConstNull(nodep->varScopep()); } else { - iterateNull(nodep->varp()); + iterateConstNull(nodep->varp()); m_hash += nodep->selfPointer(); } }); } void visit(AstVarXRef* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { - iterateNull(nodep->varp()); + iterateConstNull(nodep->varp()); m_hash += nodep->dotted(); }); } @@ -233,7 +234,7 @@ private: } void visit(AstAddrOfCFunc* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { // - iterateNull(nodep->funcp()); + iterateConstNull(nodep->funcp()); }); } @@ -249,13 +250,13 @@ private: } void visit(AstNodeCCall* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { // - iterateNull(nodep->funcp()); + iterateConstNull(nodep->funcp()); }); } void visit(AstNodeFTaskRef* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { - iterateNull(nodep->taskp()); - iterateNull(nodep->classOrPackagep()); + iterateConstNull(nodep->taskp()); + iterateConstNull(nodep->classOrPackagep()); }); } void visit(AstCMethodHard* nodep) override { @@ -265,12 +266,12 @@ private: } void visit(AstCAwait* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { // - iterateNull(nodep->sensesp()); + iterateConstNull(nodep->sensesp()); }); } void visit(AstCoverInc* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { // - iterateNull(nodep->declp()); + iterateConstNull(nodep->declp()); }); } void visit(AstDisplay* nodep) override { @@ -285,12 +286,12 @@ private: } void visit(AstJumpGo* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { // - iterateNull(nodep->labelp()); + iterateConstNull(nodep->labelp()); }); } void visit(AstTraceInc* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { // - iterateNull(nodep->declp()); + iterateConstNull(nodep->declp()); }); } void visit(AstNodeCoverOrAssert* nodep) override { @@ -332,7 +333,7 @@ private: } void visit(AstClassOrPackageRef* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { // - iterateNull(nodep->classOrPackageNodep()); + iterateConstNull(nodep->classOrPackageNodep()); }); } void visit(AstSenItem* nodep) override { @@ -376,7 +377,7 @@ private: if (dtypep) { const uint32_t size = dtypep->elementsConst(); for (uint32_t n = 0; n < size; ++n) { // - iterateNull(nodep->getIndexDefaultedValuep(n)); + iterateConstNull(nodep->getIndexDefaultedValuep(n)); } } }); @@ -417,13 +418,13 @@ private: void visit(AstScope* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, false, [=]() { m_hash += nodep->name(); - iterateNull(nodep->aboveScopep()); + iterateConstNull(nodep->aboveScopep()); }); } void visit(AstVarScope* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { - iterateNull(nodep->varp()); - iterateNull(nodep->scopep()); + iterateConstNull(nodep->varp()); + iterateConstNull(nodep->scopep()); }); } void visit(AstEnumItem* nodep) override { @@ -443,19 +444,19 @@ private: } void visit(AstActive* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { // - iterateNull(nodep->sensesp()); + iterateConstNull(nodep->sensesp()); }); } void visit(AstCell* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { m_hash += nodep->name(); - iterateNull(nodep->modp()); + iterateConstNull(nodep->modp()); }); } void visit(AstCellInline* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { m_hash += nodep->name(); - iterateNull(nodep->scopep()); + iterateConstNull(nodep->scopep()); }); } void visit(AstNodeFTask* nodep) override { @@ -471,13 +472,13 @@ private: void visit(AstModportVarRef* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { m_hash += nodep->name(); - iterateNull(nodep->varp()); + iterateConstNull(nodep->varp()); }); } void visit(AstModportFTaskRef* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { m_hash += nodep->name(); - iterateNull(nodep->ftaskp()); + iterateConstNull(nodep->ftaskp()); }); } void visit(AstMTaskBody* nodep) override { @@ -502,12 +503,12 @@ public: // CONSTRUCTORS explicit HasherVisitor(AstNode* nodep) : m_cacheInUser4{true} { - iterate(nodep); + iterateConst(nodep); } class Uncached {}; HasherVisitor(const AstNode* nodep, Uncached) : m_cacheInUser4{false} { - iterate(const_cast(nodep)); + iterateConst(const_cast(nodep)); } V3Hash finalHash() const { return m_hash; } ~HasherVisitor() override = default; diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index 78ea53c8f..061e5caeb 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -139,7 +139,7 @@ public: //###################################################################### -class InstDeModVarVisitor final : public VNVisitor { +class InstDeModVarVisitor final : public VNVisitorConst { // Expand all module variables, and save names for later reference private: // STATE @@ -151,10 +151,10 @@ private: UINFO(8, " dm-1-VAR " << nodep << endl); insert(nodep); } - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstNodeExpr*) override {} // Accelerate - void visit(AstNode* nodep) override { iterateChildren(nodep); } + void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } public: // METHODS @@ -182,7 +182,7 @@ public: void main(AstNodeModule* nodep) { UINFO(8, " dmMODULE " << nodep << endl); m_modVarNameMap.clear(); - iterate(nodep); + iterateConst(nodep); } }; @@ -252,7 +252,7 @@ private: // Make all of the required clones for (int i = 0; i < m_cellRangep->elementsConst(); i++) { m_instSelNum - = m_cellRangep->littleEndian() ? (m_cellRangep->elementsConst() - 1 - i) : i; + = m_cellRangep->ascending() ? (m_cellRangep->elementsConst() - 1 - i) : i; const int instNum = m_cellRangep->loConst() + i; AstCell* const newp = nodep->cloneTree(false); @@ -331,7 +331,7 @@ private: // Connection to array, where array dimensions match the instant dimension const AstRange* const rangep = VN_AS(nodep->exprp()->dtypep(), UnpackArrayDType)->rangep(); - const int arraySelNum = rangep->littleEndian() + const int arraySelNum = rangep->ascending() ? (rangep->elementsConst() - 1 - m_instSelNum) : m_instSelNum; AstNodeExpr* exprp = VN_AS(nodep->exprp(), NodeExpr)->unlinkFrBack(); @@ -342,11 +342,11 @@ private: } else if (expwidth == modwidth * m_cellRangep->elementsConst()) { // Arrayed instants: one bit for each of the instants (each // assign is 1 modwidth wide) - if (m_cellRangep->littleEndian()) { - nodep->exprp()->v3warn(LITENDIAN, "Big endian instance range connecting to " - "vector: left < right of instance range: [" - << m_cellRangep->leftConst() << ":" - << m_cellRangep->rightConst() << "]"); + if (m_cellRangep->ascending()) { + nodep->exprp()->v3warn(ASCRANGE, "Ascending instance range connecting to " + "vector: left < right of instance range: [" + << m_cellRangep->leftConst() << ":" + << m_cellRangep->rightConst() << "]"); } AstNodeExpr* exprp = VN_AS(nodep->exprp(), NodeExpr)->unlinkFrBack(); const bool inputPin = nodep->modVarp()->isNonOutput(); @@ -555,7 +555,7 @@ public: V3Inst::checkOutputShort(pinp); AstNodeExpr* const pinexprp = VN_AS(pinp->exprp(), NodeExpr)->unlinkFrBack(); const string newvarname - = (string(pinVarp->isWritable() ? "__Vcellout" : "__Vcellinp") + = (string{pinVarp->isWritable() ? "__Vcellout" : "__Vcellinp"} // Prevent name conflict if both tri & non-tri add signals + (forTristate ? "t" : "") + "__" + cellp->name() + "__" + pinp->name()); AstVar* const newvarp diff --git a/src/V3InstrCount.cpp b/src/V3InstrCount.cpp index 96d19aa90..68574f6c9 100644 --- a/src/V3InstrCount.cpp +++ b/src/V3InstrCount.cpp @@ -32,7 +32,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; /// we'll count instructions from either the 'if' or the 'else' branch, /// whichever is larger. We know we won't run both. -class InstrCountVisitor final : public VNVisitor { +class InstrCountVisitor final : public VNVisitorConst { private: // NODE STATE // AstNode::user4() -> int. Path cost + 1, 0 means don't dump @@ -76,7 +76,7 @@ public: : m_startNodep{nodep} , m_assertNoDups{assertNoDups} , m_osp{osp} { - if (nodep) iterate(nodep); + if (nodep) iterateConst(nodep); } ~InstrCountVisitor() override = default; @@ -135,7 +135,7 @@ private: // Hence, exclude the child of the AstWordSel from the computation, // whose cost scales with the size of the entire (maybe large) vector. const VisitBase vb{this, nodep}; - iterateAndNextNull(nodep->bitp()); + iterateAndNextConstNull(nodep->bitp()); } void visit(AstSel* nodep) override { if (m_ignoreRemaining) return; @@ -144,8 +144,8 @@ private: // its width) and the cost of the lsbp() and widthp() nodes, but not // the fromp() node which could be disproportionately large. const VisitBase vb{this, nodep}; - iterateAndNextNull(nodep->lsbp()); - iterateAndNextNull(nodep->widthp()); + iterateAndNextConstNull(nodep->lsbp()); + iterateAndNextConstNull(nodep->widthp()); } void visit(AstSliceSel* nodep) override { // LCOV_EXCL_LINE nodep->v3fatalSrc("AstSliceSel unhandled"); @@ -177,18 +177,18 @@ private: void visit(AstNodeIf* nodep) override { if (m_ignoreRemaining) return; const VisitBase vb{this, nodep}; - iterateAndNextNull(nodep->condp()); + iterateAndNextConstNull(nodep->condp()); const uint32_t savedCount = m_instrCount; UINFO(8, "thensp:\n"); reset(); - iterateAndNextNull(nodep->thensp()); + iterateAndNextConstNull(nodep->thensp()); uint32_t ifCount = m_instrCount; if (nodep->branchPred().unlikely()) ifCount = 0; UINFO(8, "elsesp:\n"); reset(); - iterateAndNextNull(nodep->elsesp()); + iterateAndNextConstNull(nodep->elsesp()); uint32_t elseCount = m_instrCount; if (nodep->branchPred().likely()) elseCount = 0; @@ -206,17 +206,17 @@ private: // Just like if/else above, the ternary operator only evaluates // one of the two expressions, so only count the max. const VisitBase vb{this, nodep}; - iterateAndNextNull(nodep->condp()); + iterateAndNextConstNull(nodep->condp()); const uint32_t savedCount = m_instrCount; UINFO(8, "?\n"); reset(); - iterateAndNextNull(nodep->thenp()); + iterateAndNextConstNull(nodep->thenp()); const uint32_t ifCount = m_instrCount; UINFO(8, ":\n"); reset(); - iterateAndNextNull(nodep->elsep()); + iterateAndNextConstNull(nodep->elsep()); const uint32_t elseCount = m_instrCount; reset(); @@ -230,7 +230,7 @@ private: } void visit(AstCAwait* nodep) override { if (m_ignoreRemaining) return; - iterateChildren(nodep); + iterateChildrenConst(nodep); // Anything past a co_await is irrelevant m_ignoreRemaining = true; } @@ -241,7 +241,7 @@ private: // Sum counts in each statement until the first await for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { reset(); - iterate(stmtp); + iterateConst(stmtp); totalCount += m_instrCount; } m_instrCount = totalCount; @@ -266,9 +266,9 @@ private: void visit(AstNodeCCall* nodep) override { if (m_ignoreRemaining) return; const VisitBase vb{this, nodep}; - iterateChildren(nodep); + iterateChildrenConst(nodep); m_tracingCall = true; - iterate(nodep->funcp()); + iterateConst(nodep->funcp()); UASSERT_OBJ(!m_tracingCall, nodep, "visit(AstCFunc) should have cleared m_tracingCall."); } void visit(AstCFunc* nodep) override { @@ -282,21 +282,21 @@ private: { m_inCFunc = true; const VisitBase vb{this, nodep}; - iterateChildren(nodep); + iterateChildrenConst(nodep); } m_ignoreRemaining = false; } void visit(AstNode* nodep) override { if (m_ignoreRemaining) return; const VisitBase vb{this, nodep}; - iterateChildren(nodep); + iterateChildrenConst(nodep); } VL_UNCOPYABLE(InstrCountVisitor); }; // Iterate the graph printing the critical path marked by previous visitation -class InstrCountDumpVisitor final : public VNVisitor { +class InstrCountDumpVisitor final : public VNVisitorConst { private: // NODE STATE // AstNode::user4() -> int. Path cost, 0 means don't dump @@ -311,7 +311,7 @@ public: : m_osp{osp} { // No check for nullptr output, so... UASSERT_OBJ(osp, nodep, "Don't call if not dumping"); - if (nodep) iterate(nodep); + if (nodep) iterateConst(nodep); } ~InstrCountDumpVisitor() override = default; @@ -323,7 +323,7 @@ private: if (unsigned costPlus1 = nodep->user4()) { *m_osp << " " << indent() << "cost " << std::setw(6) << std::left << (costPlus1 - 1) << " " << nodep << '\n'; - iterateChildren(nodep); + iterateChildrenConst(nodep); } --m_depth; } diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index 20f5e545c..f30bb9601 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -59,8 +59,8 @@ public: : V3GraphVertex{graphp} , m_modp{modp} {} ~LinkCellsVertex() override = default; - AstNodeModule* modp() const { return m_modp; } - string name() const override { return modp()->name(); } + AstNodeModule* modp() const VL_MT_STABLE { return m_modp; } + string name() const override VL_MT_STABLE { return modp()->name(); } FileLine* fileline() const override { return modp()->fileline(); } // Recursive modules get space for maximum recursion uint32_t rankAdder() const override { @@ -73,7 +73,7 @@ public: explicit LibraryVertex(V3Graph* graphp) : V3GraphVertex{graphp} {} ~LibraryVertex() override = default; - string name() const override { return "*LIBRARY*"; } + string name() const override VL_MT_STABLE { return "*LIBRARY*"; } }; void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp) { diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index db591012f..498763fac 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -161,7 +161,6 @@ private: bool m_forPrimary; // First link bool m_forPrearray; // Compress cell__[array] refs bool m_forScopeCreation; // Remove VarXRefs for V3Scope - bool m_removeVoidParamedClasses; // Remove classes with void params public: // METHODS @@ -208,7 +207,6 @@ public: m_forPrimary = (step == LDS_PRIMARY); m_forPrearray = (step == LDS_PARAMED || step == LDS_PRIMARY); m_forScopeCreation = (step == LDS_SCOPED); - m_removeVoidParamedClasses = (step == LDS_PARAMED); s_errorThisp = this; V3Error::errorExitCb(preErrorDumpHandler); // If get error, dump self } @@ -222,7 +220,6 @@ public: bool forPrimary() const { return m_forPrimary; } bool forPrearray() const { return m_forPrearray; } bool forScopeCreation() const { return m_forScopeCreation; } - bool removeVoidParamedClasses() const { return m_removeVoidParamedClasses; } // METHODS static string nodeTextType(AstNode* nodep) { @@ -780,7 +777,7 @@ class LinkDotFindVisitor final : public VNVisitor { // First back iterate, to find all packages. Backward as must do base // packages before using packages - iterateChildrenBackwards(nodep); + iterateChildrenBackwardsConst(nodep); // The first modules in the list are always the top modules // (sorted before this is called). @@ -916,18 +913,6 @@ class LinkDotFindVisitor final : public VNVisitor { void visit(AstClass* nodep) override { UASSERT_OBJ(m_curSymp, nodep, "Class not under module/package/$unit"); UINFO(8, " " << nodep << endl); - // Remove classes that have void params, as they were only used for the parameterization - // step and will not be instantiated - if (m_statep->removeVoidParamedClasses()) { - for (auto* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { - if (auto* dtypep = VN_CAST(stmtp, ParamTypeDType)) { - if (VN_IS(dtypep->subDTypep(), VoidDType)) { - VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); - return; - } - } - } - } VL_RESTORER(m_scope); VL_RESTORER(m_classOrPackagep); VL_RESTORER(m_modSymp); @@ -1709,6 +1694,13 @@ private: } iterateChildren(nodep); } + void visit(AstPull* nodep) override { + // Deal with implicit definitions + // We used to nodep->allowImplicit() here, but it turns out + // normal "assigns" can also make implicit wires. Yuk. + pinImplicitExprRecurse(nodep->lhsp()); + iterateChildren(nodep); + } void visit(AstTypedefFwd* nodep) override { VSymEnt* const foundp = m_statep->getNodeSym(nodep)->findIdFallback(nodep->name()); if (!foundp && v3Global.opt.pedantic() @@ -1749,7 +1741,7 @@ class LinkDotScopeVisitor final : public VNVisitor { // VISITs void visit(AstNetlist* nodep) override { // Recurse..., backward as must do packages before using packages - iterateChildrenBackwards(nodep); + iterateChildrenBackwardsConst(nodep); } void visit(AstConstPool*) override {} void visit(AstScope* nodep) override { @@ -2004,9 +1996,6 @@ private: // *::user4() -> See LinkDotState // Cleared on Cell // AstVar::user5() // bool. True if pin used in this cell - // AstClass::user5() // bool. True if class has a parameter - // as a (possibly indirect) base class. - // Used only in LDS_PRIMARY pass const VNUser3InUse m_inuser3; const VNUser5InUse m_inuser5; @@ -2030,6 +2019,9 @@ private: int m_modportNum = 0; // Uniqueify modport numbers bool m_inSens = false; // True if in senitem std::set m_ifClassImpNames; // Names imported from interface class + std::set m_extendsParam; // Classes that has a parameter as its super class + bool m_insideClassExtParam = false; // Inside a class that extends a parameter. + // It may be set only in linkDotPrimary. struct DotStates { DotPosition m_dotPos; // Scope part of dotted resolution @@ -2227,11 +2219,17 @@ private: } } } + void importSymbolsFromExtended(AstClass* const nodep, AstClassExtends* const cextp) { + AstClass* const classp = cextp->classp(); + VSymEnt* const srcp = m_statep->getNodeSym(classp); + if (classp->isInterfaceClass()) importImplementsClass(nodep, srcp, classp); + if (!cextp->isImplements()) m_curSymp->importFromClass(m_statep->symsp(), srcp); + } // VISITs void visit(AstNetlist* nodep) override { // Recurse..., backward as must do packages before using packages - iterateChildrenBackwards(nodep); + iterateChildrenBackwardsConst(nodep); } void visit(AstTypeTable*) override {} void visit(AstConstPool*) override {} @@ -2393,12 +2391,17 @@ private: nodep->v3error("'super' used on non-extended class (IEEE 1800-2017 8.15)"); m_ds.m_dotErr = true; } else { - const auto cextp = classp->extendsp(); - UASSERT_OBJ(cextp, nodep, "Bad super extends link"); - const auto sclassp = cextp->classp(); - UASSERT_OBJ(sclassp, nodep, "Bad superclass"); - m_ds.m_dotSymp = m_statep->getNodeSym(sclassp); - UINFO(8, " super. " << m_ds.ascii() << endl); + if (m_statep->forPrimary() + && m_extendsParam.find(classp) != m_extendsParam.end()) { + m_ds.m_unresolvedClass = true; + } else { + const auto cextp = classp->extendsp(); + UASSERT_OBJ(cextp, nodep, "Bad super extends link"); + const auto sclassp = cextp->classp(); + UASSERT_OBJ(sclassp, nodep, "Bad superclass"); + m_ds.m_dotSymp = m_statep->getNodeSym(sclassp); + UINFO(8, " super. " << m_ds.ascii() << endl); + } } } } else if (VN_IS(nodep->lhsp(), ClassOrPackageRef)) { @@ -2527,12 +2530,14 @@ private: string expectWhat; bool allowScope = false; bool allowVar = false; + bool staticAccess = false; if (m_ds.m_dotPos == DP_PACKAGE) { // {package}::{a} AstNodeModule* classOrPackagep = nullptr; expectWhat = "scope/variable"; allowScope = true; allowVar = true; + staticAccess = true; UASSERT_OBJ(VN_IS(m_ds.m_dotp->lhsp(), ClassOrPackageRef), m_ds.m_dotp->lhsp(), "Bad package link"); AstClassOrPackageRef* const cpackagerefp @@ -2660,6 +2665,11 @@ private: newp = refp; } } else { + if (staticAccess && !varp->lifetime().isStatic() && !varp->isParam()) { + // TODO bug4077 + // nodep->v3error("Static access to non-static member variable " + // << varp->prettyNameQ() << endl); + } AstVarRef* const refp = new AstVarRef{ nodep->fileline(), varp, VAccess::READ}; // lvalue'ness computed later refp->classOrPackagep(foundp->classOrPackagep()); @@ -2745,11 +2755,14 @@ private: m_ds.m_dotSymp = foundp; ok = m_ds.m_dotPos == DP_SCOPE; } else if (const AstNodeFTask* const ftaskp = VN_CAST(foundp->nodep(), NodeFTask)) { - if (!ftaskp->isFunction()) { + + if (!ftaskp->isFunction() || ftaskp->classMethod()) { ok = m_ds.m_dotPos == DP_NONE; if (ok) { - // The condition is true for tasks, properties and void functions. + // The condition is true for tasks, + // properties and void functions. // In these cases, the parentheses may be skipped. + // Also SV class methods can be called without parens AstFuncRef* const funcRefp = new AstFuncRef{nodep->fileline(), nodep->name(), nullptr}; nodep->replaceWith(funcRefp); @@ -2757,8 +2770,10 @@ private: } } } - // - if (!ok) { + // Don't throw error ifthe reference is inside a class that extends a param, because + // some members can't be linked in such a case. m_insideClassExtParam may be true only + // in the first stage of linking. + if (!ok && !m_insideClassExtParam) { // Cells/interfaces can't be implicit const bool isCell = foundp ? VN_IS(foundp->nodep(), Cell) : false; const bool checkImplicit = (!m_ds.m_dotp && m_ds.m_dotText == "" && !isCell); @@ -2973,6 +2988,7 @@ private: iterateChildren(nodep); } + bool staticAccess = false; if (m_ds.m_unresolvedClass) { // Unable to link before V3Param return; @@ -2988,6 +3004,7 @@ private: } else if (m_ds.m_dotp && m_ds.m_dotPos == DP_PACKAGE) { UASSERT_OBJ(VN_IS(m_ds.m_dotp->lhsp(), ClassOrPackageRef), m_ds.m_dotp->lhsp(), "Bad package link"); + staticAccess = true; AstClassOrPackageRef* const cpackagerefp = VN_AS(m_ds.m_dotp->lhsp(), ClassOrPackageRef); if (cpackagerefp->name() == "local") { @@ -3059,9 +3076,18 @@ private: AstNodeFTask* const taskp = foundp ? VN_CAST(foundp->nodep(), NodeFTask) : nullptr; // Maybe nullptr if (taskp) { + if (staticAccess && !taskp->lifetime().isStatic()) { + // TODO bug4077 + // nodep->v3error("Static access to non-static task/function " + // << taskp->prettyNameQ() << endl); + } nodep->taskp(taskp); nodep->classOrPackagep(foundp->classOrPackagep()); UINFO(7, " Resolved " << nodep << endl); // Also prints taskp + } else if (m_insideClassExtParam) { + // The reference may point to a method declared in a super class, which is proved + // by a parameter. In such a case, it can't be linked at the first stage. + return; } else { // Note ParseRef has similar error handling/message output UINFO(7, " ErrFtask curSymp=se" << cvtToHex(m_curSymp) << " dotSymp=se" @@ -3083,6 +3109,8 @@ private: } } else if (VN_IS(nodep, New) && m_statep->forPrearray()) { // Resolved in V3Width + } else if (nodep->name() == "randomize" || nodep->name() == "srandom") { + // Resolved in V3Width } else if (nodep->dotted() == "") { if (nodep->pli()) { if (v3Global.opt.bboxSys()) { @@ -3244,124 +3272,134 @@ private: iterateChildren(nodep); } void visit(AstClass* nodep) override { - nodep->user3SetOnce(); + if (nodep->user3SetOnce()) return; UINFO(5, " " << nodep << endl); checkNoDot(nodep); VL_RESTORER(m_curSymp); VL_RESTORER(m_modSymp); VL_RESTORER(m_ifClassImpNames); + VL_RESTORER(m_insideClassExtParam); { m_ds.init(m_curSymp); // Until overridden by a SCOPE m_ds.m_dotSymp = m_curSymp = m_modSymp = m_statep->getNodeSym(nodep); m_modp = nodep; int next = 0; - for (AstNode* itemp = nodep->extendsp(); itemp; itemp = itemp->nextp()) { - if (AstClassExtends* const cextp = VN_CAST(itemp, ClassExtends)) { - // Replace abstract reference with hard pointer - // Will need later resolution when deal with parameters - if (++next == 2 && !nodep->isInterfaceClass() && !cextp->isImplements()) { - cextp->v3error("Multiple inheritance illegal on non-interface classes" - " (IEEE 1800-2017 8.13)"); - } - if (cextp->childDTypep() || cextp->dtypep()) continue; // Already converted - if (VN_IS(cextp->classOrPkgsp(), Dot)) { - itemp->v3warn(E_UNSUPPORTED, "Unsupported: Hierarchical class references"); - continue; - } - AstClassOrPackageRef* const cpackagerefp - = VN_CAST(cextp->classOrPkgsp(), ClassOrPackageRef); - if (VL_UNCOVERABLE(!cpackagerefp)) { - // Linking the extend gives an error before this is hit - cextp->v3error("Attempting to extend using non-class"); // LCOV_EXCL_LINE + for (AstClassExtends* cextp = nodep->extendsp(); cextp; + cextp = VN_AS(cextp->nextp(), ClassExtends)) { + // Replace abstract reference with hard pointer + // Will need later resolution when deal with parameters + if (++next == 2 && !nodep->isInterfaceClass() && !cextp->isImplements()) { + cextp->v3error("Multiple inheritance illegal on non-interface classes" + " (IEEE 1800-2017 8.13)"); + } + if (cextp->childDTypep() || cextp->dtypep()) { + // Already converted. Update symbol table to link unlinked members. + // Base class has to be visited in a case if its extends statement + // needs to be handled. Recursive inheritance was already checked. + iterate(cextp->classp()); + importSymbolsFromExtended(nodep, cextp); + continue; + } + AstNode* cprp = cextp->classOrPkgsp(); + VSymEnt* lookSymp = m_curSymp; + if (AstDot* const dotp = VN_CAST(cprp, Dot)) { + dotp->user3(true); + if (AstClassOrPackageRef* lookNodep + = VN_CAST(dotp->lhsp(), ClassOrPackageRef)) { + iterate(lookNodep); + cprp = dotp->rhsp(); + lookSymp = m_statep->getNodeSym(lookNodep->classOrPackagep()); } else { - VSymEnt* const foundp = m_curSymp->findIdFallback(cpackagerefp->name()); - if (foundp) { - AstClassRefDType* classRefDtypep = nullptr; - AstClass* classp = VN_CAST(foundp->nodep(), Class); - if (classp) { - if (classp != nodep) { - // Case with recursive inheritance is handled later in this - // function - iterate(classp); - } - if (classp->user5()) { - // Has a parameter as its base class - nodep->user5(true); - return; - } - AstPin* paramsp = cpackagerefp->paramsp(); - if (paramsp) paramsp = paramsp->cloneTree(true); - classRefDtypep - = new AstClassRefDType{nodep->fileline(), classp, paramsp}; - } else if (AstParamTypeDType* const paramp - = VN_CAST(foundp->nodep(), ParamTypeDType)) { - if (m_statep->forPrimary()) { - // Extending has to be handled after V3Param.cpp, but the type - // reference has to be visited - iterate(paramp); - nodep->user5(true); - return; - } else { - AstNodeDType* const paramTypep = paramp->getChildDTypep(); - classRefDtypep - = VN_CAST(paramTypep->cloneTree(false), ClassRefDType); - if (!classRefDtypep) { - paramTypep->v3error( - "Attempting to extend using non-class"); - } else { - classp = classRefDtypep->classp(); - } - } - } else { - cextp->v3warn(E_UNSUPPORTED, - "Unsupported: " << foundp->nodep()->prettyTypeName() - << " in AstClassExtends"); + dotp->lhsp()->v3error("Attempting to extend" // LCOV_EXCL_LINE + " using non-class under dot"); + } + } + AstClassOrPackageRef* const cpackagerefp = VN_CAST(cprp, ClassOrPackageRef); + if (VL_UNCOVERABLE(!cpackagerefp)) { + // Linking the extend gives an error before this is hit + cextp->v3error("Attempting to extend using non-class"); // LCOV_EXCL_LINE + } else { + VSymEnt* const foundp = lookSymp->findIdFallback(cpackagerefp->name()); + if (foundp) { + AstClassRefDType* classRefDtypep = nullptr; + AstClass* classp = VN_CAST(foundp->nodep(), Class); + if (classp) { + if (classp != nodep) { + // Case with recursive inheritance is handled later in this + // function + iterate(classp); } - - if (classp) { - UINFO(8, "Import to " << nodep << " from export class " << classp - << endl); - if (classp == nodep) { - cextp->v3error("Attempting to extend class " - << nodep->prettyNameQ() << " from itself"); - } else if (cextp->isImplements() && !classp->isInterfaceClass()) { - cextp->v3error( - "Attempting to implement from non-interface class " - << classp->prettyNameQ() << '\n' - << "... Suggest use 'extends'"); - } else if (!cextp->isImplements() && !nodep->isInterfaceClass() - && classp->isInterfaceClass()) { - cextp->v3error("Attempting to extend from interface class " - << classp->prettyNameQ() << '\n' - << "... Suggest use 'implements'"); + if (m_statep->forPrimary() + && m_extendsParam.find(classp) != m_extendsParam.end()) { + // Has a parameter as its base class + m_extendsParam.insert(nodep); + m_insideClassExtParam = true; + } + AstPin* paramsp = cpackagerefp->paramsp(); + if (paramsp) paramsp = paramsp->cloneTree(true); + classRefDtypep + = new AstClassRefDType{nodep->fileline(), classp, paramsp}; + } else if (AstParamTypeDType* const paramp + = VN_CAST(foundp->nodep(), ParamTypeDType)) { + if (m_statep->forPrimary()) { + // Extending has to be handled after V3Param.cpp + m_extendsParam.insert(nodep); + m_insideClassExtParam = true; + continue; + } else { + AstNodeDType* const paramTypep = paramp->subDTypep(); + classRefDtypep + = VN_CAST(paramTypep->cloneTree(false), ClassRefDType); + if (!classRefDtypep) { + paramTypep->v3error("Attempting to extend using non-class"); } else { - cextp->childDTypep(classRefDtypep); - classp->isExtended(true); - nodep->isExtended(true); - VSymEnt* const srcp = m_statep->getNodeSym(classp); - if (classp->isInterfaceClass()) { - importImplementsClass(nodep, srcp, classp); - } - if (!cextp->isImplements()) { - m_curSymp->importFromClass(m_statep->symsp(), srcp); - } - VL_DO_DANGLING(cpackagerefp->unlinkFrBack()->deleteTree(), - cpackagerefp); + classp = classRefDtypep->classp(); } } } else { - const string suggest = m_statep->suggestSymFallback( - m_curSymp, cpackagerefp->name(), LinkNodeMatcherClass{}); - cpackagerefp->v3error( - "Class for '" - << cextp->verilogKwd() // extends/implements - << "' not found: " << cpackagerefp->prettyNameQ() << '\n' - << (suggest.empty() ? "" : cpackagerefp->warnMore() + suggest)); + cextp->v3warn(E_UNSUPPORTED, + "Unsupported: " << foundp->nodep()->prettyTypeName() + << " in AstClassExtends"); } + + if (classp) { + UINFO(8, "Import to " << nodep << " from export class " << classp + << endl); + if (classp == nodep) { + cextp->v3error("Attempting to extend class " + << nodep->prettyNameQ() << " from itself"); + } else if (cextp->isImplements() && !classp->isInterfaceClass()) { + cextp->v3error("Attempting to implement from non-interface class " + << classp->prettyNameQ() << '\n' + << "... Suggest use 'extends'"); + } else if (!cextp->isImplements() && !nodep->isInterfaceClass() + && classp->isInterfaceClass()) { + cextp->v3error("Attempting to extend from interface class " + << classp->prettyNameQ() << '\n' + << "... Suggest use 'implements'"); + } else { + cextp->childDTypep(classRefDtypep); + classp->isExtended(true); + nodep->isExtended(true); + importSymbolsFromExtended(nodep, cextp); + VL_DO_DANGLING(cextp->classOrPkgsp()->unlinkFrBack()->deleteTree(), + cpackagerefp); + } + } + } else { + const string suggest = m_statep->suggestSymFallback( + m_curSymp, cpackagerefp->name(), LinkNodeMatcherClass{}); + cpackagerefp->v3error( + "Class for '" + << cextp->verilogKwd() // extends/implements + << "' not found: " << cpackagerefp->prettyNameQ() << '\n' + << (suggest.empty() ? "" : cpackagerefp->warnMore() + suggest)); } } } + m_ds.m_dotSymp = m_curSymp; + iterateChildren(nodep); } // V3Width when determines types needs to find enum values and such diff --git a/src/V3LinkInc.cpp b/src/V3LinkInc.cpp index 4a2bb0c1c..e420d1276 100644 --- a/src/V3LinkInc.cpp +++ b/src/V3LinkInc.cpp @@ -240,11 +240,12 @@ private: return; } - const AstNodeVarRef* varrefp = nullptr; - if (m_unsupportedHere || !(varrefp = VN_CAST(nodep->rhsp(), VarRef))) { + if (m_unsupportedHere) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: Incrementation in this context."); return; } + AstNodeExpr* const readp = nodep->rhsp(); + AstNodeExpr* const writep = nodep->thsp(); AstConst* const constp = VN_AS(nodep->lhsp(), Const); UASSERT_OBJ(nodep, constp, "Expecting CONST"); @@ -253,9 +254,10 @@ private: // Prepare a temporary variable FileLine* const fl = backp->fileline(); - const string name = string("__Vincrement") + cvtToStr(++m_modIncrementsNum); - AstVar* const varp = new AstVar{fl, VVarType::BLOCKTEMP, name, VFlagChildDType{}, - varrefp->varp()->subDTypep()->cloneTree(true)}; + const string name = string{"__Vincrement"} + cvtToStr(++m_modIncrementsNum); + AstVar* const varp = new AstVar{ + fl, VVarType::BLOCKTEMP, name, VFlagChildDType{}, + new AstRefDType{fl, AstRefDType::FlagTypeOfExpr{}, readp->cloneTree(true)}}; if (m_ftaskp) varp->funcLocal(true); // Declare the variable @@ -264,30 +266,29 @@ private: // Define what operation will we be doing AstNodeExpr* operp; if (VN_IS(nodep, PostSub) || VN_IS(nodep, PreSub)) { - operp = new AstSub{fl, new AstVarRef{fl, varrefp->varp(), VAccess::READ}, newconstp}; + operp = new AstSub{fl, readp->cloneTree(true), newconstp}; } else { - operp = new AstAdd{fl, new AstVarRef{fl, varrefp->varp(), VAccess::READ}, newconstp}; + operp = new AstAdd{fl, readp->cloneTree(true), newconstp}; } if (VN_IS(nodep, PreAdd) || VN_IS(nodep, PreSub)) { // PreAdd/PreSub operations // Immediately after declaration - increment it by one - varp->addNextHere(new AstAssign{fl, new AstVarRef{fl, varrefp->varp(), VAccess::WRITE}, + varp->addNextHere(new AstAssign{fl, writep->cloneTree(true), new AstVarRef{fl, varp, VAccess::READ}}); // Immediately after incrementing - assign it to the original variable varp->addNextHere(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, operp}); } else { // PostAdd/PostSub operations // assign the original variable to the temporary one - varp->addNextHere( - new AstAssign{fl, new AstVarRef{fl, varrefp->varp(), VAccess::WRITE}, operp}); + varp->addNextHere(new AstAssign{fl, writep->cloneTree(true), operp}); // Increment the original variable by one varp->addNextHere(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, - new AstVarRef{fl, varrefp->varp(), VAccess::READ}}); + readp->cloneTree(true)}); } // Replace the node with the temporary - nodep->replaceWith(new AstVarRef{varrefp->fileline(), varp, VAccess::READ}); + nodep->replaceWith(new AstVarRef{readp->fileline(), varp, VAccess::READ}); VL_DO_DANGLING(nodep->deleteTree(), nodep); } void visit(AstPreAdd* nodep) override { prepost_visit(nodep); } diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index 14c1eddac..7ac37ade3 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -35,6 +35,7 @@ #include "V3LinkJump.h" #include "V3Ast.h" +#include "V3AstUserAllocator.h" #include "V3Global.h" #include @@ -46,6 +47,12 @@ VL_DEFINE_DEBUG_FUNCTIONS; class LinkJumpVisitor final : public VNVisitor { private: + // NODE STATE + // AstNode::user1() -> AstJumpLabel*, for this block if endOfIter + // AstNode::user2() -> AstJumpLabel*, for this block if !endOfIter + const VNUser1InUse m_user1InUse; + const VNUser2InUse m_user2InUse; + // STATE AstNodeModule* m_modp = nullptr; // Current module AstNodeFTask* m_ftaskp = nullptr; // Current function/task @@ -61,6 +68,13 @@ private: UINFO(4, "Create label for " << nodep << endl); if (VN_IS(nodep, JumpLabel)) return VN_AS(nodep, JumpLabel); // Done + // Made it previously? We always jump to the end, so this works out + if (endOfIter) { + if (nodep->user1p()) return VN_AS(nodep->user1p(), JumpLabel); + } else { + if (nodep->user2p()) return VN_AS(nodep->user2p(), JumpLabel); + } + AstNode* underp = nullptr; bool under_and_next = true; if (VN_IS(nodep, NodeBlock)) { @@ -125,9 +139,27 @@ private: } // Label goes last blockp->addEndStmtsp(labelp); + if (endOfIter) { + nodep->user1p(labelp); + } else { + nodep->user2p(labelp); + } return labelp; } } + void addPrefixToBlocksRecurse(AstNode* nodep) { + // Add do_while_ prefix to blocks + // Used to not have blocks with duplicated names + if (AstBegin* const beginp = VN_CAST(nodep, Begin)) { + if (beginp->name() != "") beginp->name("__Vdo_while_" + beginp->name()); + } + + if (nodep->op1p()) addPrefixToBlocksRecurse(nodep->op1p()); + if (nodep->op2p()) addPrefixToBlocksRecurse(nodep->op2p()); + if (nodep->op3p()) addPrefixToBlocksRecurse(nodep->op3p()); + if (nodep->op4p()) addPrefixToBlocksRecurse(nodep->op4p()); + if (nodep->nextp()) addPrefixToBlocksRecurse(nodep->nextp()); + } // VISITORS void visit(AstNodeModule* nodep) override { @@ -160,7 +192,7 @@ private: // REPEAT(count,body) -> loop=count,WHILE(loop>0) { body, loop-- } // Note var can be signed or unsigned based on original number. AstNodeExpr* const countp = nodep->countp()->unlinkFrBackWithNext(); - const string name = string("__Vrepeat") + cvtToStr(m_modRepeatNum++); + const string name = string{"__Vrepeat"} + cvtToStr(m_modRepeatNum++); // Spec says value is integral, if negative is ignored AstVar* const varp = new AstVar{nodep->fileline(), VVarType::BLOCKTEMP, name, nodep->findSigned32DType()}; @@ -200,23 +232,21 @@ private: void visit(AstDoWhile* nodep) override { // It is converted to AstWhile in this visit method VL_RESTORER(m_loopp); - VL_RESTORER(m_loopInc); { m_loopp = nodep; - m_loopInc = false; - iterateAndNextNull(nodep->precondsp()); iterateAndNextNull(nodep->condp()); iterateAndNextNull(nodep->stmtsp()); - m_loopInc = true; - iterateAndNextNull(nodep->incsp()); } AstNodeExpr* const condp = nodep->condp() ? nodep->condp()->unlinkFrBack() : nullptr; AstNode* const bodyp = nodep->stmtsp() ? nodep->stmtsp()->unlinkFrBack() : nullptr; - AstNode* const incsp = nodep->incsp() ? nodep->incsp()->unlinkFrBack() : nullptr; - AstWhile* const whilep = new AstWhile{nodep->fileline(), condp, bodyp, incsp}; + AstWhile* const whilep = new AstWhile{nodep->fileline(), condp, bodyp}; nodep->replaceWith(whilep); VL_DO_DANGLING(nodep->deleteTree(), nodep); - if (bodyp) whilep->addHereThisAsNext(bodyp->cloneTree(false)); + if (bodyp) { + AstNode* const copiedBodyp = bodyp->cloneTree(false); + addPrefixToBlocksRecurse(copiedBodyp); + whilep->addHereThisAsNext(copiedBodyp); + } } void visit(AstForeach* nodep) override { VL_RESTORER(m_loopp); diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index 5672089fc..ca0f07574 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -116,83 +116,43 @@ private: iterateAndNextNull(nodep->top()); } } - void visit(AstFOpen* nodep) override { - VL_RESTORER(m_setRefLvalue); - { - m_setRefLvalue = VAccess::WRITE; - iterateAndNextNull(nodep->filep()); - m_setRefLvalue = VAccess::NOCHANGE; - iterateAndNextNull(nodep->filenamep()); - iterateAndNextNull(nodep->modep()); - } - } - void visit(AstFOpenMcd* nodep) override { - VL_RESTORER(m_setRefLvalue); - { - m_setRefLvalue = VAccess::WRITE; - iterateAndNextNull(nodep->filep()); - m_setRefLvalue = VAccess::NOCHANGE; - iterateAndNextNull(nodep->filenamep()); - } - } - void visit(AstFClose* nodep) override { - VL_RESTORER(m_setRefLvalue); - { - m_setRefLvalue = VAccess::WRITE; - iterateAndNextNull(nodep->filep()); - } - } void visit(AstFError* nodep) override { VL_RESTORER(m_setRefLvalue); { - m_setRefLvalue = VAccess::WRITE; iterateAndNextNull(nodep->filep()); + m_setRefLvalue = VAccess::WRITE; iterateAndNextNull(nodep->strp()); } } - void visit(AstFFlush* nodep) override { - VL_RESTORER(m_setRefLvalue); - { - m_setRefLvalue = VAccess::WRITE; - iterateAndNextNull(nodep->filep()); - } - } - void visit(AstFGetC* nodep) override { - VL_RESTORER(m_setRefLvalue); - { - m_setRefLvalue = VAccess::WRITE; - iterateAndNextNull(nodep->filep()); - } - } void visit(AstFGetS* nodep) override { VL_RESTORER(m_setRefLvalue); { - m_setRefLvalue = VAccess::WRITE; iterateAndNextNull(nodep->filep()); + m_setRefLvalue = VAccess::WRITE; iterateAndNextNull(nodep->strgp()); } } void visit(AstFRead* nodep) override { VL_RESTORER(m_setRefLvalue); { + iterateAndNextNull(nodep->filep()); m_setRefLvalue = VAccess::WRITE; iterateAndNextNull(nodep->memp()); - iterateAndNextNull(nodep->filep()); } } void visit(AstFScanF* nodep) override { VL_RESTORER(m_setRefLvalue); { - m_setRefLvalue = VAccess::WRITE; iterateAndNextNull(nodep->filep()); + m_setRefLvalue = VAccess::WRITE; iterateAndNextNull(nodep->exprsp()); } } void visit(AstFUngetC* nodep) override { VL_RESTORER(m_setRefLvalue); { - m_setRefLvalue = VAccess::WRITE; iterateAndNextNull(nodep->filep()); + m_setRefLvalue = VAccess::WRITE; iterateAndNextNull(nodep->rhsp()); } } diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index 9718b9b67..11fffed29 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -59,7 +59,7 @@ void V3LinkLevel::modSortByLevel() { if (tops.size() >= 2) { const AstNode* const secp = tops[1]; // Complain about second one, as first often intended if (!secp->fileline()->warnIsOff(V3ErrorCode::MULTITOP)) { - auto warnTopModules = [](std::string warnMore, ModVec tops) + auto warnTopModules = [](const std::string& warnMore, ModVec tops) VL_REQUIRES(V3Error::s().m_mutex) -> std::string { std::stringstream ss; for (AstNode* alsop : tops) { diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index c0984fe98..3b7d8eb0a 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -65,6 +65,7 @@ private: int m_genblkAbove = 0; // Begin block number of if/case/for above int m_genblkNum = 0; // Begin block number, 0=none seen VLifetime m_lifetime = VLifetime::STATIC; // Propagating lifetime + bool m_insideLoop = false; // True if the node is inside a loop // METHODS void cleanFileline(AstNode* nodep) { @@ -134,6 +135,9 @@ private: // VISITs void visit(AstNodeFTask* nodep) override { if (!nodep->user1SetOnce()) { // Process only once. + // Mark class methods + if (VN_IS(m_modp, Class)) nodep->classMethod(true); + V3Config::applyFTask(m_modp, nodep); cleanFileline(nodep); VL_RESTORER(m_ftaskp); @@ -222,6 +226,14 @@ private: void visit(AstVar* nodep) override { cleanFileline(nodep); + if (nodep->lifetime().isStatic() && m_insideLoop && nodep->valuep()) { + nodep->lifetime(VLifetime::AUTOMATIC); + nodep->v3warn(STATICVAR, "Static variable with assignment declaration declared in a " + "loop converted to automatic"); + } + if (m_ftaskp && m_ftaskp->classMethod() && nodep->lifetime().isNone()) { + nodep->lifetime(VLifetime::AUTOMATIC); + } if (nodep->lifetime().isNone() && nodep->varType() != VVarType::PORT) { nodep->lifetime(m_lifetime); } @@ -248,7 +260,10 @@ private: // Maybe this variable has a signal attribute V3Config::applyVarAttr(m_modp, m_ftaskp, nodep); - if (v3Global.opt.publicFlatRW()) { + 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 @@ -259,6 +274,8 @@ private: } } + 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 @@ -454,6 +471,8 @@ private: // 2. ASTSELBIT(first, var0)) // 3. ASTSELLOOPVARS(first, var0..var1)) // 4. DOT(DOT(first, second), ASTSELBIT(third, var0)) + VL_RESTORER(m_insideLoop); + m_insideLoop = true; AstNode* bracketp = nodep->arrayp(); while (AstDot* dotp = VN_CAST(bracketp, Dot)) bracketp = dotp->rhsp(); if (AstSelBit* const selp = VN_CAST(bracketp, SelBit)) { @@ -466,14 +485,34 @@ private: } else if (VN_IS(bracketp, SelLoopVars)) { // Ok } else { - nodep->v3error( - "Syntax error; foreach missing bracketed loop variable (IEEE 1800-2017 12.7.3)"); + nodep->v3error("Syntax error; foreach missing bracketed loop variable (IEEE " + "1800-2017 12.7.3)"); VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); return; } iterateChildren(nodep); } - + void visit(AstRepeat* nodep) override { + VL_RESTORER(m_insideLoop); + { + m_insideLoop = true; + iterateChildren(nodep); + } + } + void visit(AstDoWhile* nodep) override { + VL_RESTORER(m_insideLoop); + { + m_insideLoop = true; + iterateChildren(nodep); + } + } + void visit(AstWhile* nodep) override { + VL_RESTORER(m_insideLoop); + { + m_insideLoop = true; + iterateChildren(nodep); + } + } void visit(AstNodeModule* nodep) override { V3Config::applyModule(nodep); diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 6ecc90b2c..df1872d19 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -115,7 +115,7 @@ private: if (m_underGenerate) nodep->underGenerate(true); // Remember the existing symbol table scope if (m_classp) { - if (nodep->name() == "randomize") { + if (nodep->name() == "randomize" || nodep->name() == "srandom") { nodep->v3error(nodep->prettyNameQ() << " is a predefined class method; redefinition not allowed" " (IEEE 1800-2017 18.6.3)"); @@ -318,14 +318,6 @@ private: if (filep && filep->varp()) filep->varp()->attrFileDescr(true); } - void visit(AstFOpen* nodep) override { - iterateChildren(nodep); - expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef)); - } - void visit(AstFOpenMcd* nodep) override { - iterateChildren(nodep); - expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef)); - } void visit(AstFClose* nodep) override { iterateChildren(nodep); expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef)); @@ -454,7 +446,7 @@ public: // Recurses cells backwards, so we can pick up those things that propagate // from child cells up to the top module. -class LinkBotupVisitor final : public VNVisitor { +class LinkBotupVisitor final : public VNVisitorConst { private: // STATE AstNodeModule* m_modp = nullptr; // Current module @@ -462,14 +454,12 @@ private: // VISITs void visit(AstNetlist* nodep) override { // Iterate modules backwards, in bottom-up order. - iterateChildrenBackwards(nodep); + iterateChildrenBackwardsConst(nodep); } void visit(AstNodeModule* nodep) override { VL_RESTORER(m_modp); - { - m_modp = nodep; - iterateChildren(nodep); - } + m_modp = nodep; + iterateChildrenConst(nodep); } void visit(AstCell* nodep) override { // Parent module inherits child's publicity @@ -477,11 +467,11 @@ private: //** No iteration for speed } void visit(AstNodeExpr*) override {} // Accelerate - void visit(AstNode* nodep) override { iterateChildren(nodep); } + void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } public: // CONSTRUCTORS - explicit LinkBotupVisitor(AstNetlist* rootp) { iterate(rootp); } + explicit LinkBotupVisitor(AstNetlist* rootp) { iterateConst(rootp); } ~LinkBotupVisitor() override = default; }; diff --git a/src/V3MergeCond.cpp b/src/V3MergeCond.cpp index c7fe8ec75..d5756cc67 100644 --- a/src/V3MergeCond.cpp +++ b/src/V3MergeCond.cpp @@ -158,7 +158,7 @@ using StmtPropertiesAllocator = AstUser3Allocator; // Pure analysis visitor that build the StmtProperties for each statement in the given // AstNode list (following AstNode::nextp()) -class CodeMotionAnalysisVisitor final : public VNVisitor { +class CodeMotionAnalysisVisitor final : public VNVisitorConst { // NODE STATE // AstNodeStmt::user3 -> StmtProperties (accessed via m_stmtProperties, managed externally, // see MergeCondVisitor::process) diff --git a/src/V3Mutex.h b/src/V3Mutex.h new file mode 100644 index 000000000..b1ee4b021 --- /dev/null +++ b/src/V3Mutex.h @@ -0,0 +1,164 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Thread pool for Verilator itself +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2005-2023 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* +/// +/// \file +/// \brief Verilator Mutex and LockGuard used only in verilator. +/// +/// This file defines Mutex and LockGuard that is used in verilator +/// source code. It shouldn't be used by verilated code. +/// In contrast to VerilatedMutex that is used by verilated code, +/// this mutex can be configured and disabled when verilation is using +/// single thread. +/// +/// This implementation also allows using different base mutex class in +/// wrapper V3Mutex class. +/// +//************************************************************************* + +#ifndef VERILATOR_V3MUTEX_H_ +#define VERILATOR_V3MUTEX_H_ 1 + +#include "verilatedos.h" + +#include +#include +#include + +#define VL_LOCK_SPINS 50000 /// Number of times to spin for a mutex before yielding + +// MutexConfig class that allows to configure how mutex and lockgurads behave +// once configured and locked, it cannot be changed. Configuration and lock needs to be +// done before starting any additional threads. +class V3MutexConfig final { +private: + // Allows to disable mutexes and lockguards. + // Use carefully as it can cause undefined behavior when used inappropriately. + // All mutexes needs to be unlocked. + bool m_enable = false; // Allows locking on mutexes, default don't lock mutexes + bool m_lockConfig = false; // After set, configuration cannot be changed + + V3MutexConfig() = default; + ~V3MutexConfig() = default; + +public: + static V3MutexConfig& s() VL_MT_SAFE { + static V3MutexConfig s; + return s; + } + + // configures class + void configure(bool enable) VL_MT_UNSAFE { + if (!s().m_lockConfig) { + s().m_enable = enable; + s().m_lockConfig = true; + } else { + // requires + // avoided to reduce compile time + // std::cerr << "%Error: V3Mutex already configured." << std::endl; + std::abort(); + } + } + bool lockConfig() VL_MT_SAFE { return m_lockConfig; } + bool enable() VL_MT_SAFE { return m_enable; } +}; + +/// Mutex, wrapped to allow -fthread_safety checks +template +class VL_CAPABILITY("mutex") V3MutexImp final { +private: + T m_mutex; // Mutex + +public: + /// Construct mutex (without locking it) + V3MutexImp() = default; + ~V3MutexImp() = default; + const V3MutexImp& operator!() const { return *this; } // For -fthread_safety + /// Acquire/lock mutex + void lock() VL_ACQUIRE() VL_MT_SAFE { + if (V3MutexConfig::s().enable()) { + // Try to acquire the lock by spinning. If the wait is short, + // avoids a trap to the OS plus OS scheduler overhead. + if (VL_LIKELY(try_lock())) return; // Short circuit loop + for (int i = 0; i < VL_LOCK_SPINS; ++i) { + if (VL_LIKELY(try_lock())) return; + VL_CPU_RELAX(); + } + // Spinning hasn't worked, pay the cost of blocking. + m_mutex.lock(); + } + } + /// Release/unlock mutex + void unlock() VL_RELEASE() VL_MT_SAFE { + if (V3MutexConfig::s().enable()) { m_mutex.unlock(); } + } + /// Try to acquire mutex. Returns true on success, and false on failure. + bool try_lock() VL_TRY_ACQUIRE(true) VL_MT_SAFE { + return V3MutexConfig::s().enable() ? m_mutex.try_lock() : true; + } + /// Acquire/lock mutex and check for stop request + /// It tries to lock the mutex and if it fails, it check if stop request was send. + /// It returns after locking mutex. + /// This function should be extracted to V3ThreadPool, but due to clang thread-safety + /// limitations it needs to be placed here. + void lockCheckStopRequest(std::function checkStopRequestFunction) + VL_ACQUIRE() VL_MT_SAFE { + if (V3MutexConfig::s().enable()) { + while (true) { + checkStopRequestFunction(); + if (m_mutex.try_lock()) return; + VL_CPU_RELAX(); + } + } + } +}; + +/// Lock guard for mutex (ala std::unique_lock), wrapped to allow -fthread_safety checks +template +class VL_SCOPED_CAPABILITY V3LockGuardImp final { + VL_UNCOPYABLE(V3LockGuardImp); + +private: + T& m_mutexr; + +public: + /// Construct and hold given mutex lock until destruction or unlock() + explicit V3LockGuardImp(T& mutexr) VL_ACQUIRE(mutexr) VL_MT_SAFE + : m_mutexr(mutexr) { // Need () or GCC 4.8 false warning + m_mutexr.lock(); + } + /// Destruct and unlock the mutex + ~V3LockGuardImp() VL_RELEASE() { m_mutexr.unlock(); } + /// Lock the mutex + void lock() VL_ACQUIRE() VL_MT_SAFE { m_mutexr.lock(); } + /// Unlock the mutex + void unlock() VL_RELEASE() VL_MT_SAFE { m_mutexr.unlock(); } + /// Acquire/lock mutex and check for stop request. + /// It tries to lock the mutex and if it fails, it check if stop request was send. + /// It returns after locking mutex. + /// This function should be extracted to V3ThreadPool, but due to clang thread-safety + /// limitations it needs to be placed here. + void lockCheckStopRequest(std::function checkStopRequestFunction) + VL_ACQUIRE() VL_MT_SAFE { + m_mutexr.lockCheckStopRequest(checkStopRequestFunction); + } +}; + +using V3Mutex = V3MutexImp; +using V3RecursiveMutex = V3MutexImp; +using V3LockGuard = V3LockGuardImp; +using V3RecursiveLockGuard = V3LockGuardImp; + +#endif // guard diff --git a/src/V3Name.cpp b/src/V3Name.cpp index 68a282140..bec3dd9cc 100644 --- a/src/V3Name.cpp +++ b/src/V3Name.cpp @@ -103,13 +103,19 @@ private: } void visit(AstMemberDType* nodep) override { if (!nodep->user1()) { - rename(nodep, false); + rename(nodep, true); iterateChildren(nodep); } } void visit(AstMemberSel* nodep) override { if (!nodep->user1()) { - rename(nodep, false); + rename(nodep, true); + iterateChildren(nodep); + } + } + void visit(AstStructSel* nodep) override { + if (!nodep->user1()) { + rename(nodep, true); iterateChildren(nodep); } } diff --git a/src/V3Number.cpp b/src/V3Number.cpp index b99717d60..9e5e295c5 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -385,7 +385,7 @@ void V3Number::create(const char* sourcep) { // m_value[0]); } -void V3Number::nodep(AstNode* nodep) { +void V3Number::nodep(AstNode* nodep) VL_MT_STABLE { m_nodep = nodep; if (!nodep) return; m_fileline = nodep->fileline(); @@ -498,7 +498,7 @@ V3Number& V3Number::setMask(int nbits) { //====================================================================== // ACCESSORS - as strings -string V3Number::ascii(bool prefixed, bool cleanVerilog) const { +string V3Number::ascii(bool prefixed, bool cleanVerilog) const VL_MT_STABLE { std::ostringstream out; if (is1Step()) { @@ -598,17 +598,17 @@ bool V3Number::displayedFmtLegal(char format, bool isScan) { } } -string V3Number::displayPad(size_t fmtsize, char pad, bool left, const string& in) { +string V3Number::displayPad(size_t fmtsize, char pad, bool left, const string& in) VL_PURE { string padding; if (in.length() < fmtsize) padding = string(fmtsize - in.length(), pad); return left ? (in + padding) : (padding + in); } -string V3Number::displayed(AstNode* nodep, const string& vformat) const { +string V3Number::displayed(AstNode* nodep, const string& vformat) const VL_MT_STABLE { return displayed(nodep->fileline(), vformat); } -string V3Number::displayed(FileLine* fl, const string& vformat) const { +string V3Number::displayed(FileLine* fl, const string& vformat) const VL_MT_STABLE { auto pos = vformat.cbegin(); UASSERT(pos != vformat.cend() && pos[0] == '%', "$display-like function with non format argument " << *this); @@ -847,7 +847,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const { } } -string V3Number::toDecimalS() const { +string V3Number::toDecimalS() const VL_MT_STABLE { if (isNegative()) { V3Number lhsNoSign = *this; lhsNoSign.opNegate(*this); @@ -857,7 +857,7 @@ string V3Number::toDecimalS() const { } } -string V3Number::toDecimalU() const { +string V3Number::toDecimalU() const VL_MT_STABLE { const int maxdecwidth = (width() + 3) * 4 / 3; // Or (maxdecwidth+7)/8], but can't have more than 4 BCD bits per word @@ -913,7 +913,7 @@ uint32_t V3Number::toUInt() const VL_MT_SAFE { return m_data.num()[0].m_value; } -double V3Number::toDouble() const { +double V3Number::toDouble() const VL_MT_SAFE { if (VL_UNCOVERABLE(!isDouble() || width() != 64)) { v3fatalSrc("Real operation on wrong sized/non-real number"); } @@ -926,7 +926,7 @@ double V3Number::toDouble() const { return u.d; } -int32_t V3Number::toSInt() const { +int32_t V3Number::toSInt() const VL_MT_SAFE { if (isSigned()) { const uint32_t v = toUInt(); const uint32_t signExtend = (-(v & (1UL << (width() - 1)))); @@ -996,14 +996,14 @@ uint8_t V3Number::dataByte(int byte) const { return (edataWord(byte / (VL_EDATASIZE / 8)) >> ((byte * 8) % VL_EDATASIZE)) & 0xff; } -bool V3Number::isAllZ() const { +bool V3Number::isAllZ() const VL_MT_SAFE { if (isDouble() || isString()) return false; for (int i = 0; i < width(); i++) { if (!bitIsZ(i)) return false; } return true; } -bool V3Number::isAllX() const { +bool V3Number::isAllX() const VL_MT_SAFE { if (isDouble() || isString()) return false; uint32_t mask = hiWordMask(); for (int i = words() - 1; i >= 0; --i) { @@ -1013,7 +1013,7 @@ bool V3Number::isAllX() const { } return true; } -bool V3Number::isEqZero() const { +bool V3Number::isEqZero() const VL_MT_SAFE { if (isString()) return m_data.str().empty(); for (int i = 0; i < words(); i++) { const ValueAndX v = m_data.num()[i]; @@ -1050,14 +1050,14 @@ bool V3Number::isEqAllOnes(int optwidth) const { } return true; } -bool V3Number::isFourState() const { +bool V3Number::isFourState() const VL_MT_SAFE { if (isDouble() || isString()) return false; for (int i = 0; i < words(); ++i) { if (m_data.num()[i].m_valueX) return true; } return false; } -bool V3Number::isAnyX() const { +bool V3Number::isAnyX() const VL_MT_SAFE { if (isDouble() || isString()) return false; for (int bit = 0; bit < width(); bit++) { if (bitIsX(bit)) return true; @@ -1065,7 +1065,7 @@ bool V3Number::isAnyX() const { return false; } bool V3Number::isAnyXZ() const { return isAnyX() || isAnyZ(); } -bool V3Number::isAnyZ() const { +bool V3Number::isAnyZ() const VL_MT_SAFE { if (isDouble() || isString()) return false; for (int bit = 0; bit < width(); bit++) { if (bitIsZ(bit)) return true; @@ -1082,7 +1082,7 @@ bool V3Number::isLtXZ(const V3Number& rhs) const { } return false; } -int V3Number::countX(int lsb, int nbits) const { +int V3Number::countX(int lsb, int nbits) const VL_MT_SAFE { int count = 0; for (int bitn = 0; bitn < nbits; ++bitn) { if (lsb + bitn >= width()) return count; @@ -1090,7 +1090,7 @@ int V3Number::countX(int lsb, int nbits) const { } return count; } -int V3Number::countZ(int lsb, int nbits) const { +int V3Number::countZ(int lsb, int nbits) const VL_MT_SAFE { int count = 0; for (int bitn = 0; bitn < nbits; ++bitn) { if (lsb + bitn >= width()) return count; @@ -2232,8 +2232,10 @@ V3Number& V3Number::opNToI(const V3Number& lhs) { const string& str = lhs.toString(); for (size_t n = 0; n < str.length(); ++n) { const char c = str[str.length() - 1 - n]; - for (size_t cbit = 0; cbit < 8; ++cbit) - setBit(n * 8 + cbit, VL_BITISSET_I(c, cbit) ? 1 : 0); + for (size_t cbit = 0; cbit < 8; ++cbit) { + const std::bitset<8> sbit{static_cast(c)}; + setBit(n * 8 + cbit, sbit.test(cbit) ? 1 : 0); + } } return *this; } diff --git a/src/V3Number.h b/src/V3Number.h index 25b59706a..46ba16094 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -52,7 +52,7 @@ public: uint32_t m_value; // Each bit is true if it's X or Z, 10=z, 11=x uint32_t m_valueX; - bool operator==(const ValueAndX& other) const { + bool operator==(const ValueAndX& other) const VL_MT_SAFE { return m_value == other.m_value && m_valueX == other.m_valueX; } }; @@ -63,7 +63,7 @@ public: DOUBLE = 2, STRING = 3, }; - friend std::ostream& operator<<(std::ostream& os, const V3NumberDataType& rhs) { + friend std::ostream& operator<<(std::ostream& os, const V3NumberDataType& rhs) VL_MT_SAFE { switch (rhs) { case V3NumberDataType::UNINITIALIZED: return os << "UNINITIALIZED"; case V3NumberDataType::LOGIC: return os << "LOGIC"; @@ -356,7 +356,7 @@ class V3Number final { void opCleanThis(bool warnOnTruncation = false); public: - void nodep(AstNode* nodep); + void nodep(AstNode* nodep) VL_MT_STABLE; FileLine* fileline() const VL_MT_SAFE { return m_fileline; } V3Number& setZero(); V3Number& setQuad(uint64_t value); @@ -383,7 +383,7 @@ public: } private: - char bitIs(int bit) const VL_MT_SAFE { + char bitIs(int bit) const { if (bit >= m_data.width() || bit < 0) { // We never sign extend return '0'; @@ -559,9 +559,9 @@ private: m_data.m_sized = false; } } - static string displayPad(size_t fmtsize, char pad, bool left, const string& in); - string displayed(FileLine* fl, const string& vformat) const VL_MT_SAFE; - string displayed(const string& vformat) const VL_MT_SAFE { + static string displayPad(size_t fmtsize, char pad, bool left, const string& in) VL_PURE; + string displayed(FileLine* fl, const string& vformat) const VL_MT_STABLE; + string displayed(const string& vformat) const VL_MT_STABLE { return displayed(m_fileline, vformat); } @@ -583,8 +583,8 @@ public: V3Number& setMask(int nbits); // IE if nbits=1, then 0b1, if 2->0b11, if 3->0b111 etc // ACCESSORS - string ascii(bool prefixed = true, bool cleanVerilog = false) const VL_MT_SAFE; - string displayed(AstNode* nodep, const string& vformat) const; + string ascii(bool prefixed = true, bool cleanVerilog = false) const VL_MT_STABLE; + string displayed(AstNode* nodep, const string& vformat) const VL_MT_STABLE; static bool displayedFmtLegal(char format, bool isScan); // Is this a valid format letter? int width() const VL_MT_SAFE { return m_data.width(); } int widthMin() const; // Minimum width that can represent this number (~== log2(num)+1) @@ -637,13 +637,13 @@ public: bool isAnyXZ() const; bool isAnyZ() const VL_MT_SAFE; bool isMsbXZ() const { return bitIsXZ(m_data.width() - 1); } - uint32_t toUInt() const; + uint32_t toUInt() const VL_MT_SAFE; int32_t toSInt() const VL_MT_SAFE; - uint64_t toUQuad() const; + uint64_t toUQuad() const VL_MT_SAFE; int64_t toSQuad() const VL_MT_SAFE; string toString() const VL_MT_SAFE; - string toDecimalS() const VL_MT_SAFE; // return ASCII signed decimal number - string toDecimalU() const VL_MT_SAFE; // return ASCII unsigned decimal number + string toDecimalS() const VL_MT_STABLE; // return ASCII signed decimal number + string toDecimalU() const VL_MT_STABLE; // return ASCII unsigned decimal number double toDouble() const VL_MT_SAFE; V3Hash toHash() const; uint32_t edataWord(int eword) const; @@ -777,7 +777,7 @@ public: V3Number& opLtN(const V3Number& lhs, const V3Number& rhs); V3Number& opLteN(const V3Number& lhs, const V3Number& rhs); }; -inline std::ostream& operator<<(std::ostream& os, const V3Number& rhs) { +inline std::ostream& operator<<(std::ostream& os, const V3Number& rhs) VL_MT_SAFE { return os << rhs.ascii(); } diff --git a/src/V3Options.cpp b/src/V3Options.cpp index faec6ba50..3b8a26040 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -395,12 +395,12 @@ void V3Options::addForceInc(const string& filename) { m_forceIncs.push_back(file void V3Options::addArg(const string& arg) { m_impp->m_allArgs.push_back(arg); } string V3Options::allArgsString() const VL_MT_SAFE { - string out; + string result; for (const string& i : m_impp->m_allArgs) { - if (out != "") out += " "; - out += i; + if (result != "") result += " "; + result += i; } - return out; + return result; } // Delete some options for Verilation of the hierarchical blocks. @@ -656,8 +656,11 @@ string V3Options::getenvPERL() { // string V3Options::getenvSYSTEMC() { string var = V3Os::getenvStr("SYSTEMC", ""); - if (var == "" && string(DEFENV_SYSTEMC) != "") { - var = DEFENV_SYSTEMC; + // Treat compiled-in DEFENV string literals as C-strings to enable + // binary patching for relocatable installs (e.g. conda) + string defenv = string{DEFENV_SYSTEMC}.c_str(); + if (var == "" && defenv != "") { + var = defenv; V3Os::setenvStr("SYSTEMC", var, "Hardcoded at build time"); } return var; @@ -665,8 +668,11 @@ string V3Options::getenvSYSTEMC() { string V3Options::getenvSYSTEMC_ARCH() { string var = V3Os::getenvStr("SYSTEMC_ARCH", ""); - if (var == "" && string(DEFENV_SYSTEMC_ARCH) != "") { - var = DEFENV_SYSTEMC_ARCH; + // Treat compiled-in DEFENV string literals as C-strings to enable + // binary patching for relocatable installs (e.g. conda) + string defenv = string{DEFENV_SYSTEMC_ARCH}.c_str(); + if (var == "" && defenv != "") { + var = defenv; V3Os::setenvStr("SYSTEMC_ARCH", var, "Hardcoded at build time"); } if (var == "") { @@ -697,8 +703,11 @@ string V3Options::getenvSYSTEMC_ARCH() { string V3Options::getenvSYSTEMC_INCLUDE() { string var = V3Os::getenvStr("SYSTEMC_INCLUDE", ""); - if (var == "" && string(DEFENV_SYSTEMC_INCLUDE) != "") { - var = DEFENV_SYSTEMC_INCLUDE; + // Treat compiled-in DEFENV string literals as C-strings to enable + // binary patching for relocatable installs (e.g. conda) + string defenv = string{DEFENV_SYSTEMC_INCLUDE}.c_str(); + if (var == "" && defenv != "") { + var = defenv; V3Os::setenvStr("SYSTEMC_INCLUDE", var, "Hardcoded at build time"); } if (var == "") { @@ -710,8 +719,11 @@ string V3Options::getenvSYSTEMC_INCLUDE() { string V3Options::getenvSYSTEMC_LIBDIR() { string var = V3Os::getenvStr("SYSTEMC_LIBDIR", ""); - if (var == "" && string(DEFENV_SYSTEMC_LIBDIR) != "") { - var = DEFENV_SYSTEMC_LIBDIR; + // Treat compiled-in DEFENV string literals as C-strings to enable + // binary patching for relocatable installs (e.g. conda) + string defenv = string{DEFENV_SYSTEMC_LIBDIR}.c_str(); + if (var == "" && defenv != "") { + var = defenv; V3Os::setenvStr("SYSTEMC_LIBDIR", var, "Hardcoded at build time"); } if (var == "") { @@ -724,8 +736,11 @@ string V3Options::getenvSYSTEMC_LIBDIR() { string V3Options::getenvVERILATOR_ROOT() { string var = V3Os::getenvStr("VERILATOR_ROOT", ""); - if (var == "" && string(DEFENV_VERILATOR_ROOT) != "") { - var = DEFENV_VERILATOR_ROOT; + // Treat compiled-in DEFENV string literals as C-strings to enable + // binary patching for relocatable installs (e.g. conda) + string defenv = string{DEFENV_VERILATOR_ROOT}.c_str(); + if (var == "" && defenv != "") { + var = defenv; V3Os::setenvStr("VERILATOR_ROOT", var, "Hardcoded at build time"); } if (var == "") v3fatal("$VERILATOR_ROOT needs to be in environment\n"); @@ -874,13 +889,15 @@ void V3Options::notify() { //###################################################################### // V3 Options accessors -string V3Options::version() { +string V3Options::version() VL_PURE { string ver = DTVERSION; ver += " rev " + cvtToStr(DTVERSION_rev); return ver; } -string V3Options::protectKeyDefaulted() { +string V3Options::protectKeyDefaulted() VL_MT_SAFE { + static V3Mutex mutex; + const V3LockGuard lock{mutex}; if (m_protectKey.empty()) { // Create a key with a human-readable symbol-like name. // This conversion drops ~2 bits of entropy out of 256, shouldn't matter. @@ -949,9 +966,9 @@ void V3Options::parseOpts(FileLine* fl, int argc, char** argv) { // Default prefix to the filename if (prefix() == "" && topModule() != "") - m_prefix = string("V") + AstNode::encodeName(topModule()); + m_prefix = string{"V"} + AstNode::encodeName(topModule()); if (prefix() == "" && vFilesList.size() >= 1) - m_prefix = string("V") + AstNode::encodeName(V3Os::filenameNonExt(*(vFilesList.begin()))); + m_prefix = string{"V"} + AstNode::encodeName(V3Os::filenameNonExt(*(vFilesList.begin()))); if (modPrefix() == "") m_modPrefix = prefix(); // Find files in makedir @@ -1381,11 +1398,15 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char m_protectIds = true; }); DECL_OPTION("-public", OnOff, &m_public); + DECL_OPTION("-public-depth", Set, &m_publicDepth); DECL_OPTION("-public-flat-rw", CbOnOff, [this](bool flag) { m_publicFlatRW = flag; v3Global.dpi(true); }); - + DECL_OPTION("-public-params", CbOnOff, [this](bool flag) { + m_public_params = flag; + v3Global.dpi(true); + }); DECL_OPTION("-quiet-exit", OnOff, &m_quietExit); DECL_OPTION("-relative-includes", OnOff, &m_relativeIncludes); @@ -1934,7 +1955,7 @@ unsigned V3Options::dumpLevel(const string& tag) const VL_MT_SAFE { return iter != m_dumpLevel.end() ? iter->second : 0; } -unsigned V3Options::dumpSrcLevel(const string& srcfile_path) const VL_MT_SAFE { +unsigned V3Options::dumpSrcLevel(const string& srcfile_path) const { // For simplicity, calling functions can just use __FILE__ for srcfile. // That means we need to strip the filenames: ../Foo.cpp -> Foo return dumpLevel(V3Os::filenameNonDirExt(srcfile_path)); diff --git a/src/V3Options.h b/src/V3Options.h index aa681b3ea..360c28b53 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -22,6 +22,7 @@ #include "V3Error.h" #include "V3LangCode.h" +#include "V3Mutex.h" #include #include @@ -262,6 +263,7 @@ 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_quietExit = false; // main switch: --quiet-exit bool m_relativeIncludes = false; // main switch: --relative-includes bool m_reportUnoptflat = false; // main switch: --report-unoptflat @@ -302,6 +304,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_publicDepth = 0; // main switch: --public-depth int m_reloopLimit = 40; // main switch: --reloop-limit VOptionBool m_skipIdentical; // main switch: --skip-identical int m_threads = 1; // main switch: --threads @@ -405,7 +408,7 @@ public: unsigned debugLevel(const string& tag) const VL_MT_SAFE; unsigned debugSrcLevel(const string& srcfile_path) const VL_MT_SAFE; unsigned dumpLevel(const string& tag) const VL_MT_SAFE; - unsigned dumpSrcLevel(const string& srcfile_path) const VL_MT_SAFE; + unsigned dumpSrcLevel(const string& srcfile_path) const; // METHODS void addCppFile(const string& filename); @@ -492,6 +495,7 @@ 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 publicFlatRW() const { return m_publicFlatRW; } bool lintOnly() const VL_MT_SAFE { return m_lintOnly; } bool ignc() const { return m_ignc; } @@ -519,6 +523,7 @@ public: int outputSplitCFuncs() const { return m_outputSplitCFuncs; } int outputSplitCTrace() const { return m_outputSplitCTrace; } int pinsBv() const { return m_pinsBv; } + int publicDepth() const { return m_publicDepth; } int reloopLimit() const { return m_reloopLimit; } VOptionBool skipIdentical() const { return m_skipIdentical; } int threads() const VL_MT_SAFE { return m_threads; } @@ -569,7 +574,7 @@ public: string prefix() const VL_MT_SAFE { return m_prefix; } // Not just called protectKey() to avoid bugs of not using protectKeyDefaulted() bool protectKeyProvided() const { return !m_protectKey.empty(); } - string protectKeyDefaulted(); // Set default key if not set by user + string protectKeyDefaulted() VL_MT_SAFE; // Set default key if not set by user string topModule() const { return m_topModule; } string unusedRegexp() const { return m_unusedRegexp; } string waiverOutput() const { return m_waiverOutput; } @@ -646,7 +651,7 @@ public: } // METHODS (from main) - static string version(); + static string version() VL_PURE; static string argString(int argc, char** argv); ///< Return list of arguments as simple string string allArgsString() const VL_MT_SAFE; ///< Return all passed arguments as simple string // Return options for child hierarchical blocks when forTop==false, otherwise returns args for diff --git a/src/V3Order.cpp b/src/V3Order.cpp index a81a8afe0..dc1905f97 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -510,7 +510,7 @@ public: } } string name() const { - return (string("MDS:") + " d=" + cvtToHex(domainp()) + " s=" + cvtToHex(scopep())); + return string{"MDS:"} + " d=" + cvtToHex(domainp()) + " s=" + cvtToHex(scopep()); } }; diff --git a/src/V3OrderGraph.h b/src/V3OrderGraph.h index 5cc0ea85b..d51a540a0 100644 --- a/src/V3OrderGraph.h +++ b/src/V3OrderGraph.h @@ -123,7 +123,7 @@ public: virtual bool domainMatters() = 0; // ACCESSORS - AstSenTree* domainp() const { return m_domainp; } + AstSenTree* domainp() const VL_MT_STABLE { return m_domainp; } void domainp(AstSenTree* domainp) { #if VL_DEBUG UASSERT(!m_domainp, "Domain should only be set once"); @@ -154,12 +154,12 @@ public: bool domainMatters() override { return true; } // ACCESSORS - AstNode* nodep() const { return m_nodep; } - AstScope* scopep() const { return m_scopep; } + AstNode* nodep() const VL_MT_STABLE { return m_nodep; } + AstScope* scopep() const VL_MT_STABLE { return m_scopep; } AstSenTree* hybridp() const { return m_hybridp; } // LCOV_EXCL_START // Debug code - string name() const override { + string name() const override VL_MT_STABLE { return (cvtToHex(m_nodep) + "\\n " + cvtToStr(nodep()->typeName())); } string dotShape() const override { return VN_IS(m_nodep, Active) ? "doubleoctagon" : "rect"; } @@ -181,8 +181,8 @@ public: // LCOV_EXCL_START // Debug code string dotShape() const override final { return "ellipse"; } - virtual string nameSuffix() const = 0; - string name() const override final { + virtual string nameSuffix() const VL_MT_SAFE = 0; + string name() const override final VL_MT_STABLE { return cvtToHex(m_vscp) + " " + nameSuffix() + "\\n " + m_vscp->name(); } // LCOV_EXCL_STOP @@ -199,7 +199,7 @@ public: bool domainMatters() override { return true; } // LCOV_EXCL_START // Debug code - string nameSuffix() const override { return ""; } + string nameSuffix() const override VL_MT_SAFE { return ""; } string dotColor() const override { return "grey"; } // LCOV_EXCL_STOP }; @@ -215,7 +215,7 @@ public: bool domainMatters() override { return false; } // LCOV_EXCL_START // Debug code - string nameSuffix() const override { return "PRE"; } + string nameSuffix() const override VL_MT_SAFE { return "PRE"; } string dotColor() const override { return "green"; } // LCOV_EXCL_STOP }; @@ -231,7 +231,7 @@ public: bool domainMatters() override { return false; } // LCOV_EXCL_START // Debug code - string nameSuffix() const override { return "POST"; } + string nameSuffix() const override VL_MT_SAFE { return "POST"; } string dotColor() const override { return "red"; } // LCOV_EXCL_STOP }; @@ -247,7 +247,7 @@ public: bool domainMatters() override { return false; } // LCOV_EXCL_START // Debug code - string nameSuffix() const override { return "PORD"; } + string nameSuffix() const override VL_MT_SAFE { return "PORD"; } string dotColor() const override { return "blue"; } // LCOV_EXCL_STOP }; diff --git a/src/V3OrderMoveGraph.h b/src/V3OrderMoveGraph.h index c0187a68f..fb54ac7a0 100644 --- a/src/V3OrderMoveGraph.h +++ b/src/V3OrderMoveGraph.h @@ -64,18 +64,18 @@ public: } } - string name() const override { + string name() const override VL_MT_STABLE { string nm; if (VL_UNCOVERABLE(!logicp())) { // Avoid crash when debugging nm = "nul"; // LCOV_EXCL_LINE } else { nm = logicp()->name(); - nm += (string("\\nMV:") + " d=" + cvtToHex(logicp()->domainp()) + nm += (string{"\\nMV:"} + " d=" + cvtToHex(logicp()->domainp()) + " s=" + cvtToHex(logicp()->scopep())); } return nm; } - OrderLogicVertex* logicp() const { return m_logicp; } + OrderLogicVertex* logicp() const VL_MT_STABLE { return m_logicp; } bool isWait() const { return m_state == POM_WAIT; } void setReady() { UASSERT(m_state == POM_WAIT, "Wait->Ready on node not in proper state"); @@ -127,7 +127,7 @@ public: string nm; if (logicp()) { nm = logicp()->name(); - nm += (string("\\nMV:") + " d=" + cvtToHex(logicp()->domainp()) + " s=" + nm += (string{"\\nMV:"} + " d=" + cvtToHex(logicp()->domainp()) + " s=" + cvtToHex(logicp()->scopep()) // "color()" represents the mtask ID. + "\\nt=" + cvtToStr(color())); diff --git a/src/V3Os.cpp b/src/V3Os.cpp index a18dd6c2f..4a80f98ac 100644 --- a/src/V3Os.cpp +++ b/src/V3Os.cpp @@ -142,7 +142,7 @@ string V3Os::filenameDir(const string& filename) { } } -string V3Os::filenameNonDir(const string& filename) { +string V3Os::filenameNonDir(const string& filename) VL_PURE { string::size_type pos; if ((pos = filename.rfind('/')) != string::npos) { return filename.substr(pos + 1); @@ -151,7 +151,7 @@ string V3Os::filenameNonDir(const string& filename) { } } -string V3Os::filenameNonExt(const string& filename) { +string V3Os::filenameNonExt(const string& filename) VL_PURE { string base = filenameNonDir(filename); string::size_type pos; if ((pos = base.find('.')) != string::npos) base.erase(pos); @@ -159,7 +159,7 @@ string V3Os::filenameNonExt(const string& filename) { } string V3Os::filenameSubstitute(const string& filename) { - string out; + string result; // cppcheck-has-bug-suppress unusedLabel enum : uint8_t { NONE, PAREN, CURLY } brackets = NONE; for (string::size_type pos = 0; pos < filename.length(); ++pos) { @@ -186,20 +186,20 @@ string V3Os::filenameSubstitute(const string& filename) { string envvalue; if (!envvar.empty()) envvalue = getenvStr(envvar, ""); if (!envvalue.empty()) { - out += envvalue; + result += envvalue; if (brackets == NONE) { pos = endpos; } else { pos = endpos + 1; } } else { - out += filename[pos]; // *pos == '$' + result += filename[pos]; // *pos == '$' } } else { - out += filename[pos]; + result += filename[pos]; } } - return out; + return result; } string V3Os::filenameRealPath(const string& filename) { @@ -286,7 +286,7 @@ uint64_t V3Os::rand64(std::array& stater) { return result; } -string V3Os::trueRandom(size_t size) { +string V3Os::trueRandom(size_t size) VL_MT_SAFE { string result(size, '\xFF'); char* const data = const_cast(result.data()); // Note: std::string.data() returns a non-const Char* from C++17 onwards. diff --git a/src/V3Os.h b/src/V3Os.h index 6b73aa3b5..4f4d44bb9 100644 --- a/src/V3Os.h +++ b/src/V3Os.h @@ -37,10 +37,11 @@ public: // METHODS (generic filename utilities) static string filenameFromDirBase(const string& dir, const string& basename); /// Return non-directory part of filename - static string filenameNonDir(const string& filename); + static string filenameNonDir(const string& filename) VL_PURE; /// Return non-extensioned (no .) part of filename - static string filenameNonExt(const string& filename); - static string filenameNonDirExt(const string& filename) { ///< Return basename of filename + static string filenameNonExt(const string& filename) VL_PURE; + ///< Return basename of filename + static string filenameNonDirExt(const string& filename) VL_PURE { return filenameNonExt(filenameNonDir(filename)); } static string filenameDir(const string& filename); ///< Return directory part of filename @@ -58,7 +59,7 @@ public: // METHODS (random) static uint64_t rand64(std::array& stater); - static string trueRandom(size_t size); + static string trueRandom(size_t size) VL_MT_SAFE; // METHODS (time & performance) static void u_sleep(int64_t usec); ///< Sleep for a given number of microseconds. diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 287968268..e15d38834 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -342,6 +342,7 @@ class ParamProcessor final { // TODO: This parameter value number lookup via a constructed key string is not // particularly robust for type parameters. We should really have a type // equivalence predicate function. + if (const AstRefDType* const refp = VN_CAST(nodep, RefDType)) nodep = refp->skipRefp(); const string key = paramValueKey(nodep); // cppcheck-has-bug-suppress unreadVariable V3Hash hash = V3Hasher::uncachedHash(nodep); @@ -545,6 +546,9 @@ class ParamProcessor final { } void replaceRefsRecurse(AstNode* const nodep, const AstClass* const oldClassp, AstClass* const newClassp) { + // Some of the nodes may be already marked as visited, because they were copied. They + // should be marked as unvisited, because parameterized references have to be handled. + nodep->user5(false); if (AstClassRefDType* const classRefp = VN_CAST(nodep, ClassRefDType)) { if (classRefp->classp() == oldClassp) classRefp->classp(newClassp); } else if (AstClassOrPackageRef* const classRefp = VN_CAST(nodep, ClassOrPackageRef)) { @@ -563,8 +567,9 @@ class ParamProcessor final { // Note all module internal variables will be re-linked to the new modules by clone // However links outside the module (like on the upper cells) will not. AstNodeModule* const newmodp = srcModp->cloneTree(false); - if (VN_IS(srcModp, Class)) { - replaceRefsRecurse(newmodp->stmtsp(), VN_AS(newmodp, Class), VN_AS(srcModp, Class)); + if (AstClass* const newClassp = VN_CAST(newmodp, Class)) { + newClassp->isParameterized(false); + replaceRefsRecurse(newmodp->stmtsp(), newClassp, VN_AS(srcModp, Class)); } newmodp->name(newname); @@ -705,7 +710,7 @@ class ParamProcessor final { << modvarp->prettyNameQ()); } else { UINFO(9, "Parameter type assignment expr=" << exprp << " to " << origp << endl); - if (exprp->sameTree(origp)) { + if (exprp->similarDType(origp)) { // Setting parameter to its default value. Just ignore it. // This prevents making additional modules, and makes coverage more // obvious as it won't show up under a unique module page name. @@ -805,6 +810,9 @@ class ParamProcessor final { if (!any_overrides) { UINFO(8, "Cell parameters all match original values, skipping expansion.\n"); + // Mark that the defeult instance is used. + // It will be checked only if srcModpr is a class. + srcModpr->user2(true); } else if (AstNodeModule* const paramedModp = m_hierBlocks.findByParams(srcModpr->name(), paramsp, m_modp)) { paramedModp->dead(false); @@ -912,7 +920,7 @@ public: class ParamVisitor final : public VNVisitor { // NODE STATE // AstNodeModule::user1 -> bool: already fixed level - + // AstClass::user2 -> bool: Referenced (value read only in parameterized classes) // STATE ParamProcessor m_processor; // De-parameterize a cell, build modules UnrollStateful m_unroller; // Loop unroller @@ -924,6 +932,7 @@ class ParamVisitor final : public VNVisitor { std::vector m_dots; // Dot references to process std::multimap m_cellps; // Cells left to process (in current module) std::multimap m_workQueue; // Modules left to process + std::vector m_paramClasses; // Parameterized classes // Map from AstNodeModule to set of all AstNodeModules that instantiates it. std::unordered_map> m_parentps; @@ -945,16 +954,18 @@ class ParamVisitor final : public VNVisitor { // Process once; note user5 will be cleared on specialization, so we will do the // specialized module if needed - if (modp->user5SetOnce()) continue; + if (!modp->user5SetOnce()) { - // TODO: this really should be an assert, but classes and hier_blocks are special... - if (modp->someInstanceName().empty()) modp->someInstanceName(modp->origName()); + // TODO: this really should be an assert, but classes and hier_blocks are + // special... + if (modp->someInstanceName().empty()) modp->someInstanceName(modp->origName()); - // Iterate the body - { - VL_RESTORER(m_modp); - m_modp = modp; - iterateChildren(modp); + // Iterate the body + { + VL_RESTORER(m_modp); + m_modp = modp; + iterateChildren(modp); + } } // Process interface cells, then non-interface cells, which may reference an interface @@ -1022,7 +1033,7 @@ class ParamVisitor final : public VNVisitor { m_cellps.emplace(!isIface, nodep); } - // RHSs of AstDots need a relink when LHS is a parametrized class reference + // RHSs of AstDots need a relink when LHS is a parameterized class reference void relinkDots() { for (AstDot* const dotp : m_dots) { const AstClassOrPackageRef* const classRefp = VN_AS(dotp->lhsp(), ClassOrPackageRef); @@ -1041,6 +1052,14 @@ class ParamVisitor final : public VNVisitor { void visit(AstNodeModule* nodep) override { if (nodep->recursiveClone()) nodep->dead(true); // Fake, made for recursive elimination if (nodep->dead()) return; // Marked by LinkDot (and above) + if (AstClass* const classp = VN_CAST(nodep, Class)) { + if (classp->isParameterized()) { + // Don't enter into a definition. + // If a class is used, it will be visited through a reference + m_paramClasses.push_back(classp); + return; + } + } if (m_iterateModule) { // Iterating body UINFO(4, " MOD-under-MOD. " << nodep << endl); @@ -1357,6 +1376,17 @@ public: // Re-insert modules for (AstNodeModule* const modp : modps) netlistp->addModulesp(modp); + + for (AstClass* const classp : m_paramClasses) { + if (!classp->user2()) { + // Unreferenced, so it can be removed + VL_DO_DANGLING(pushDeletep(classp->unlinkFrBack()), classp); + } else { + // Referenced. classp became a specialized class with the default + // values of parameters and is not a parameterized class anymore + classp->isParameterized(false); + } + } } } ~ParamVisitor() override = default; diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index b310beb96..cbf1808d5 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -554,7 +554,7 @@ void V3ParseImp::tokenPipelineSym() { } if (foundp && !v3Global.usesStdPackage()) { AstPackageImport* const impp - = new AstPackageImport(stdpkgp->fileline(), stdpkgp, "*"); + = new AstPackageImport{stdpkgp->fileline(), stdpkgp, "*"}; unitPackage(stdpkgp->fileline())->addStmtsp(impp); v3Global.setUsesStdPackage(); } diff --git a/src/V3Partition.cpp b/src/V3Partition.cpp index 82b41ca32..a10ffec1c 100644 --- a/src/V3Partition.cpp +++ b/src/V3Partition.cpp @@ -268,7 +268,7 @@ public: uint32_t id() const override { return m_serialId; } void id(uint32_t id) { m_serialId = id; } // Abstract cost of every logic mtask - uint32_t cost() const override { return m_cost; } + uint32_t cost() const override VL_MT_SAFE { return m_cost; } void setCost(uint32_t cost) { m_cost = cost; } // For tests only uint32_t stepCost() const { return stepCost(m_cost); } static uint32_t stepCost(uint32_t cost) { @@ -321,7 +321,7 @@ public: void checkRelativesCp(GraphWay way) const; - string name() const override { + string name() const override VL_MT_STABLE { // Display forward and reverse critical path costs. This gives a quick // read on whether graph partitioning looks reasonable or bad. std::ostringstream out; @@ -3129,8 +3129,8 @@ static const std::vector createThreadFunctions(const ThreadSchedule& funcp->argTypes("void* voidSelf, bool even_cycle"); // Setup vlSelf an vlSyms - funcp->addStmtsp(new AstCStmt{fl, EmitCBaseVisitor::voidSelfAssign(modp)}); - funcp->addStmtsp(new AstCStmt{fl, EmitCBaseVisitor::symClassAssign()}); + funcp->addStmtsp(new AstCStmt{fl, EmitCBase::voidSelfAssign(modp)}); + funcp->addStmtsp(new AstCStmt{fl, EmitCBase::symClassAssign()}); // Invoke each mtask scheduled to this thread from the thread function for (const ExecMTask* const mtaskp : thread) { diff --git a/src/V3PartitionGraph.h b/src/V3PartitionGraph.h index fc16c81c6..0827ca381 100644 --- a/src/V3PartitionGraph.h +++ b/src/V3PartitionGraph.h @@ -72,7 +72,7 @@ public: , m_bodyp{bodyp} , m_id{id} {} AstMTaskBody* bodyp() const { return m_bodyp; } - uint32_t id() const override { return m_id; } + uint32_t id() const override VL_MT_SAFE { return m_id; } uint32_t priority() const { return m_priority; } void priority(uint32_t pri) { m_priority = pri; } uint32_t cost() const override { return m_cost; } @@ -85,7 +85,7 @@ public: // If this MTask maps to a C function, this should be the name return std::string{"__Vmtask"} + "__" + cvtToStr(m_id); } - string name() const override { return std::string{"mt"} + cvtToStr(id()); } + string name() const override VL_MT_STABLE { return std::string{"mt"} + cvtToStr(id()); } string hashName() const { return m_hashName; } void hashName(const string& name) { m_hashName = name; } void dump(std::ostream& str) const { diff --git a/src/V3PreLex.l b/src/V3PreLex.l index 3168f7025..4524374b4 100644 --- a/src/V3PreLex.l +++ b/src/V3PreLex.l @@ -720,11 +720,11 @@ void V3PreLex::dumpStack() { } string V3PreLex::cleanDbgStrg(const string& in) { - string out = in; + string result = in; string::size_type pos; - while ((pos = out.find('\n')) != string::npos) { out.replace(pos, 1, "\\n"); } - while ((pos = out.find('\r')) != string::npos) { out.replace(pos, 1, "\\r"); } - return out; + while ((pos = result.find('\n')) != string::npos) result.replace(pos, 1, "\\n"); + while ((pos = result.find('\r')) != string::npos) result.replace(pos, 1, "\\r"); + return result; } void V3PreLex::unused() { diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index 540d1526d..5cc257f7e 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -287,6 +287,10 @@ private: iterateChildren(nodep); checkNode(nodep); } + void visit(AstRandRNG* nodep) override { + iterateChildren(nodep); + checkNode(nodep); + } void visit(AstUCFunc* nodep) override { iterateChildren(nodep); checkNode(nodep); diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 9dfb2a536..34c90bf9a 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -36,7 +36,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### // Visitor that marks classes needing a randomize() method -class RandomizeMarkVisitor final : public VNVisitor { +class RandomizeMarkVisitor final : public VNVisitorConst { private: // NODE STATE // Cleared on Netlist @@ -57,9 +57,11 @@ private: if (VN_IS(memberp, Var) && VN_AS(memberp, Var)->isRand()) { if (const auto* const classRefp = VN_CAST(memberp->dtypep(), ClassRefDType)) { auto* const rclassp = classRefp->classp(); - markMembers(rclassp); - markDerived(rclassp); - rclassp->user1(true); + if (!rclassp->user1()) { + rclassp->user1(true); + markMembers(rclassp); + markDerived(rclassp); + } } } } @@ -69,9 +71,11 @@ private: const auto it = m_baseToDerivedMap.find(nodep); if (it != m_baseToDerivedMap.end()) { for (auto* classp : it->second) { - classp->user1(true); - markMembers(classp); - markDerived(classp); + if (!classp->user1p()) { + classp->user1(true); + markMembers(classp); + markDerived(classp); + } } } } @@ -83,7 +87,7 @@ private: // VISITORS void visit(AstClass* nodep) override { - iterateChildren(nodep); + iterateChildrenConst(nodep); if (nodep->extendsp()) { // Save pointer to derived class AstClass* const basep = nodep->extendsp()->classp(); @@ -91,7 +95,7 @@ private: } } void visit(AstMethodCall* nodep) override { - iterateChildren(nodep); + iterateChildrenConst(nodep); if (nodep->name() != "randomize") return; if (const AstClassRefDType* const classRefp = VN_CAST(nodep->fromp()->dtypep(), ClassRefDType)) { @@ -101,12 +105,12 @@ private: } } - void visit(AstNode* nodep) override { iterateChildren(nodep); } + void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } public: // CONSTRUCTORS explicit RandomizeMarkVisitor(AstNetlist* nodep) { - iterate(nodep); + iterateConst(nodep); markAllDerived(); } ~RandomizeMarkVisitor() override = default; @@ -121,6 +125,7 @@ private: // Cleared on Netlist // AstClass::user1() -> bool. Set true to indicate needs randomize processing // AstEnumDType::user2() -> AstVar*. Pointer to table with enum values + // AstClass::user3() -> AstFunc*. Pointer to randomize() method of a class // VNUser1InUse m_inuser1; (Allocated for use in RandomizeMarkVisitor) const VNUser2InUse m_inuser2; @@ -183,15 +188,15 @@ private: AstVarRef* const tabRefp = new AstVarRef{fl, enumValueTabp(enumDtp), VAccess::READ}; tabRefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp()); - AstRand* const randp = new AstRand{fl, nullptr, false}; + AstRandRNG* const randp + = new AstRandRNG{fl, varrefp->findBasicDType(VBasicDTypeKwd::UINT32)}; AstNodeExpr* const moddivp = new AstModDiv{ fl, randp, new AstConst{fl, static_cast(enumDtp->itemCount())}}; - randp->dtypep(varrefp->findBasicDType(VBasicDTypeKwd::UINT32)); moddivp->dtypep(enumDtp); valp = new AstArraySel{fl, tabRefp, moddivp}; } else { - valp = new AstRand{fl, nullptr, false}; - valp->dtypep(memberp ? memberp->dtypep() : varrefp->varp()->dtypep()); + valp = new AstRandRNG{fl, + (memberp ? memberp->dtypep() : varrefp->varp()->dtypep())}; } return new AstAssign{fl, new AstSel{fl, varrefp, offset + (memberp ? memberp->lsb() : 0), @@ -230,41 +235,59 @@ private: if (!nodep->user1()) return; // Doesn't need randomize, or already processed UINFO(9, "Define randomize() for " << nodep << endl); AstFunc* const funcp = V3Randomize::newRandomizeFunc(nodep); + nodep->user3p(funcp); AstVar* const fvarp = VN_AS(funcp->fvarp(), Var); addPrePostCall(nodep, funcp, "pre_randomize"); - funcp->addStmtsp(new AstAssign{ - nodep->fileline(), new AstVarRef{nodep->fileline(), fvarp, VAccess::WRITE}, - new AstConst{nodep->fileline(), AstConst::WidthedValue{}, 32, 1}}); - for (AstClass* classp = nodep; classp; - classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr) { - for (auto* memberp = classp->stmtsp(); memberp; memberp = memberp->nextp()) { - AstVar* const memberVarp = VN_CAST(memberp, Var); - if (!memberVarp || !memberVarp->isRand()) continue; - const AstNodeDType* const dtypep = memberp->dtypep()->skipRefp(); - if (VN_IS(dtypep, BasicDType) || VN_IS(dtypep, StructDType)) { - AstVarRef* const refp - = new AstVarRef{nodep->fileline(), memberVarp, VAccess::WRITE}; - AstNodeStmt* const stmtp = newRandStmtsp(nodep->fileline(), refp); - funcp->addStmtsp(stmtp); - } else if (const auto* const classRefp = VN_CAST(dtypep, ClassRefDType)) { - AstVarRef* const refp - = new AstVarRef{nodep->fileline(), memberVarp, VAccess::WRITE}; - AstFunc* const memberFuncp - = V3Randomize::newRandomizeFunc(classRefp->classp()); - AstMethodCall* const callp - = new AstMethodCall{nodep->fileline(), refp, "randomize", nullptr}; - callp->taskp(memberFuncp); - callp->dtypeFrom(memberFuncp); - funcp->addStmtsp(new AstAssign{ - nodep->fileline(), new AstVarRef{nodep->fileline(), fvarp, VAccess::WRITE}, - new AstAnd{nodep->fileline(), - new AstVarRef{nodep->fileline(), fvarp, VAccess::READ}, - callp}}); - } else { - memberp->v3warn(E_UNSUPPORTED, - "Unsupported: random member variables with type " - << memberp->dtypep()->prettyDTypeNameQ()); + FileLine* fl = nodep->fileline(); + + AstNodeExpr* beginValp = nullptr; + if (nodep->extendsp()) { + // Call randomize() from the base class + AstFunc* const baseRandomizep = VN_AS(nodep->extendsp()->classp()->user3p(), Func); + if (baseRandomizep) { + AstFuncRef* const baseRandCallp = new AstFuncRef{fl, "randomize", nullptr}; + baseRandCallp->taskp(baseRandomizep); + baseRandCallp->dtypeFrom(baseRandomizep->dtypep()); + baseRandCallp->classOrPackagep(nodep->extendsp()->classp()); + beginValp = baseRandCallp; + } + } + if (!beginValp) beginValp = new AstConst{fl, AstConst::WidthedValue{}, 32, 1}; + + funcp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, fvarp, VAccess::WRITE}, beginValp}); + + for (auto* memberp = nodep->stmtsp(); memberp; memberp = memberp->nextp()) { + AstVar* const memberVarp = VN_CAST(memberp, Var); + if (!memberVarp || !memberVarp->isRand()) continue; + const AstNodeDType* const dtypep = memberp->dtypep()->skipRefp(); + if (VN_IS(dtypep, BasicDType) || VN_IS(dtypep, StructDType)) { + AstVarRef* const refp = new AstVarRef{fl, memberVarp, VAccess::WRITE}; + AstNodeStmt* const stmtp = newRandStmtsp(fl, refp); + funcp->addStmtsp(stmtp); + } else if (const auto* const classRefp = VN_CAST(dtypep, ClassRefDType)) { + if (classRefp->classp() == nodep) { + memberp->v3warn( + E_UNSUPPORTED, + "Unsupported: random member variable with type of a current class"); + continue; } + AstVarRef* const refp = new AstVarRef{fl, memberVarp, VAccess::WRITE}; + AstFunc* const memberFuncp = V3Randomize::newRandomizeFunc(classRefp->classp()); + AstMethodCall* const callp = new AstMethodCall{fl, refp, "randomize", nullptr}; + callp->taskp(memberFuncp); + callp->dtypeFrom(memberFuncp); + AstAssign* const assignp = new AstAssign{ + fl, new AstVarRef{fl, fvarp, VAccess::WRITE}, + new AstAnd{fl, new AstVarRef{fl, fvarp, VAccess::READ}, callp}}; + AstIf* const assignIfNotNullp + = new AstIf{fl, + new AstNeq{fl, new AstVarRef{fl, memberVarp, VAccess::READ}, + new AstConst{fl, AstConst::Null{}}}, + assignp}; + funcp->addStmtsp(assignIfNotNullp); + } else { + memberp->v3warn(E_UNSUPPORTED, "Unsupported: random member variable with type " + << memberp->dtypep()->prettyDTypeNameQ()); } } addPrePostCall(nodep, funcp, "post_randomize"); @@ -351,6 +374,7 @@ void V3Randomize::randomizeNetlist(AstNetlist* nodep) { AstFunc* V3Randomize::newRandomizeFunc(AstClass* nodep) { AstFunc* funcp = VN_AS(nodep->findMember("randomize"), Func); if (!funcp) { + v3Global.useRandomizeMethods(true); AstNodeDType* const dtypep = nodep->findBitDType(32, 32, VSigning::SIGNED); // IEEE says int return of 0/1 AstVar* const fvarp = new AstVar{nodep->fileline(), VVarType::MEMBER, "randomize", dtypep}; @@ -364,6 +388,31 @@ AstFunc* V3Randomize::newRandomizeFunc(AstClass* nodep) { funcp->isVirtual(nodep->isExtended()); nodep->addMembersp(funcp); nodep->repairCache(); + AstClass* const basep = nodep->baseMostClassp(); + basep->needRNG(true); + } + return funcp; +} + +AstFunc* V3Randomize::newSRandomFunc(AstClass* nodep) { + AstClass* const basep = nodep->baseMostClassp(); + AstFunc* funcp = VN_AS(basep->findMember("srandom"), Func); + if (!funcp) { + v3Global.useRandomizeMethods(true); + AstNodeDType* const dtypep + = basep->findBitDType(32, 32, VSigning::SIGNED); // IEEE says argument 0/1 + AstVar* const ivarp = new AstVar{basep->fileline(), VVarType::MEMBER, "seed", dtypep}; + ivarp->lifetime(VLifetime::AUTOMATIC); + ivarp->funcLocal(true); + ivarp->direction(VDirection::INPUT); + funcp = new AstFunc{basep->fileline(), "srandom", ivarp, nullptr}; + funcp->dtypep(basep->findVoidDType()); + funcp->classMethod(true); + funcp->isVirtual(false); + basep->addMembersp(funcp); + basep->repairCache(); + funcp->addStmtsp(new AstCStmt{basep->fileline(), "__Vm_rng.srandom(seed);\n"}); + basep->needRNG(true); } return funcp; } diff --git a/src/V3Randomize.h b/src/V3Randomize.h index ff56f9a86..223575203 100644 --- a/src/V3Randomize.h +++ b/src/V3Randomize.h @@ -29,6 +29,7 @@ public: static void randomizeNetlist(AstNetlist* nodep); static AstFunc* newRandomizeFunc(AstClass* nodep); + static AstFunc* newSRandomFunc(AstClass* nodep); }; #endif // Guard diff --git a/src/V3Reloop.cpp b/src/V3Reloop.cpp index 5f605ebcf..bd2d110c0 100644 --- a/src/V3Reloop.cpp +++ b/src/V3Reloop.cpp @@ -75,7 +75,10 @@ private: const string newvarname{"__Vilp"}; varp = new AstVar{fl, VVarType::STMTTEMP, newvarname, VFlagLogicPacked{}, 32}; UASSERT_OBJ(cfuncp, fl, "Assignment not under a function"); - cfuncp->addInitsp(varp); + if (cfuncp->initsp()) + cfuncp->initsp()->addNextHere(varp); + else + cfuncp->addInitsp(varp); cfuncp->user1p(varp); } return varp; diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index 601b82c7b..f9ac9cc8e 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -311,27 +311,26 @@ struct TriggerKit { // No VL_UNCOPYABLE(TriggerKit) as causes C++20 errors on MSVC // Utility that assigns the given index trigger to fire when the given variable is zero - void addFirstIterationTriggerAssignment(AstVarScope* counterp, uint32_t /*index*/) const { + void addFirstIterationTriggerAssignment(AstVarScope* counterp, uint32_t index) const { FileLine* const flp = counterp->fileline(); AstVarRef* const vrefp = new AstVarRef{flp, m_vscp, VAccess::WRITE}; - AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, "at", new AstConst{flp, 0}}; - callp->dtypeSetBit(); - callp->pure(true); - m_funcp->stmtsp()->addHereThisAsNext(new AstAssign{ - flp, callp, - new AstEq{flp, new AstVarRef{flp, counterp, VAccess::READ}, new AstConst{flp, 0}}}); + AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, "set"}; + callp->addPinsp(new AstConst{flp, index}); + callp->addPinsp( + new AstEq{flp, new AstVarRef{flp, counterp, VAccess::READ}, new AstConst{flp, 0}}); + callp->dtypeSetVoid(); + m_funcp->stmtsp()->addHereThisAsNext(callp->makeStmt()); } // Utility to set then clear the dpiExportTrigger trigger void addDpiExportTriggerAssignment(AstVarScope* dpiExportTriggerVscp, uint32_t index) const { FileLine* const flp = dpiExportTriggerVscp->fileline(); AstVarRef* const vrefp = new AstVarRef{flp, m_vscp, VAccess::WRITE}; - AstCMethodHard* const callp - = new AstCMethodHard{flp, vrefp, "at", new AstConst{flp, index}}; - callp->dtypeSetBit(); - callp->pure(true); - AstNode* stmtp - = new AstAssign{flp, callp, new AstVarRef{flp, dpiExportTriggerVscp, VAccess::READ}}; + AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, "set"}; + callp->addPinsp(new AstConst{flp, index}); + callp->addPinsp(new AstVarRef{flp, dpiExportTriggerVscp, VAccess::READ}); + callp->dtypeSetVoid(); + AstNode* const stmtp = callp->makeStmt(); stmtp->addNext(new AstAssign{flp, new AstVarRef{flp, dpiExportTriggerVscp, VAccess::WRITE}, new AstConst{flp, AstConst::BitFalse{}}}); m_funcp->stmtsp()->addHereThisAsNext(stmtp); @@ -354,13 +353,19 @@ struct EvalKit { // Create an AstSenTree that is sensitive to the given trigger index. Must not exist yet! AstSenTree* createTriggerSenTree(AstNetlist* netlistp, AstVarScope* const vscp, uint32_t index) { + UASSERT_OBJ(index != std::numeric_limits::max(), netlistp, "Invalid trigger index"); AstTopScope* const topScopep = netlistp->topScopep(); FileLine* const flp = topScopep->fileline(); AstVarRef* const vrefp = new AstVarRef{flp, vscp, VAccess::READ}; - AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, "at", new AstConst{flp, index}}; - callp->dtypeSetBit(); + const uint32_t wordIndex = index / 64; + const uint32_t bitIndex = index % 64; + AstCMethodHard* const callp + = new AstCMethodHard{flp, vrefp, "word", new AstConst{flp, wordIndex}}; + callp->dtypeSetUInt64(); callp->pure(true); - AstSenItem* const senItemp = new AstSenItem{flp, VEdgeType::ET_TRUE, callp}; + AstNodeExpr* const termp + = new AstAnd{flp, new AstConst{flp, AstConst::Unsized64{}, 1ULL << bitIndex}, callp}; + AstSenItem* const senItemp = new AstSenItem{flp, VEdgeType::ET_TRUE, termp}; AstSenTree* const resultp = new AstSenTree{flp, senItemp}; topScopep->addSenTreesp(resultp); return resultp; @@ -425,14 +430,28 @@ const TriggerKit createTriggers(AstNetlist* netlistp, AstCFunc* const initFuncp, new AstText{flp, "VL_DBG_MSGF(\" No triggers active\\n\");\n", true}); } + // Set the given trigger to the given value + const auto setTrig = [&](uint32_t index, AstNodeExpr* valp) { + AstVarRef* const vrefp = new AstVarRef{flp, vscp, VAccess::WRITE}; + AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, "set"}; + callp->addPinsp(new AstConst{flp, index}); + callp->addPinsp(valp); + callp->dtypeSetVoid(); + return callp->makeStmt(); + }; + // Create a reference to a trigger flag - const auto getTrigRef = [&](uint32_t index, VAccess access) { - AstVarRef* const vrefp = new AstVarRef{flp, vscp, access}; - AstConst* const idxp = new AstConst{flp, index}; - AstCMethodHard* callp = new AstCMethodHard{flp, vrefp, "at", idxp}; - callp->dtypeSetBit(); + const auto getTrig = [&](uint32_t index) { + AstVarRef* const vrefp = new AstVarRef{flp, vscp, VAccess::READ}; + const uint32_t wordIndex = index / 64; + const uint32_t bitIndex = index % 64; + AstCMethodHard* const callp + = new AstCMethodHard{flp, vrefp, "word", new AstConst{flp, wordIndex}}; + callp->dtypeSetUInt64(); callp->pure(true); - return callp; + AstNodeExpr* const termp + = new AstAnd{flp, new AstConst{flp, AstConst::Unsized64{}, 1ULL << bitIndex}, callp}; + return termp; }; // Add a debug dumping statement for this trigger @@ -444,7 +463,7 @@ const TriggerKit createTriggers(AstNetlist* netlistp, AstCFunc* const initFuncp, ss << "\\n\");\n"; const string message{ss.str()}; - AstIf* const ifp = new AstIf{flp, getTrigRef(index, VAccess::READ)}; + AstIf* const ifp = new AstIf{flp, getTrig(index)}; dumpp->addStmtsp(ifp); ifp->addThensp(new AstText{flp, message, true}); }; @@ -456,13 +475,13 @@ const TriggerKit createTriggers(AstNetlist* netlistp, AstCFunc* const initFuncp, // Add trigger computation uint32_t triggerNumber = extraTriggers.size(); - AstNode* initialTrigsp = nullptr; + AstNodeStmt* initialTrigsp = nullptr; for (const AstSenTree* const senTreep : senTreeps) { UASSERT_OBJ(senTreep->hasClocked() || senTreep->hasHybrid(), senTreep, "Cannot create trigger expression for non-clocked sensitivity"); - // Create the trigger AstSenTrees and associate it with the original AstSenTree - AstCMethodHard* const senp = getTrigRef(triggerNumber, VAccess::READ); + // Create the trigger AstSenTrees and associate them with the original AstSenTree + AstNodeExpr* const senp = getTrig(triggerNumber); AstSenItem* const senItemp = new AstSenItem{flp, VEdgeType::ET_TRUE, senp}; AstSenTree* const trigpSenp = new AstSenTree{flp, senItemp}; topScopep->addSenTreesp(trigpSenp); @@ -470,14 +489,12 @@ const TriggerKit createTriggers(AstNetlist* netlistp, AstCFunc* const initFuncp, // Add the trigger computation const auto& pair = senExprBuilder.build(senTreep); - funcp->addStmtsp( - new AstAssign{flp, getTrigRef(triggerNumber, VAccess::WRITE), pair.first}); + funcp->addStmtsp(setTrig(triggerNumber, pair.first)); // Add initialization time trigger if (pair.second || v3Global.opt.xInitialEdge()) { - AstNode* const assignp = new AstAssign{flp, getTrigRef(triggerNumber, VAccess::WRITE), - new AstConst{flp, 1}}; - initialTrigsp = AstNode::addNext(initialTrigsp, assignp); + initialTrigsp + = AstNode::addNext(initialTrigsp, setTrig(triggerNumber, new AstConst{flp, 1})); } // Add a debug statement for this trigger @@ -607,7 +624,7 @@ std::pair makeEvalLoop(AstNetlist* netlistp, const s AstTextBlock* const blockp = new AstTextBlock{flp}; failp->addThensp(blockp); FileLine* const locp = netlistp->topModulep()->fileline(); - const string& file = EmitCBaseVisitor::protect(locp->filename()); + const string& file = VIdProtect::protect(locp->filename()); const string& line = cvtToStr(locp->lineno()); const auto add = [&](const string& text) { blockp->addText(flp, text, true); }; add("#ifdef VL_DEBUG\n"); @@ -751,7 +768,8 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp, // The DPI Export trigger AstSenTree* const dpiExportTriggered - = createTriggerSenTree(netlistp, trig.m_vscp, dpiExportTriggerIndex); + = dpiExportTriggerVscp ? createTriggerSenTree(netlistp, trig.m_vscp, dpiExportTriggerIndex) + : nullptr; // Create and Order the body function AstCFunc* const icoFuncp @@ -800,7 +818,7 @@ AstStmtExpr* createTriggerSetCall(FileLine* const flp, AstVarScope* const toVscp AstVarScope* const fromVscp) { AstVarRef* const lhsp = new AstVarRef{flp, toVscp, VAccess::WRITE}; AstVarRef* const argp = new AstVarRef{flp, fromVscp, VAccess::READ}; - AstCMethodHard* const callp = new AstCMethodHard{flp, lhsp, "set", argp}; + AstCMethodHard* const callp = new AstCMethodHard{flp, lhsp, "thisOr", argp}; callp->dtypeSetVoid(); return callp->makeStmt(); } @@ -1111,7 +1129,9 @@ void schedule(AstNetlist* netlistp) { // The DPI Export trigger AstSenTree AstSenTree* const dpiExportTriggeredAct - = createTriggerSenTree(netlistp, actTrig.m_vscp, dpiExportTriggerIndex); + = dpiExportTriggerVscp + ? createTriggerSenTree(netlistp, actTrig.m_vscp, dpiExportTriggerIndex) + : nullptr; AstCFunc* const actFuncp = V3Order::order( netlistp, {&logicRegions.m_pre, &logicRegions.m_act, &logicReplicas.m_act}, trigToSenAct, @@ -1139,7 +1159,9 @@ void schedule(AstNetlist* netlistp) { invertAndMergeSenTreeMap(trigToSen, trigMap); AstSenTree* const dpiExportTriggered - = createTriggerSenTree(netlistp, trigVscp, dpiExportTriggerIndex); + = dpiExportTriggerVscp + ? createTriggerSenTree(netlistp, trigVscp, dpiExportTriggerIndex) + : nullptr; const auto& timingDomains = timingKit.remapDomains(trigMap); AstCFunc* const funcp = V3Order::order( diff --git a/src/V3SchedAcyclic.cpp b/src/V3SchedAcyclic.cpp index 5e085f59d..ac9bec353 100644 --- a/src/V3SchedAcyclic.cpp +++ b/src/V3SchedAcyclic.cpp @@ -68,12 +68,16 @@ public: : V3GraphVertex{graphp} , m_logicp{logicp} , m_scopep{scopep} {} + V3GraphVertex* clone(V3Graph* graphp) const override { + return new LogicVertex{graphp, logicp(), scopep()}; + } + AstNode* logicp() const { return m_logicp; } AstScope* scopep() const { return m_scopep; } // LCOV_EXCL_START // Debug code - string name() const override { return m_logicp->fileline()->ascii(); }; - string dotShape() const override { return "rectangle"; } + string name() const override VL_MT_STABLE { return m_logicp->fileline()->ascii(); }; + string dotShape() const override { return "rectangle2"; } // LCOV_EXCL_STOP }; @@ -86,9 +90,10 @@ public: , m_vscp{vscp} {} AstVarScope* vscp() const { return m_vscp; } AstVar* varp() const { return m_vscp->varp(); } + V3GraphVertex* clone(V3Graph* graphp) const override { return new VarVertex{graphp, vscp()}; } // LCOV_EXCL_START // Debug code - string name() const override { return m_vscp->name(); } + string name() const override VL_MT_STABLE { return m_vscp->name(); } string dotShape() const override { return "ellipse"; } string dotColor() const override { return "blue"; } // LCOV_EXCL_STOP diff --git a/src/V3SchedPartition.cpp b/src/V3SchedPartition.cpp index 0234dab01..849ff16e7 100644 --- a/src/V3SchedPartition.cpp +++ b/src/V3SchedPartition.cpp @@ -89,7 +89,7 @@ public: AstNode* logicp() const { return m_logicp; } // LCOV_EXCL_START // Debug code - string name() const override { + string name() const override VL_MT_STABLE { return m_logicp->typeName() + ("\n" + m_logicp->fileline()->ascii()); }; string dotShape() const override { return "rectangle"; } @@ -105,7 +105,7 @@ public: , m_vscp{vscp} {} // LCOV_EXCL_START // Debug code - string name() const override { return m_vscp->name(); } + string name() const override VL_MT_STABLE { return m_vscp->name(); } string dotShape() const override { return m_vscp->scopep()->isTop() && m_vscp->varp()->isNonOutput() ? "invhouse" : "ellipse"; } diff --git a/src/V3SchedReplicate.cpp b/src/V3SchedReplicate.cpp index 0564bb60d..7db10cabf 100644 --- a/src/V3SchedReplicate.cpp +++ b/src/V3SchedReplicate.cpp @@ -109,7 +109,7 @@ public: RegionFlags assignedRegion() const { return m_assignedRegion; } // For graph dumping - string name() const override { return m_logicp->fileline()->ascii(); }; + string name() const override VL_MT_STABLE { return m_logicp->fileline()->ascii(); }; string dotShape() const override { return "rectangle"; } }; @@ -134,7 +134,7 @@ public: AstScope* scopep() const { return m_vscp->scopep(); } // For graph dumping - string name() const override { return m_vscp->name(); } + string name() const override VL_MT_STABLE { return m_vscp->name(); } string dotShape() const override { return varp()->isPrimaryInish() ? "invhouse" : "ellipse"; } }; diff --git a/src/V3SchedTiming.cpp b/src/V3SchedTiming.cpp index 212163164..0b3da2451 100644 --- a/src/V3SchedTiming.cpp +++ b/src/V3SchedTiming.cpp @@ -272,22 +272,20 @@ void transformForks(AstNetlist* const netlistp) { // If it a fork sync or an intra-assignment variable, pass it by value const bool passByValue = (dtypep && dtypep->isForkSync()) || VString::startsWith(varp->name(), "__Vintra"); - // Only handle vars passed by value or locals declared before the fork - if (!passByValue && (!varp->user1() || !varp->isFuncLocal())) return; if (passByValue) { // We can just pass it to the new function + } else if (!varp->user1() || !varp->isFuncLocal()) { + // Not func local, or not declared before the fork. Their lifetime is longer + // than the forked process. Skip + return; } else if (m_forkp->joinType().join()) { // If it's fork..join, we can refer to variables from the parent process - if (!m_funcp->user1SetOnce()) { // Only do this once per function - // Move all locals to the heap before the fork - AstCExpr* const nowp - = new AstCExpr{m_forkp->fileline(), "VlNow{}", 0, true}; - nowp->dtypeSetVoid(); // TODO: this is sloppy but harmless - AstCAwait* const awaitp = new AstCAwait{m_forkp->fileline(), nowp}; - awaitp->dtypeSetVoid(); - m_forkp->addHereThisAsNext(awaitp->makeStmt()); - } } else { + // TODO: It is possible to relax this by allowing the use of such variables up + // until the first await. Also, variables defined within a forked process + // (inside a begin) are extracted out by V3Begin, so they also trigger this + // error. Preventing this (or detecting such cases and moving the vars back) + // would also allow for using them freely. refp->v3warn(E_UNSUPPORTED, "Unsupported: variable local to a forking process " "accessed in a fork..join_any or fork..join_none"); return; @@ -305,7 +303,8 @@ void transformForks(AstNetlist* const netlistp) { = new AstVarScope{newvarp->fileline(), funcp->scopep(), newvarp}; funcp->scopep()->addVarsp(newvscp); vscp->user2p(newvscp); - callp->addArgsp(new AstVarRef{refp->fileline(), vscp, VAccess::READ}); + callp->addArgsp(new AstVarRef{ + refp->fileline(), vscp, passByValue ? VAccess::READ : VAccess::READWRITE}); } AstVarScope* const newvscp = VN_AS(vscp->user2p(), VarScope); refp->varScopep(newvscp); @@ -362,7 +361,7 @@ void transformForks(AstNetlist* const netlistp) { // If we're in a class, add a vlSymsp arg if (m_inClass) { newfuncp->addInitsp(new AstCStmt{nodep->fileline(), "VL_KEEP_THIS;\n"}); - newfuncp->argTypes(EmitCBaseVisitor::symClassVar()); + newfuncp->argTypes(EmitCBase::symClassVar()); callp->argTypes("vlSymsp"); } // Put the begin's statements in the function, delete the begin diff --git a/src/V3Scope.cpp b/src/V3Scope.cpp index e10bc172b..2dd807015 100644 --- a/src/V3Scope.cpp +++ b/src/V3Scope.cpp @@ -255,7 +255,7 @@ private: iterateChildren(nodep); if (nodep->varsp()) m_scopep->modp()->addStmtsp(nodep->varsp()->unlinkFrBackWithNext()); if (nodep->eventp()) m_scopep->modp()->addStmtsp(nodep->eventp()->unlinkFrBack()); - VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); } void visit(AstNodeFTask* nodep) override { // Add to list of blocks under this scope diff --git a/src/V3SenExprBuilder.h b/src/V3SenExprBuilder.h index 7188b22fd..86b09d03f 100644 --- a/src/V3SenExprBuilder.h +++ b/src/V3SenExprBuilder.h @@ -93,21 +93,22 @@ class SenExprBuilder final { FileLine* const flp = exprp->fileline(); const auto rdCurr = [=]() { return getCurr(exprp); }; + AstNode* scopeExprp = exprp; + if (AstVarRef* const refp = VN_CAST(exprp, VarRef)) scopeExprp = refp->varScopep(); // Create the 'previous value' variable - auto it = m_prev.find(*exprp); + auto it = m_prev.find(*scopeExprp); if (it == m_prev.end()) { - // For readability, use the scoped signal name if the trigger is a simple AstVarRef - string name; - if (AstVarRef* const refp = VN_CAST(exprp, VarRef)) { - AstVarScope* vscp = refp->varScopep(); - name = "__Vtrigrprev__" + vscp->scopep()->nameDotless() + "__" - + vscp->varp()->name(); - } else { - name = m_prevNames.get(exprp); - } - AstVarScope* prevp; if (m_scopep->isTop()) { + // For readability, use the scoped signal name if the trigger is a simple AstVarRef + string name; + if (AstVarRef* const refp = VN_CAST(exprp, VarRef)) { + AstVarScope* const vscp = refp->varScopep(); + name = "__" + vscp->scopep()->nameDotless() + "__" + vscp->varp()->name(); + name = m_prevNames.get(name); + } else { + name = m_prevNames.get(exprp); + } prevp = m_scopep->createTemp(name, exprp->dtypep()); } else { AstVar* const varp = new AstVar{flp, VVarType::BLOCKTEMP, m_prevNames.get(exprp), @@ -117,7 +118,7 @@ class SenExprBuilder final { prevp = new AstVarScope{flp, m_scopep, varp}; m_scopep->addVarsp(prevp); } - it = m_prev.emplace(*exprp, prevp).first; + it = m_prev.emplace(*scopeExprp, prevp).first; // Add the initializer init AstAssign* const initp = new AstAssign{flp, new AstVarRef{flp, prevp, VAccess::WRITE}, diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 7668b453d..05fac3e7a 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -64,7 +64,7 @@ public: ~SimStackNode() = default; }; -class SimulateVisitor VL_NOT_FINAL : public VNVisitor { +class SimulateVisitor VL_NOT_FINAL : public VNVisitorConst { // Simulate a node tree, returning value of variables // Two major operating modes: // Test the tree to see if it is conformant @@ -103,7 +103,7 @@ private: bool m_isOutputter; // Creates output int m_instrCount; ///< Number of nodes int m_dataCount; ///< Bytes of data - AstJumpGo* m_jumpp; ///< Jump label we're branching from + AstJumpGo* m_jumpp = nullptr; ///< Jump label we're branching from // Simulating: std::unordered_map> m_constps; ///< Lists of all AstConst* allocated per dtype @@ -400,7 +400,7 @@ private: void visit(AstAlways* nodep) override { if (jumpingOver(nodep)) return; checkNodeInfo(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstSenTree* nodep) override { // Sensitivities aren't inputs per se; we'll keep our tree under the same sens. @@ -409,7 +409,7 @@ private: if (jumpingOver(nodep)) return; if (!optimizable()) return; // Accelerate UASSERT_OBJ(nodep->varp(), nodep, "Unlinked"); - iterateChildren(nodep->varp()); + iterateChildrenConst(nodep->varp()); AstNode* const vscp = varOrScope(nodep); // We can't have non-delayed assignments with same value on LHS and RHS @@ -502,7 +502,7 @@ private: clearOptimizable(nodep, "Constant function called under generate"); } checkNodeInfo(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstInitialStatic* nodep) override { if (jumpingOver(nodep)) return; @@ -511,21 +511,21 @@ private: return; } checkNodeInfo(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstNodeIf* nodep) override { if (jumpingOver(nodep)) return; UINFO(5, " IF " << nodep << endl); checkNodeInfo(nodep); if (m_checkOnly) { - iterateChildren(nodep); + iterateChildrenConst(nodep); } else { - iterateAndNextNull(nodep->condp()); + iterateAndNextConstNull(nodep->condp()); if (optimizable()) { if (fetchConst(nodep->condp())->num().isNeqZero()) { - iterateAndNextNull(nodep->thensp()); + iterateAndNextConstNull(nodep->thensp()); } else { - iterateAndNextNull(nodep->elsesp()); + iterateAndNextConstNull(nodep->elsesp()); } } } @@ -544,7 +544,7 @@ private: if (!m_checkOnly && optimizable()) { AstNode* const valuep = nodep->itemp()->valuep(); if (valuep) { - iterateAndNextNull(valuep); + iterateAndNextConstNull(valuep); if (optimizable()) newValue(nodep, fetchValue(valuep)); } else { clearOptimizable(nodep, "No value found for enum item"); // LCOV_EXCL_LINE @@ -554,7 +554,7 @@ private: void visit(AstNodeUniop* nodep) override { if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); if (!m_checkOnly && optimizable()) { nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num()); } @@ -562,7 +562,7 @@ private: void visit(AstNodeBiop* nodep) override { if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); if (!m_checkOnly && optimizable()) { nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num(), fetchConst(nodep->rhsp())->num()); @@ -571,7 +571,7 @@ private: void visit(AstNodeTriop* nodep) override { if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); if (!m_checkOnly && optimizable()) { nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num(), fetchConst(nodep->rhsp())->num(), @@ -581,7 +581,7 @@ private: void visit(AstNodeQuadop* nodep) override { if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); if (!m_checkOnly && optimizable()) { nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num(), fetchConst(nodep->rhsp())->num(), @@ -594,12 +594,12 @@ private: if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); if (m_checkOnly) { - iterateChildren(nodep); + iterateChildrenConst(nodep); } else { - iterate(nodep->lhsp()); + iterateConst(nodep->lhsp()); if (optimizable()) { if (fetchConst(nodep->lhsp())->num().isNeqZero()) { - iterate(nodep->rhsp()); + iterateConst(nodep->rhsp()); newValue(nodep, fetchValue(nodep->rhsp())); } else { newValue(nodep, fetchValue(nodep->lhsp())); // a zero @@ -612,14 +612,14 @@ private: if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); if (m_checkOnly) { - iterateChildren(nodep); + iterateChildrenConst(nodep); } else { - iterate(nodep->lhsp()); + iterateConst(nodep->lhsp()); if (optimizable()) { if (fetchConst(nodep->lhsp())->num().isNeqZero()) { newValue(nodep, fetchValue(nodep->lhsp())); // a one } else { - iterate(nodep->rhsp()); + iterateConst(nodep->rhsp()); newValue(nodep, fetchValue(nodep->rhsp())); } } @@ -630,16 +630,16 @@ private: if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); if (m_checkOnly) { - iterateChildren(nodep); + iterateChildrenConst(nodep); } else { - iterate(nodep->lhsp()); + iterateConst(nodep->lhsp()); if (optimizable()) { if (fetchConst(nodep->lhsp())->num().isEqZero()) { const AstConst cnst{nodep->fileline(), AstConst::WidthedValue{}, 1, 1}; // a one newValue(nodep, &cnst); // a one } else { - iterate(nodep->rhsp()); + iterateConst(nodep->rhsp()); newValue(nodep, fetchValue(nodep->rhsp())); } } @@ -652,15 +652,15 @@ private: if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); if (m_checkOnly) { - iterateChildren(nodep); + iterateChildrenConst(nodep); } else { - iterate(nodep->condp()); + iterateConst(nodep->condp()); if (optimizable()) { if (fetchConst(nodep->condp())->num().isNeqZero()) { - iterate(nodep->thenp()); + iterateConst(nodep->thenp()); newValue(nodep, fetchValue(nodep->thenp())); } else { - iterate(nodep->elsep()); + iterateConst(nodep->elsep()); newValue(nodep, fetchValue(nodep->elsep())); } } @@ -668,11 +668,11 @@ private: } void handleAssignArray(AstNodeAssign* nodep, AstArraySel* selp) { - iterateAndNextNull(nodep->rhsp()); // Value to assign + iterateAndNextConstNull(nodep->rhsp()); // Value to assign // At present we only handle single dimensional assignments // To do better, we need the concept of lvalues, or similar, to know where/how to insert checkNodeInfo(selp); - iterateAndNextNull(selp->bitp()); // Bit index + iterateAndNextConstNull(selp->bitp()); // Bit index AstVarRef* const varrefp = VN_CAST(selp->fromp(), VarRef); if (!varrefp) { clearOptimizable(nodep, "Array select LHS isn't simple variable"); @@ -719,7 +719,7 @@ private: void handleAssignSel(AstNodeAssign* nodep, AstSel* selp) { AstVarRef* varrefp = nullptr; V3Number lsb{nodep}; - iterateAndNextNull(nodep->rhsp()); // Value to assign + iterateAndNextConstNull(nodep->rhsp()); // Value to assign handleAssignSelRecurse(nodep, selp, varrefp /*ref*/, lsb /*ref*/, 0); if (!m_checkOnly && optimizable()) { UASSERT_OBJ(varrefp, nodep, @@ -748,7 +748,7 @@ private: // Recurse down to find final variable being set (outVarrefp), with // lsb to be eventually set on lsbRef checkNodeInfo(selp); - iterateAndNextNull(selp->lsbp()); // Bit index + iterateAndNextConstNull(selp->lsbp()); // Bit index if (AstVarRef* const varrefp = VN_CAST(selp->fromp(), VarRef)) { outVarrefpRef = varrefp; lsbRef = fetchConst(selp->lsbp())->num(); @@ -798,9 +798,9 @@ private: } else if (!VN_IS(nodep->lhsp(), VarRef)) { clearOptimizable(nodep, "LHS isn't simple variable"); } else if (m_checkOnly) { - iterateChildren(nodep); + iterateChildrenConst(nodep); } else if (optimizable()) { - iterateAndNextNull(nodep->rhsp()); + iterateAndNextConstNull(nodep->rhsp()); if (optimizable()) { AstNode* const vscp = varOrScope(VN_CAST(nodep->lhsp(), VarRef)); assignOutValue(nodep, vscp, fetchValue(nodep->rhsp())); @@ -809,7 +809,7 @@ private: } void visit(AstArraySel* nodep) override { checkNodeInfo(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); if (AstInitArray* const initp = VN_CAST(fetchValueNull(nodep->fromp()), InitArray)) { AstConst* const indexp = fetchConst(nodep->bitp()); const uint32_t offset = indexp->num().toUInt(); @@ -826,28 +826,28 @@ private: } void visit(AstBegin* nodep) override { checkNodeInfo(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstNodeCase* nodep) override { if (jumpingOver(nodep)) return; UINFO(5, " CASE " << nodep << endl); checkNodeInfo(nodep); if (m_checkOnly) { - iterateChildren(nodep); + iterateChildrenConst(nodep); } else if (optimizable()) { - iterateAndNextNull(nodep->exprp()); + iterateAndNextConstNull(nodep->exprp()); bool hit = false; for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp = VN_AS(itemp->nextp(), CaseItem)) { if (!itemp->isDefault()) { for (AstNode* ep = itemp->condsp(); ep; ep = ep->nextp()) { if (hit) break; - iterateAndNextNull(ep); + iterateAndNextConstNull(ep); if (optimizable()) { V3Number match{nodep, 1}; match.opEq(fetchConst(nodep->exprp())->num(), fetchConst(ep)->num()); if (match.isNeqZero()) { - iterateAndNextNull(itemp->stmtsp()); + iterateAndNextConstNull(itemp->stmtsp()); hit = true; } } @@ -859,7 +859,7 @@ private: itemp = VN_AS(itemp->nextp(), CaseItem)) { if (hit) break; if (!hit && itemp->isDefault()) { - iterateAndNextNull(itemp->stmtsp()); + iterateAndNextConstNull(itemp->stmtsp()); hit = true; } } @@ -870,7 +870,7 @@ private: // Real handling is in AstNodeCase if (jumpingOver(nodep)) return; checkNodeInfo(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstComment*) override {} @@ -878,12 +878,12 @@ private: void visit(AstStmtExpr* nodep) override { if (jumpingOver(nodep)) return; checkNodeInfo(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstJumpBlock* nodep) override { if (jumpingOver(nodep)) return; - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstJumpGo* nodep) override { if (jumpingOver(nodep)) return; @@ -898,7 +898,7 @@ private: // AstJumpGo::broken uses brokeExistsBelow() to check this. if (jumpingOver(nodep)) return; checkNodeInfo(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); if (m_jumpp && m_jumpp->labelp() == nodep) { UINFO(5, " JUMP DONE " << nodep << endl); m_jumpp = nullptr; @@ -924,19 +924,19 @@ private: } checkNodeInfo(nodep); if (m_checkOnly) { - iterateChildren(nodep); + iterateChildrenConst(nodep); } else if (optimizable()) { int loops = 0; - iterateAndNextNull(nodep->initsp()); + iterateAndNextConstNull(nodep->initsp()); while (true) { UINFO(5, " FOR-ITER " << nodep << endl); - iterateAndNextNull(nodep->condp()); + iterateAndNextConstNull(nodep->condp()); if (!optimizable()) break; if (!fetchConst(nodep->condp())->num().isNeqZero()) { // break; } - iterateAndNextNull(nodep->stmtsp()); - iterateAndNextNull(nodep->incsp()); + iterateAndNextConstNull(nodep->stmtsp()); + iterateAndNextConstNull(nodep->incsp()); if (loops++ > unrollCount() * 16) { clearOptimizable(nodep, "Loop unrolling took too long; probably this is an" "infinite loop, or set --unroll-count above " @@ -957,22 +957,22 @@ private: } checkNodeInfo(nodep); if (m_checkOnly) { - iterateChildren(nodep); + iterateChildrenConst(nodep); } else if (optimizable()) { int loops = 0; while (true) { UINFO(5, " WHILE-ITER " << nodep << endl); - iterateAndNextNull(nodep->precondsp()); + iterateAndNextConstNull(nodep->precondsp()); if (jumpingOver(nodep)) break; - iterateAndNextNull(nodep->condp()); + iterateAndNextConstNull(nodep->condp()); if (jumpingOver(nodep)) break; if (!optimizable()) break; if (!fetchConst(nodep->condp())->num().isNeqZero()) { // break; } - iterateAndNextNull(nodep->stmtsp()); + iterateAndNextConstNull(nodep->stmtsp()); if (jumpingOver(nodep)) break; - iterateAndNextNull(nodep->incsp()); + iterateAndNextConstNull(nodep->incsp()); if (jumpingOver(nodep)) break; // Prep for next loop @@ -1024,7 +1024,7 @@ private: return; } // Evaluate pin value - iterate(pinp); + iterateConst(pinp); } } for (V3TaskConnects::iterator it = tconnects.begin(); it != tconnects.end(); ++it) { @@ -1050,7 +1050,7 @@ private: newValue(funcp->fvarp(), &cnst); } // Evaluate the function - iterate(funcp); + iterateConst(funcp); m_callStack.pop_back(); if (!m_checkOnly && optimizable()) { // Grab return value from output variable (if it's a function) @@ -1076,7 +1076,7 @@ private: if (jumpingOver(nodep)) return; if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); if (m_params) { AstNode* nextArgp = nodep->exprsp(); @@ -1139,7 +1139,7 @@ private: // We ignore isPredictOptimizable as $display is often in constant // functions and we want them to work if used with parameters checkNodeInfo(nodep, /*display:*/ true); - iterateChildren(nodep); + iterateChildrenConst(nodep); if (m_params) { AstConst* const textp = fetchConst(nodep->fmtp()); switch (nodep->displayType()) { @@ -1171,7 +1171,7 @@ private: m_params = params; } void mainGuts(AstNode* nodep) { - iterate(nodep); + iterateConst(nodep); UASSERT_OBJ(!m_jumpp, m_jumpp, "JumpGo branched to label that wasn't found"); } diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index 17ad58852..4c5a392cd 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -90,7 +90,7 @@ class SliceVisitor final : public VNVisitor { AstNodeExpr* newp; if (const AstInitArray* const initp = VN_CAST(nodep, InitArray)) { UINFO(9, " cloneInitArray(" << elements << "," << offset << ") " << nodep << endl); - const int leOffset = !arrayp->rangep()->littleEndian() + const int leOffset = !arrayp->rangep()->ascending() ? arrayp->rangep()->elementsConst() - 1 - offset : offset; AstNodeExpr* const itemp = initp->getIndexDefaultedValuep(leOffset); @@ -107,14 +107,14 @@ class SliceVisitor final : public VNVisitor { } else if (const AstSliceSel* const snodep = VN_CAST(nodep, SliceSel)) { UINFO(9, " cloneSliceSel(" << elements << "," << offset << ") " << nodep << endl); const int leOffset = (snodep->declRange().lo() - + (!snodep->declRange().littleEndian() + + (!snodep->declRange().ascending() ? snodep->declRange().elements() - 1 - offset : offset)); newp = new AstArraySel{nodep->fileline(), snodep->fromp()->cloneTree(false), leOffset}; } else if (VN_IS(nodep, ArraySel) || VN_IS(nodep, NodeVarRef) || VN_IS(nodep, NodeSel) || VN_IS(nodep, CMethodHard) || VN_IS(nodep, MemberSel)) { UINFO(9, " cloneSel(" << elements << "," << offset << ") " << nodep << endl); - const int leOffset = !arrayp->rangep()->littleEndian() + const int leOffset = !arrayp->rangep()->ascending() ? arrayp->rangep()->elementsConst() - 1 - offset : offset; newp = new AstArraySel{nodep->fileline(), VN_AS(nodep, NodeExpr)->cloneTree(false), @@ -139,9 +139,10 @@ class SliceVisitor final : public VNVisitor { if (debug() >= 9) nodep->dumpTree("- Deslice-In: "); AstNodeDType* const dtp = nodep->lhsp()->dtypep()->skipRefp(); if (const AstUnpackArrayDType* const arrayp = VN_CAST(dtp, UnpackArrayDType)) { - // Left and right could have different msb/lsbs/endianness, but #elements is common - // and all variables are realigned to start at zero - // Assign of a little endian'ed slice to a big endian one must reverse the elements + // Left and right could have different ascending/descending range, + // but #elements is common and all variables are realigned to start at zero + // Assign of an ascending range slice to a descending range one must reverse the + // elements AstNodeAssign* newlistp = nullptr; const int elements = arrayp->rangep()->elementsConst(); for (int offset = 0; offset < elements; ++offset) { diff --git a/src/V3Split.cpp b/src/V3Split.cpp index db8a8da73..88cd94652 100644 --- a/src/V3Split.cpp +++ b/src/V3Split.cpp @@ -109,7 +109,9 @@ protected: // ACCESSORS // Do not make accessor for nodep(), It may change due to // reordering a lower block, but we don't repair it - string name() const override { return cvtToHex(m_nodep) + ' ' + m_nodep->prettyTypeName(); } + string name() const override VL_MT_STABLE { + return cvtToHex(m_nodep) + ' ' + m_nodep->prettyTypeName(); + } FileLine* fileline() const override { return nodep()->fileline(); } public: @@ -121,7 +123,7 @@ public: explicit SplitPliVertex(V3Graph* graphp, AstNode* nodep) : SplitNodeVertex{graphp, nodep} {} ~SplitPliVertex() override = default; - string name() const override { return "*PLI*"; } + string name() const override VL_MT_STABLE { return "*PLI*"; } string dotColor() const override { return "green"; } }; @@ -146,7 +148,7 @@ public: SplitVarPostVertex(V3Graph* graphp, AstNode* nodep) : SplitNodeVertex{graphp, nodep} {} ~SplitVarPostVertex() override = default; - string name() const override { return string("POST ") + SplitNodeVertex::name(); } + string name() const override VL_MT_STABLE { return string{"POST "} + SplitNodeVertex::name(); } string dotColor() const override { return "CadetBlue"; } }; @@ -511,10 +513,10 @@ protected: // And a real ordering to get the statements into something reasonable // We don't care if there's cutable violations here... // Non-cutable violations should be impossible; as those edges are program-order - if (dumpGraph() >= 9) m_graph.dumpDotFilePrefixed(string("splitg_preo"), false); + if (dumpGraph() >= 9) m_graph.dumpDotFilePrefixed("splitg_preo", false); m_graph.acyclic(&SplitEdge::followCyclic); m_graph.rank(&SplitEdge::followCyclic); // Or order(), but that's more expensive - if (dumpGraph() >= 9) m_graph.dumpDotFilePrefixed(string("splitg_opt"), false); + if (dumpGraph() >= 9) m_graph.dumpDotFilePrefixed("splitg_opt", false); } void reorderBlock(AstNode* nodep) { @@ -620,7 +622,7 @@ private: using ColorSet = std::unordered_set; using AlwaysVec = std::vector; -class IfColorVisitor final : public VNVisitor { +class IfColorVisitor final : public VNVisitorConst { // MEMBERS ColorSet m_colors; // All colors in the original always block @@ -634,7 +636,7 @@ class IfColorVisitor final : public VNVisitor { public: // Visit through *nodep and map each AstNodeIf within to the set of // colors it will participate in. Also find the whole set of colors. - explicit IfColorVisitor(AstAlways* nodep) { iterate(nodep); } + explicit IfColorVisitor(AstAlways* nodep) { iterateConst(nodep); } ~IfColorVisitor() override = default; // METHODS @@ -665,12 +667,12 @@ protected: void visit(AstNodeIf* nodep) override { m_ifStack.push_back(nodep); trackNode(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); m_ifStack.pop_back(); } void visit(AstNode* nodep) override { trackNode(nodep); - iterateChildren(nodep); + iterateChildrenConst(nodep); } private: diff --git a/src/V3SplitVar.cpp b/src/V3SplitVar.cpp index 43af5a4a2..4550ee310 100644 --- a/src/V3SplitVar.cpp +++ b/src/V3SplitVar.cpp @@ -591,7 +591,7 @@ class SplitUnpackedVarVisitor final : public VNVisitor, public SplitVarImpl { // restore the original decl range here. const VNumRange selRange{nodep->declRange().hi() + dtypep->declRange().lo(), nodep->declRange().lo() + dtypep->declRange().lo(), - nodep->declRange().littleEndian()}; + nodep->declRange().ascending()}; UASSERT_OBJ(dtypep->lo() <= selRange.lo() && selRange.hi() <= dtypep->hi(), nodep, "Range check for AstSliceSel must have been finished in V3Width.cpp"); UINFO(4, "add " << nodep << " for " << refp->varp()->prettyName() << "\n"); @@ -1071,7 +1071,7 @@ class SplitPackedVarVisitor final : public VNVisitor, public SplitVarImpl { for (SplitNewVar& newvar : vars) { int left = newvar.msb(); int right = newvar.lsb(); - if (basicp->littleEndian()) std::swap(left, right); + if (basicp->ascending()) std::swap(left, right); const std::string name = (left == right) ? varp->name() + "__BRA__" + AstNode::encodeNumber(left) + "__KET__" @@ -1091,7 +1091,7 @@ class SplitPackedVarVisitor final : public VNVisitor, public SplitVarImpl { default: UASSERT_OBJ(false, basicp, "Only bit and logic are allowed"); } dtypep->rangep(new AstRange{ - varp->fileline(), VNumRange{newvar.msb(), newvar.lsb(), basicp->littleEndian()}}); + varp->fileline(), VNumRange{newvar.msb(), newvar.lsb(), basicp->ascending()}}); newvar.varp(new AstVar{varp->fileline(), VVarType::VAR, name, dtypep}); newvar.varp()->propagateAttrFrom(varp); newvar.varp()->funcLocal(varp->isFuncLocal() || varp->isFuncReturn()); diff --git a/src/V3Stats.cpp b/src/V3Stats.cpp index df38e83ac..f79da45e4 100644 --- a/src/V3Stats.cpp +++ b/src/V3Stats.cpp @@ -33,7 +33,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### // Stats class functions -class StatsVisitor final : public VNVisitor { +class StatsVisitor final : public VNVisitorConst { private: // NODE STATE/TYPES @@ -175,7 +175,7 @@ private: if (m_fast && !nodep->funcp()->entryPoint()) { // Enter the function and trace it m_tracingCall = true; - iterate(nodep->funcp()); + iterateConst(nodep->funcp()); } } void visit(AstCFunc* nodep) override { @@ -218,7 +218,7 @@ public: // Initialize arrays m_statTypeCount.resize(VNType::_ENUM_END); // Process - iterate(nodep); + iterateConst(nodep); } ~StatsVisitor() override { // Done. Publish statistics diff --git a/src/V3String.cpp b/src/V3String.cpp index f0aa6ecc8..5dcf0f63d 100644 --- a/src/V3String.cpp +++ b/src/V3String.cpp @@ -36,7 +36,7 @@ std::map VName::s_dehashMap; // Wildcard // Double procedures, inlined, unrolls loop much better -bool VString::wildmatchi(const char* s, const char* p) { +bool VString::wildmatchi(const char* s, const char* p) VL_PURE { for (; *p; s++, p++) { if (*p != '*') { if (((*s) != (*p)) && *p != '?') return false; @@ -52,7 +52,7 @@ bool VString::wildmatchi(const char* s, const char* p) { return (*s == '\0'); } -bool VString::wildmatch(const char* s, const char* p) { +bool VString::wildmatch(const char* s, const char* p) VL_PURE { for (; *p; s++, p++) { if (*p != '*') { if (((*s) != (*p)) && *p != '?') return false; @@ -68,7 +68,7 @@ bool VString::wildmatch(const char* s, const char* p) { return (*s == '\0'); } -bool VString::wildmatch(const string& s, const string& p) { +bool VString::wildmatch(const string& s, const string& p) VL_PURE { return wildmatch(s.c_str(), p.c_str()); } @@ -79,24 +79,24 @@ string VString::dot(const string& a, const string& dot, const string& b) { } string VString::downcase(const string& str) { - string out = str; - for (auto& cr : out) cr = std::tolower(cr); - return out; + string result = str; + for (char& cr : result) cr = std::tolower(cr); + return result; } string VString::upcase(const string& str) { - string out = str; - for (auto& cr : out) cr = std::toupper(cr); - return out; + string result = str; + for (char& cr : result) cr = std::toupper(cr); + return result; } string VString::quoteAny(const string& str, char tgt, char esc) { - string out; + string result; for (const char c : str) { - if (c == tgt) out += esc; - out += c; + if (c == tgt) result += esc; + result += c; } - return out; + return result; } string VString::quoteStringLiteralForShell(const string& str) { @@ -130,25 +130,25 @@ string VString::escapeStringForPath(const string& str) { return result; } -string VString::spaceUnprintable(const string& str) { - string out; +string VString::spaceUnprintable(const string& str) VL_PURE { + string result; for (const char c : str) { if (std::isprint(c)) { - out += c; + result += c; } else { - out += ' '; + result += ' '; } } - return out; + return result; } string VString::removeWhitespace(const string& str) { - string out; - out.reserve(str.size()); + string result; + result.reserve(str.size()); for (const char c : str) { - if (!std::isspace(c)) out += c; + if (!std::isspace(c)) result += c; } - return out; + return result; } bool VString::isWhitespace(const string& str) { @@ -216,10 +216,12 @@ static const uint32_t sha256K[] 0xc67178f2}; VL_ATTR_ALWINLINE -static uint32_t shaRotr32(uint32_t lhs, uint32_t rhs) { return lhs >> rhs | lhs << (32 - rhs); } +static uint32_t shaRotr32(uint32_t lhs, uint32_t rhs) VL_PURE { + return lhs >> rhs | lhs << (32 - rhs); +} VL_ATTR_ALWINLINE -static void sha256Block(uint32_t* h, const uint32_t* chunk) { +static void sha256Block(uint32_t* h, const uint32_t* chunk) VL_PURE { uint32_t ah[8]; const uint32_t* p = chunk; @@ -329,34 +331,34 @@ void VHashSha256::finalize() { string VHashSha256::digestBinary() { finalize(); - string out; - out.reserve(32); + string result; + result.reserve(32); for (size_t i = 0; i < 32; ++i) { - out += (m_inthash[i >> 2] >> (((3 - i) & 0x3) << 3)) & 0xff; + result += (m_inthash[i >> 2] >> (((3 - i) & 0x3) << 3)) & 0xff; } - return out; + return result; } uint64_t VHashSha256::digestUInt64() { const string& binhash = digestBinary(); - uint64_t out = 0; + uint64_t result = 0; for (size_t byte = 0; byte < sizeof(uint64_t); ++byte) { const unsigned char c = binhash[byte]; - out = (out << 8) | c; + result = (result << 8) | c; } - return out; + return result; } string VHashSha256::digestHex() { static const char* const digits = "0123456789abcdef"; const string& binhash = digestBinary(); - string out; - out.reserve(70); + string result; + result.reserve(70); for (size_t byte = 0; byte < 32; ++byte) { - out += digits[(binhash[byte] >> 4) & 0xf]; - out += digits[(binhash[byte] >> 0) & 0xf]; + result += digits[(binhash[byte] >> 4) & 0xf]; + result += digits[(binhash[byte] >> 0) & 0xf]; } - return out; + return result; } string VHashSha256::digestSymbol() { @@ -367,19 +369,19 @@ string VHashSha256::digestSymbol() { static const char* const digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"; const string& binhash = digestBinary(); - string out; - out.reserve(28); + string result; + result.reserve(28); int pos = 0; for (; pos < (256 / 8) - 2; pos += 3) { - out += digits[((binhash[pos] >> 2) & 0x3f)]; - out += digits[((binhash[pos] & 0x3) << 4) - | (static_cast(binhash[pos + 1] & 0xf0) >> 4)]; - out += digits[((binhash[pos + 1] & 0xf) << 2) - | (static_cast(binhash[pos + 2] & 0xc0) >> 6)]; - out += digits[((binhash[pos + 2] & 0x3f))]; + result += digits[((binhash[pos] >> 2) & 0x3f)]; + result += digits[((binhash[pos] & 0x3) << 4) + | (static_cast(binhash[pos + 1] & 0xf0) >> 4)]; + result += digits[((binhash[pos + 1] & 0xf) << 2) + | (static_cast(binhash[pos + 2] & 0xc0) >> 6)]; + result += digits[((binhash[pos + 2] & 0x3f))]; } // Any leftover bits don't matter for our purpose - return out; + return result; } void VHashSha256::selfTestOne(const string& data, const string& data2, const string& exp, diff --git a/src/V3String.h b/src/V3String.h index bbd3ba948..bd31909cc 100644 --- a/src/V3String.h +++ b/src/V3String.h @@ -34,13 +34,14 @@ // Global string-related functions template -std::string cvtToStr(const T& t) { +std::string cvtToStr(const T& t) VL_PURE { std::ostringstream os; os << t; return os.str(); } template -typename std::enable_if::value, std::string>::type cvtToHex(const T tp) { +typename std::enable_if::value, std::string>::type +cvtToHex(const T tp) VL_PURE { std::ostringstream os; os << static_cast(tp); return os.str(); @@ -69,23 +70,23 @@ inline uint32_t cvtToHash(const void* vp) { } inline string ucfirst(const string& text) { - string out = text; - out[0] = std::toupper(out[0]); - return out; + string result = text; + result[0] = std::toupper(result[0]); + return result; } //###################################################################### // VString - String manipulation class VString final { - static bool wildmatchi(const char* s, const char* p); + static bool wildmatchi(const char* s, const char* p) VL_PURE; public: // METHODS (generic string utilities) // Return true if p with ? or *'s matches s - static bool wildmatch(const char* s, const char* p); + static bool wildmatch(const char* s, const char* p) VL_PURE; // Return true if p with ? or *'s matches s - static bool wildmatch(const string& s, const string& p); + static bool wildmatch(const string& s, const string& p) VL_PURE; // Return {a}{dot}{b}, omitting dot if a or b are empty static string dot(const string& a, const string& dot, const string& b); // Convert string to lowercase (tolower) @@ -107,7 +108,7 @@ public: static string quoteStringLiteralForShell(const string& str); // Replace any unprintable with space // This includes removing tabs, so column tracking is correct - static string spaceUnprintable(const string& str); + static string spaceUnprintable(const string& str) VL_PURE; // Remove any whitespace static string removeWhitespace(const string& str); // Return true if only whitespace or "" diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 500d8e6dc..94c45b035 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -67,8 +67,8 @@ public: : TaskBaseVertex{graphp} , m_nodep{nodep} {} ~TaskFTaskVertex() override = default; - AstNodeFTask* nodep() const { return m_nodep; } - string name() const override { return nodep()->name(); } + AstNodeFTask* nodep() const VL_MT_STABLE { return m_nodep; } + string name() const override VL_MT_STABLE { return nodep()->name(); } string dotColor() const override { return pure() ? "black" : "red"; } AstCFunc* cFuncp() const { return m_cFuncp; } void cFuncp(AstCFunc* nodep) { m_cFuncp = nodep; } @@ -80,7 +80,7 @@ public: explicit TaskCodeVertex(V3Graph* graphp) : TaskBaseVertex{graphp} {} ~TaskCodeVertex() override = default; - string name() const override { return "*CODE*"; } + string name() const override VL_MT_STABLE { return "*CODE*"; } string dotColor() const override { return "green"; } }; @@ -323,7 +323,7 @@ struct TaskDpiUtils { return true; } else { const AstNodeDType* const dtypep = portp->dtypep()->skipRefp(); - frstmt = "VL_SET_" + string(dtypep->charIQWN()) + "_" + frSvType + "("; + frstmt = "VL_SET_" + string{dtypep->charIQWN()} + "_" + frSvType + "("; if (VN_IS(dtypep, UnpackArrayDType)) frstmt += "&"; frstmt += frName; ket = ")"; @@ -425,7 +425,7 @@ private: AstNode* const newbodysp = AstNode::cloneTreeNull(refp->taskp()->stmtsp(), true); // Maybe nullptr AstNode* const beginp - = new AstComment{refp->fileline(), string("Function: ") + refp->name(), true}; + = new AstComment{refp->fileline(), string{"Function: "} + refp->name(), true}; if (newbodysp) beginp->addNext(newbodysp); if (debug() >= 9) beginp->dumpTreeAndNext(cout, "- newbegi: "); // @@ -542,7 +542,7 @@ private: UASSERT_OBJ(cfuncp, refp, "No non-inline task associated with this task call?"); // AstNode* const beginp - = new AstComment{refp->fileline(), string("Function: ") + refp->name(), true}; + = new AstComment{refp->fileline(), string{"Function: "} + refp->name(), true}; AstNodeCCall* ccallp; if (VN_IS(refp, New)) { AstCNew* const cnewp = new AstCNew{refp->fileline(), cfuncp}; @@ -673,19 +673,25 @@ private: void unlinkAndClone(AstNodeFTask* funcp, AstNode* nodep, bool withNext) { UASSERT_OBJ(nodep, funcp, "null in function object clone"); - VNRelinker relinkHandle; - if (withNext) { - nodep->unlinkFrBackWithNext(&relinkHandle); - } else { - nodep->unlinkFrBack(&relinkHandle); - } if (funcp->recursive()) { + VNRelinker relinkHandle; + if (withNext) { + nodep->unlinkFrBackWithNext(&relinkHandle); + } else { + nodep->unlinkFrBack(&relinkHandle); + } // Recursive functions require the original argument list to // still be live for linking purposes. // The old function gets clone, so that node pointers are mostly // retained through the V3Task transformations AstNode* const newp = nodep->cloneTree(withNext); relinkHandle.relink(newp); + } else { + if (withNext) { + nodep->unlinkFrBackWithNext(); + } else { + nodep->unlinkFrBack(); + } } } @@ -802,7 +808,7 @@ private: // Convert input/inout DPI arguments to Internal types string args; - args += ("(" + EmitCBaseVisitor::symClassName() + args += ("(" + EmitCBase::symClassName() + "*)(__Vscopep->symsp())"); // Upcast w/o overhead AstNode* argnodesp = nullptr; for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { @@ -1181,15 +1187,14 @@ private: cfuncp->isConstructor(true); AstClass* const classp = m_statep->getClassp(nodep); if (classp->extendsp()) { - cfuncp->baseCtors( - EmitCBaseVisitor::prefixNameProtect(classp->extendsp()->classp())); + cfuncp->baseCtors(EmitCBase::prefixNameProtect(classp->extendsp()->classp())); } } if (cfuncp->dpiExportImpl()) cfuncp->cname(nodep->cname()); if (!nodep->dpiImport() && !nodep->taskPublic()) { // Need symbol table - cfuncp->argTypes(EmitCBaseVisitor::symClassVar()); + cfuncp->argTypes(EmitCBase::symClassVar()); if (cfuncp->name() == "new") { const string stmt = VIdProtect::protect("_ctor_var_reset") + "(vlSymsp);\n"; cfuncp->addInitsp(new AstCStmt{nodep->fileline(), stmt}); @@ -1720,7 +1725,7 @@ string V3Task::assignInternalToDpi(AstVar* portp, bool isPtr, const string& frSu stmt = "for (size_t " + idx + " = 0; " + idx + " < " + cvtToStr(unpackSize) + "; ++" + idx + ") "; stmt += (isBit ? "VL_SET_SVBV_" : "VL_SET_SVLV_") - + string(portp->dtypep()->skipRefp()->charIQWN()) + "(" + cvtToStr(portp->width()) + + string{portp->dtypep()->skipRefp()->charIQWN()} + "(" + cvtToStr(portp->width()) + ", "; stmt += toName + " + " + cvtToStr(portp->dtypep()->skipRefp()->widthWords()) + " * " + idx + ", "; diff --git a/src/V3ThreadPool.cpp b/src/V3ThreadPool.cpp index 6f4ba5ed6..47750a093 100644 --- a/src/V3ThreadPool.cpp +++ b/src/V3ThreadPool.cpp @@ -25,8 +25,10 @@ constexpr unsigned int V3ThreadPool::FUTUREWAITFOR_MS; void V3ThreadPool::resize(unsigned n) VL_MT_UNSAFE { // This function is not thread-safe and can result in race between threads - VerilatedLockGuard lock{m_mutex}; - VerilatedLockGuard stoppedJobsLock{m_stoppedJobsMutex}; + UASSERT(V3MutexConfig::s().lockConfig(), + "Mutex config needs to be locked before starting ThreadPool"); + V3LockGuard lock{m_mutex}; + V3LockGuard stoppedJobsLock{m_stoppedJobsMutex}; UASSERT(m_queue.empty(), "Resizing busy thread pool"); // Shut down old threads m_shutdown = true; @@ -57,7 +59,7 @@ void V3ThreadPool::workerJobLoop(int id) VL_MT_SAFE { waitIfStopRequested(); job_t job; { - VerilatedLockGuard lock(m_mutex); + V3LockGuard lock(m_mutex); m_cv.wait(lock, [&]() VL_REQUIRES(m_mutex) { return !m_queue.empty() || m_shutdown || m_stopRequested; }); @@ -82,7 +84,7 @@ void V3ThreadPool::pushJob(std::shared_ptr>& prom, f(); prom->set_value(); } else { - const VerilatedLockGuard lock{m_mutex}; + const V3LockGuard lock{m_mutex}; m_queue.push([prom, f] { f(); prom->set_value(); @@ -95,7 +97,7 @@ void V3ThreadPool::requestExclusiveAccess(const V3ThreadPool::job_t&& exclusiveA if (willExecuteSynchronously()) { exclusiveAccessJob(); } else { - VerilatedLockGuard stoppedJobLock{m_stoppedJobsMutex}; + V3LockGuard stoppedJobLock{m_stoppedJobsMutex}; // if some other job already requested exclusive access // wait until it stops if (stopRequested()) { waitStopRequested(stoppedJobLock); } @@ -110,14 +112,13 @@ void V3ThreadPool::requestExclusiveAccess(const V3ThreadPool::job_t&& exclusiveA } bool V3ThreadPool::waitIfStopRequested() VL_MT_SAFE { - VerilatedLockGuard stoppedJobLock(m_stoppedJobsMutex); + V3LockGuard stoppedJobLock(m_stoppedJobsMutex); if (!stopRequested()) return false; waitStopRequested(stoppedJobLock); return true; } -void V3ThreadPool::waitStopRequested(VerilatedLockGuard& stoppedJobLock) - VL_REQUIRES(m_stoppedJobsMutex) { +void V3ThreadPool::waitStopRequested(V3LockGuard& stoppedJobLock) VL_REQUIRES(m_stoppedJobsMutex) { ++m_stoppedJobs; m_stoppedJobsCV.notify_all(); m_stoppedJobsCV.wait( @@ -126,8 +127,8 @@ void V3ThreadPool::waitStopRequested(VerilatedLockGuard& stoppedJobLock) m_stoppedJobsCV.notify_all(); } -void V3ThreadPool::waitOtherThreads(VerilatedLockGuard& stoppedJobLock) - VL_MT_SAFE_EXCLUDES(m_mutex) VL_REQUIRES(m_stoppedJobsMutex) { +void V3ThreadPool::waitOtherThreads(V3LockGuard& stoppedJobLock) VL_MT_SAFE_EXCLUDES(m_mutex) + VL_REQUIRES(m_stoppedJobsMutex) { ++m_stoppedJobs; m_stoppedJobsCV.notify_all(); m_cv.notify_all(); @@ -139,7 +140,7 @@ void V3ThreadPool::waitOtherThreads(VerilatedLockGuard& stoppedJobLock) } void V3ThreadPool::selfTest() { - VerilatedMutex commonMutex; + V3Mutex commonMutex; int commonValue{0}; auto firstJob = [&](int sleep) -> void { @@ -151,7 +152,7 @@ void V3ThreadPool::selfTest() { }); }; auto secondJob = [&](int sleep) -> void { - VerilatedLockGuard lock{commonMutex}; + V3LockGuard lock{commonMutex}; lock.unlock(); s().waitIfStopRequested(); lock.lock(); @@ -159,7 +160,7 @@ void V3ThreadPool::selfTest() { commonValue = 1000; }; auto thirdJob = [&](int sleep) -> void { - VerilatedLockGuard lock{commonMutex}; + V3LockGuard lock{commonMutex}; std::this_thread::sleep_for(std::chrono::milliseconds{sleep}); lock.unlock(); s().requestExclusiveAccess([&]() { firstJob(sleep); }); @@ -181,18 +182,12 @@ void V3ThreadPool::selfTest() { } futures.push_back(s().enqueue(std::bind(thirdJob, 100))); futures.push_back(s().enqueue(std::bind(thirdJob, 100))); - while (!futures.empty()) { - s().waitForFuture(futures.front()); - futures.pop_front(); - } + V3ThreadPool::waitForFutures(futures); s().waitIfStopRequested(); s().requestExclusiveAccess(std::bind(firstJob, 100)); auto forthJob = [&]() -> int { return 1234; }; std::list> futuresInt; futuresInt.push_back(s().enqueue(forthJob)); - while (!futuresInt.empty()) { - int result = s().waitForFuture(futuresInt.front()); - UASSERT(result == 1234, "unexpected future result = " << commonValue); - futuresInt.pop_front(); - } + auto result = V3ThreadPool::waitForFutures(futuresInt); + UASSERT(result.back() == 1234, "unexpected future result = " << result.back()); } diff --git a/src/V3ThreadPool.h b/src/V3ThreadPool.h index b074491be..69e093fcc 100644 --- a/src/V3ThreadPool.h +++ b/src/V3ThreadPool.h @@ -17,7 +17,7 @@ #ifndef _V3THREADPOOL_H_ #define _V3THREADPOOL_H_ 1 -#include "verilated_threads.h" +#include "V3Mutex.h" #include #include @@ -27,6 +27,19 @@ #include #include +namespace future_type { +template +struct return_type { + typedef std::list type; +}; + +template <> +struct return_type { + typedef void type; +}; + +} // namespace future_type + //============================================================================ class V3ThreadPool final { @@ -34,7 +47,7 @@ class V3ThreadPool final { static constexpr unsigned int FUTUREWAITFOR_MS = 100; using job_t = std::function; - mutable VerilatedMutex m_mutex; // Mutex for use by m_queue + mutable V3Mutex m_mutex; // Mutex for use by m_queue std::queue m_queue VL_GUARDED_BY(m_mutex); // Queue of jobs // We don't need to guard this condition_variable as // both `notify_one` and `notify_all` functions are atomic, @@ -42,7 +55,7 @@ class V3ThreadPool final { // used by this condition_variable, so clang checks that we have mutex locked std::condition_variable_any m_cv; // Conditions to wake up workers std::list m_workers; // Worker threads - VerilatedMutex m_stoppedJobsMutex; // Used to signal stopped jobs + V3Mutex m_stoppedJobsMutex; // Used to signal stopped jobs // Conditions to wake up stopped jobs std::condition_variable_any m_stoppedJobsCV VL_GUARDED_BY(m_stoppedJobsMutex); std::atomic_uint m_stoppedJobs{0}; // Currently stopped jobs waiting for wake up @@ -54,7 +67,7 @@ class V3ThreadPool final { // CONSTRUCTORS V3ThreadPool() = default; ~V3ThreadPool() { - VerilatedLockGuard lock{m_mutex}; + V3LockGuard lock{m_mutex}; m_queue = {}; // make sure queue is empty lock.unlock(); resize(0); @@ -72,8 +85,14 @@ public: void resize(unsigned n) VL_MT_UNSAFE; // Enqueue a job for asynchronous execution + // Due to missing support for lambda annotations in c++11, + // `clang_check_attributes` script assumes that if + // function takes `std::function` as argument, it + // will call it. `VL_MT_START` here indicates that + // every function call inside this `std::function` requires + // annotations. template - std::future enqueue(std::function&& f) VL_MT_SAFE; + std::future enqueue(std::function&& f) VL_MT_START; // Request exclusive access to processing. // It sends request to stop all other threads and waits for them to stop. @@ -88,12 +107,47 @@ public: // Returns true if request was send and we waited, otherwise false bool waitIfStopRequested() VL_MT_SAFE; + // Waits for future. + // This function can be interupted by exclusive access request. + // When other thread requested exclusive access to processing, + // current thread is stopped and waits until it is resumed. + // Returns future result template - T waitForFuture(std::future& future) VL_MT_SAFE_EXCLUDES(m_mutex); + static T waitForFuture(std::future& future) VL_MT_SAFE_EXCLUDES(m_mutex); + + // Waits for list of futures + // This function can be interupted by exclusive access request. + // When other thread requested exclusive access to processing, + // current thread is stopped and waits until it is resumed. + // This function uses function overload instead of template + // specialization as C++11 requires them to be inside namespace scope + // Returns list of future result or void + template + static typename future_type::return_type::type + waitForFutures(std::list>& futures) { + return waitForFuturesImp(futures); + } static void selfTest(); private: + template + static typename future_type::return_type::type + waitForFuturesImp(std::list>& futures) { + typename future_type::return_type::type results; + while (!futures.empty()) { + results.push_back(V3ThreadPool::waitForFuture(futures.front())); + futures.pop_front(); + } + return results; + } + + static void waitForFuturesImp(std::list>& futures) { + while (!futures.empty()) { + V3ThreadPool::waitForFuture(futures.front()); + futures.pop_front(); + } + } bool willExecuteSynchronously() const VL_MT_SAFE { return m_workers.empty() || m_exclusiveAccess; } @@ -106,15 +160,15 @@ private: } bool stopRequestedStandalone() VL_MT_SAFE_EXCLUDES(m_stoppedJobsMutex) { - const VerilatedLockGuard lock{m_stoppedJobsMutex}; + const V3LockGuard lock{m_stoppedJobsMutex}; return stopRequested(); } // Waits until exclusive access job completes its job - void waitStopRequested(VerilatedLockGuard& stoppedJobLock) VL_REQUIRES(m_stoppedJobsMutex); + void waitStopRequested(V3LockGuard& stoppedJobLock) VL_REQUIRES(m_stoppedJobsMutex); // Waits until all other jobs are stopped - void waitOtherThreads(VerilatedLockGuard& stoppedJobLock) VL_MT_SAFE_EXCLUDES(m_mutex) + void waitOtherThreads(V3LockGuard& stoppedJobLock) VL_MT_SAFE_EXCLUDES(m_mutex) VL_REQUIRES(m_stoppedJobsMutex); template @@ -128,7 +182,7 @@ private: template T V3ThreadPool::waitForFuture(std::future& future) VL_MT_SAFE_EXCLUDES(m_mutex) { while (true) { - waitIfStopRequested(); + V3ThreadPool::s().waitIfStopRequested(); { std::future_status status = future.wait_for(std::chrono::milliseconds(V3ThreadPool::FUTUREWAITFOR_MS)); @@ -142,11 +196,11 @@ T V3ThreadPool::waitForFuture(std::future& future) VL_MT_SAFE_EXCLUDES(m_mute } template -std::future V3ThreadPool::enqueue(std::function&& f) VL_MT_SAFE { +std::future V3ThreadPool::enqueue(std::function&& f) VL_MT_START { std::shared_ptr> prom = std::make_shared>(); std::future result = prom->get_future(); pushJob(prom, std::move(f)); - const VerilatedLockGuard guard{m_mutex}; + const V3LockGuard guard{m_mutex}; m_cv.notify_one(); return result; } @@ -157,7 +211,7 @@ void V3ThreadPool::pushJob(std::shared_ptr>& prom, if (willExecuteSynchronously()) { prom->set_value(f()); } else { - const VerilatedLockGuard guard{m_mutex}; + const V3LockGuard guard{m_mutex}; m_queue.push([prom, f] { prom->set_value(f()); }); } } diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index 0a096661e..a71ad592e 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -67,7 +67,7 @@ private: class DependencyVertex final : public V3GraphVertex { AstNode* const m_nodep; // AST node represented by this graph vertex // ACCESSORS - string name() const override { + string name() const override VL_MT_STABLE { return cvtToHex(nodep()) + ' ' + nodep()->prettyTypeName(); } FileLine* fileline() const override { return nodep()->fileline(); } @@ -81,7 +81,7 @@ private: ~DependencyVertex() override = default; // ACCESSORS - virtual AstNode* nodep() const { return m_nodep; } + virtual AstNode* nodep() const VL_MT_STABLE { return m_nodep; } }; // NODE STATE diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index 32dbd8471..44a12ba59 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -154,7 +154,7 @@ public: //###################################################################### // Trace state, as a visitor of each AstNode -class TraceVisitor final : public EmitCBaseVisitor { +class TraceVisitor final : public VNVisitor { private: // NODE STATE // V3Hasher in V3DupFinder @@ -437,7 +437,7 @@ private: funcp->stmtsp()->foreachAndNext([&](AstCAwait* awaitp) { AstNode* stmtp = awaitp->backp(); while (VN_IS(stmtp, NodeExpr)) stmtp = stmtp->backp(); - if (stmtp->nextp()) stmtp->addNextHere(setterp->cloneTree(false)); + stmtp->addNextHere(setterp->cloneTree(false)); }); } funcp->addStmtsp(setterp); @@ -511,8 +511,8 @@ private: funcp->argTypes("void* voidSelf, " + v3Global.opt.traceClassBase() + "::" + (v3Global.opt.useTraceOffload() ? "OffloadBuffer" : "Buffer") + "* bufp"); - addInitStr(voidSelfAssign(m_topModp)); - addInitStr(symClassAssign()); + addInitStr(EmitCBase::voidSelfAssign(m_topModp)); + addInitStr(EmitCBase::symClassAssign()); // Add global activity check to change dump functions if (!full) { // addInitStr("if (VL_UNLIKELY(!vlSymsp->__Vm_activity)) return;\n"); @@ -709,8 +709,8 @@ private: cleanupFuncp->isStatic(true); cleanupFuncp->isLoose(true); m_topScopep->addBlocksp(cleanupFuncp); - cleanupFuncp->addInitsp(new AstCStmt{fl, voidSelfAssign(m_topModp)}); - cleanupFuncp->addInitsp(new AstCStmt{fl, symClassAssign()}); + cleanupFuncp->addInitsp(new AstCStmt{fl, EmitCBase::voidSelfAssign(m_topModp)}); + cleanupFuncp->addInitsp(new AstCStmt{fl, EmitCBase::symClassAssign()}); // Register it m_regFuncp->addStmtsp(new AstText{fl, "tracep->addCleanupCb(", true}); diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index 8c6f73e25..704d1d4b4 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -93,7 +93,7 @@ public: //###################################################################### // TraceDecl state, as a visitor of each AstNode -class TraceDeclVisitor final : public EmitCBaseVisitor { +class TraceDeclVisitor final : public VNVisitor { private: // NODE STATE @@ -237,8 +237,9 @@ private: void addIgnore(const char* why) { ++m_statIgnSigs; - addToSubFunc(new AstComment{m_traVscp->fileline(), - "Tracing: " + m_traName + " // Ignored: " + why, true}); + std::string cmt = std::string{"Tracing: "} + m_traName + " // Ignored: " + why; + if (debug() > 3 && m_traVscp) std::cout << "- " << m_traVscp->fileline() << cmt << endl; + addToSubFunc(new AstComment{m_traVscp->fileline(), cmt, true}); } // VISITORS diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 9837dbb92..61720c2a4 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -152,9 +152,9 @@ public: , m_nodep{nodep} {} ~TristateVertex() override = default; // ACCESSORS - AstNode* nodep() const { return m_nodep; } + AstNode* nodep() const VL_MT_STABLE { return m_nodep; } const AstVar* varp() const { return VN_CAST(nodep(), Var); } - string name() const override { + string name() const override VL_MT_STABLE { return ((isTristate() ? "tri\\n" : feedsTri() ? "feed\\n" : "-\\n") @@ -170,9 +170,9 @@ public: } FileLine* fileline() const override { return nodep()->fileline(); } void isTristate(bool flag) { m_isTristate = flag; } - bool isTristate() const { return m_isTristate; } + bool isTristate() const VL_MT_SAFE { return m_isTristate; } void feedsTri(bool flag) { m_feedsTri = flag; } - bool feedsTri() const { return m_feedsTri; } + bool feedsTri() const VL_MT_SAFE { return m_feedsTri; } void processed(bool flag) { m_processed = flag; } bool processed() const { return m_processed; } }; @@ -1793,7 +1793,7 @@ class TristateVisitor final : public TristateBaseVisitor { iterateChildren(nodep); } - void visit(AstNetlist* nodep) override { iterateChildrenBackwards(nodep); } + void visit(AstNetlist* nodep) override { iterateChildrenBackwardsConst(nodep); } // Default: Just iterate void visit(AstNode* nodep) override { diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 398abf29a..84e7aba35 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -101,7 +101,7 @@ private: if (lsb == msb) { bits += cvtToStr(lsb + bdtypep->lo()); } else { - if (bdtypep->littleEndian()) { + if (bdtypep->ascending()) { bits += cvtToStr(lsb + bdtypep->lo()) + ":" + cvtToStr(msb + bdtypep->lo()); } else { @@ -269,7 +269,7 @@ public: //###################################################################### // Undriven state, as a visitor of each AstNode -class UndrivenVisitor final : public VNVisitor { +class UndrivenVisitor final : public VNVisitorConst { private: // NODE STATE // Netlist: @@ -344,15 +344,15 @@ private: if (nodep->valuep()) entryp->drivenWhole(); } // Discover variables used in bit definitions, etc - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstArraySel* nodep) override { // Arrays are rarely constant assigned, so for now we punt and do all entries - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstSliceSel* nodep) override { // Arrays are rarely constant assigned, so for now we punt and do all entries - iterateChildren(nodep); + iterateChildrenConst(nodep); } void visit(AstSel* nodep) override { AstNodeVarRef* const varrefp = VN_CAST(nodep->fromp(), NodeVarRef); @@ -375,7 +375,7 @@ private: } } else { // else other varrefs handled as unknown mess in AstVarRef - iterateChildren(nodep); + iterateChildrenConst(nodep); } } void visit(AstNodeVarRef* nodep) override { @@ -460,7 +460,7 @@ private: VL_RESTORER(m_inBBox); { m_inBBox = true; - iterateChildren(nodep); + iterateChildrenConst(nodep); } } @@ -468,21 +468,21 @@ private: VL_RESTORER(m_inProcAssign); { m_inProcAssign = true; - iterateChildren(nodep); + iterateChildrenConst(nodep); } } void visit(AstAssignDly* nodep) override { VL_RESTORER(m_inProcAssign); { m_inProcAssign = true; - iterateChildren(nodep); + iterateChildrenConst(nodep); } } void visit(AstAssignW* nodep) override { VL_RESTORER(m_inContAssign); { m_inContAssign = true; - iterateChildren(nodep); + iterateChildrenConst(nodep); } } void visit(AstAlways* nodep) override { @@ -495,7 +495,7 @@ private: } else { m_alwaysCombp = nullptr; } - iterateChildren(nodep); + iterateChildrenConst(nodep); if (nodep->keyword() == VAlwaysKwd::ALWAYS_COMB) UINFO(9, " Done " << nodep << endl); } } @@ -504,13 +504,13 @@ private: VL_RESTORER(m_taskp); { m_taskp = nodep; - iterateChildren(nodep); + iterateChildrenConst(nodep); } } void visit(AstPin* nodep) override { VL_RESTORER(m_inInoutPin); m_inInoutPin = nodep->modVarp()->isInoutish(); - iterateChildren(nodep); + iterateChildrenConst(nodep); } // Until we support tables, primitives will have undriven and unused I/Os @@ -525,11 +525,11 @@ private: // iterate void visit(AstConst* nodep) override {} - void visit(AstNode* nodep) override { iterateChildren(nodep); } + void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } public: // CONSTRUCTORS - explicit UndrivenVisitor(AstNetlist* nodep) { iterate(nodep); } + explicit UndrivenVisitor(AstNetlist* nodep) { iterateConst(nodep); } ~UndrivenVisitor() override { for (UndrivenVarEntry* ip : m_entryps[1]) ip->reportViolations(); for (int usr = 1; usr < 3; ++usr) { diff --git a/src/V3VariableOrder.cpp b/src/V3VariableOrder.cpp index bdff566ba..6f19fe113 100644 --- a/src/V3VariableOrder.cpp +++ b/src/V3VariableOrder.cpp @@ -169,7 +169,7 @@ class VariableOrder final { : (sigbytes == 1) ? 2 : 10; // Anonymous structure ok - attributes.anonOk = EmitCBaseVisitor::isAnonOk(varp); + attributes.anonOk = EmitCBase::isAnonOk(varp); } } diff --git a/src/V3Waiver.cpp b/src/V3Waiver.cpp index 606aa0b46..deb9ec6f7 100644 --- a/src/V3Waiver.cpp +++ b/src/V3Waiver.cpp @@ -19,12 +19,15 @@ #include "V3Waiver.h" #include "V3File.h" +#include "V3Options.h" #include #include -void V3Waiver::addEntry(V3ErrorCode errorCode, const std::string& filename, - const std::string& str) { +void V3Waiver::addEntry(V3ErrorCode errorCode, const std::string& filename, const std::string& str) + VL_MT_SAFE_EXCLUDES(s_mutex) { + if (filename == V3Options::getStdPackagePath()) return; + const V3LockGuard lock{s_mutex}; std::stringstream entry; const size_t pos = str.find('\n'); entry << "lint_off -rule " << errorCode.ascii() << " -file \"*" << filename << "\" -match \"" @@ -34,7 +37,7 @@ void V3Waiver::addEntry(V3ErrorCode errorCode, const std::string& filename, s_waiverList.push_back(entry.str()); } -void V3Waiver::write(const std::string& filename) { +void V3Waiver::write(const std::string& filename) VL_MT_SAFE_EXCLUDES(s_mutex) { const std::unique_ptr ofp{V3File::new_ofstream(filename)}; if (ofp->fail()) v3fatal("Can't write " << filename); @@ -48,9 +51,12 @@ void V3Waiver::write(const std::string& filename) { *ofp << "// 2. Keep the waiver permanently if you are sure this is okay\n"; *ofp << "// 3. Keep the waiver temporarily to suppress the output\n\n"; + const V3LockGuard lock{s_mutex}; + if (s_waiverList.empty()) *ofp << "// No waivers needed - great!\n"; for (const auto& i : s_waiverList) *ofp << "// " << i << "\n\n"; } +V3Mutex V3Waiver::s_mutex; V3Waiver::WaiverList V3Waiver::s_waiverList; diff --git a/src/V3Waiver.h b/src/V3Waiver.h index 10f711d1c..8ad1e62dc 100644 --- a/src/V3Waiver.h +++ b/src/V3Waiver.h @@ -18,6 +18,7 @@ #define VERILATOR_V3WAIVER_H_ #include "V3Error.h" +#include "V3Mutex.h" #include #include @@ -25,11 +26,13 @@ class V3Waiver final { // TYPES using WaiverList = std::vector; - static WaiverList s_waiverList; + static V3Mutex s_mutex; // Protect members + static WaiverList s_waiverList VL_GUARDED_BY(s_mutex); public: - static void addEntry(V3ErrorCode errorCode, const string& filename, const std::string& str); - static void write(const std::string& filename); + static void addEntry(V3ErrorCode errorCode, const string& filename, const std::string& str) + VL_MT_SAFE_EXCLUDES(s_mutex); + static void write(const std::string& filename) VL_MT_SAFE_EXCLUDES(s_mutex); }; #endif // Guard diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 89ee71e5a..d1583e1c4 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -224,6 +224,7 @@ private: // STATE WidthVP* m_vup = nullptr; // Current node state + AstClass* m_classp = nullptr; // Current class const AstCell* m_cellp = nullptr; // Current cell for arrayed instantiations const AstEnumItem* m_enumItemp = nullptr; // Current enum item const AstNodeFTask* m_ftaskp = nullptr; // Current function/task @@ -858,11 +859,11 @@ private: << std::hex << width); } // Note width() not set on range; use elementsConst() - if (nodep->littleEndian() && !VN_IS(nodep->backp(), UnpackArrayDType) + if (nodep->ascending() && !VN_IS(nodep->backp(), UnpackArrayDType) && !VN_IS(nodep->backp(), Cell)) { // For cells we warn in V3Inst - nodep->v3warn(LITENDIAN, "Big bit endian vector: left < right of bit range: [" - << nodep->leftConst() << ":" << nodep->rightConst() - << "]"); + nodep->v3warn(ASCRANGE, "Ascending bit range vector: left < right of bit range: [" + << nodep->leftConst() << ":" << nodep->rightConst() + << "]"); } } } @@ -1101,18 +1102,18 @@ private: // Add subtracted value to get the original range const VNumRange declRange{nodep->declRange().hi() + subtracted, nodep->declRange().lo() + subtracted, - nodep->declRange().littleEndian()}; + nodep->declRange().ascending()}; if ((declRange.hi() > adtypep->declRange().hi()) || declRange.lo() < adtypep->declRange().lo()) { // Other simulators warn too nodep->v3error("Slice selection index '" << declRange << "'" << " outside data type's '" << adtypep->declRange() << "'"); - } else if ((declRange.littleEndian() != adtypep->declRange().littleEndian()) + } else if ((declRange.ascending() != adtypep->declRange().ascending()) && declRange.hi() != declRange.lo()) { nodep->v3error("Slice selection '" << declRange << "'" - << " has backward indexing versus data type's '" + << " has reversed range order versus data type's '" << adtypep->declRange() << "'"); } } @@ -1296,15 +1297,15 @@ private: if (const auto* const varp = VN_CAST(nodep->backp(), Var)) { if (varp->isParam()) return; // Ok, leave } - // queue_slice[#:$] - if (const auto* const selp = VN_CAST(nodep->backp(), SelExtract)) { - if (VN_IS(selp->fromp()->dtypep(), QueueDType)) { - nodep->replaceWith( - new AstConst(nodep->fileline(), AstConst::Signed32{}, 0x7FFFFFFF)); - VL_DO_DANGLING(nodep->deleteTree(), nodep); - return; - } + AstNode* backp = nodep->backp(); + if (VN_IS(backp, Sub)) backp = backp->backp(); + if (const auto* const selp = VN_CAST(backp, SelExtract)) { + if (VN_IS(selp->fromp()->dtypep(), QueueDType)) return; } + if (const auto* const selp = VN_CAST(backp, SelBit)) { + if (VN_IS(selp->fromp()->dtypep(), QueueDType)) return; + } + // queue_slice[#:$] and queue_bitsel[$] etc handled in V3WidthSel nodep->v3warn(E_UNSUPPORTED, "Unsupported/illegal unbounded ('$') in this context."); } void visit(AstIsUnbounded* nodep) override { @@ -1682,6 +1683,7 @@ private: // Iterate into subDTypep() to resolve that type and update pointer. nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); nodep->dtypep(nodep); // The array itself, not subDtype + userIterateAndNext(nodep->boundp(), WidthVP{SELF, BOTH}.p()); if (VN_IS(nodep->boundp(), Unbounded)) { nodep->boundp()->unlinkFrBack()->deleteTree(); // nullptr will represent unbounded } @@ -2595,6 +2597,8 @@ private: if (nodep->didWidthAndSet()) return; // Must do extends first, as we may in functions under this class // start following a tree of extends that takes us to other classes + VL_RESTORER(m_classp); + m_classp = nodep; userIterateAndNext(nodep->extendsp(), nullptr); userIterateChildren(nodep, nullptr); // First size all members nodep->repairCache(); @@ -2610,6 +2614,7 @@ private: // userIterateChildren(nodep->classp(), nullptr); } void visit(AstNodeModule* nodep) override { + // Visitor does not include AstClass - specialized visitor above VL_RESTORER(m_modp); m_modp = nodep; userIterateChildren(nodep, nullptr); @@ -2666,6 +2671,14 @@ private: } if (AstEnumItemRef* const adfoundp = VN_CAST(foundp, EnumItemRef)) { nodep->replaceWith(adfoundp->cloneTree(false)); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } + if (AstNodeFTask* const methodp = VN_CAST(foundp, NodeFTask)) { + nodep->replaceWith(new AstMethodCall{nodep->fileline(), + nodep->fromp()->unlinkFrBack(), + nodep->name(), nullptr}); + VL_DO_DANGLING(pushDeletep(nodep), nodep); return; } UINFO(1, "found object " << foundp << endl); @@ -2816,7 +2829,9 @@ private: AstBasicDType* const basicp = fromDtp ? fromDtp->basicp() : nullptr; UINFO(9, " from dt " << fromDtp << endl); userIterate(fromDtp, WidthVP{SELF, BOTH}.p()); - if (AstEnumDType* const adtypep = VN_CAST(fromDtp, EnumDType)) { + if (nodep->name() == "rand_mode") { + methodCallRandMode(nodep); + } else if (AstEnumDType* const adtypep = VN_CAST(fromDtp, EnumDType)) { methodCallEnum(nodep, adtypep); } else if (AstAssocArrayDType* const adtypep = VN_CAST(fromDtp, AssocArrayDType)) { methodCallAssoc(nodep, adtypep); @@ -2830,6 +2845,8 @@ private: methodCallClass(nodep, adtypep); } else if (AstUnpackArrayDType* const adtypep = VN_CAST(fromDtp, UnpackArrayDType)) { methodCallUnpack(nodep, adtypep); + } else if (basicp && nodep->name() == "constraint_mode") { + methodCallConstraint(nodep, basicp); } else if (basicp && basicp->isEvent()) { methodCallEvent(nodep, basicp); } else if (basicp && basicp->isString()) { @@ -3200,6 +3217,8 @@ private: varrefp->access(access); } else if (const AstMemberSel* const ichildp = VN_CAST(childp, MemberSel)) { methodCallLValueRecurse(nodep, ichildp->fromp(), access); + } else if (const AstStructSel* const ichildp = VN_CAST(childp, StructSel)) { + methodCallLValueRecurse(nodep, ichildp->fromp(), access); } else if (const AstNodeSel* const ichildp = VN_CAST(childp, NodeSel)) { methodCallLValueRecurse(nodep, ichildp->fromp(), access); } else { @@ -3449,8 +3468,9 @@ private: // No need to width-resolve the class, as it was done when we did the child AstClass* const first_classp = adtypep->classp(); if (nodep->name() == "randomize") { - v3Global.useRandomizeMethods(true); V3Randomize::newRandomizeFunc(first_classp); + } else if (nodep->name() == "srandom") { + V3Randomize::newSRandomFunc(first_classp); } UASSERT_OBJ(first_classp, nodep, "Unlinked"); for (AstClass* classp = first_classp; classp;) { @@ -3517,6 +3537,26 @@ private: } nodep->dtypeSetSigned32(); // Guess on error } + void methodCallConstraint(AstMethodCall* nodep, AstBasicDType*) { + // Method call on constraint (currently hacked as just a var) + if (nodep->name() == "constraint_mode") { + methodOkArguments(nodep, 0, 1); + nodep->v3warn(CONSTRAINTIGN, "constraint_mode ignored (unsupported)"); + // Constraints ignored, so we just return "OFF" + nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}}); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } else { + nodep->v3fatalSrc("Unknown built-in constraint method " << nodep->prettyNameQ()); + } + } + void methodCallRandMode(AstMethodCall* nodep) { + // Method call on constraint (currently hacked as just a var) + methodOkArguments(nodep, 0, 1); + nodep->v3warn(CONSTRAINTIGN, "rand_mode ignored (unsupported)"); + // Disables ignored, so we just return "ON" + nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitTrue{}}); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } void methodCallUnpack(AstMethodCall* nodep, AstUnpackArrayDType* adtypep) { enum : uint8_t { UNKNOWN = 0, @@ -3758,7 +3798,7 @@ private: } // The AstNodeAssign visitor will be soon be replacing this node, make sure it gets it if (!VN_IS(nodep->backp(), NodeAssign)) { - if (adtypep) UINFO(1, "Got backp " << nodep->backp() << endl); + UINFO(1, "Got backp " << nodep->backp() << endl); nodep->v3error( "dynamic new() not expected in this context (expected under an assign)"); return; @@ -4861,15 +4901,13 @@ private: } void visit(AstFOpen* nodep) override { // Although a system function in IEEE, here a statement which sets the file pointer (MCD) - assertAtStatement(nodep); - iterateCheckFileDesc(nodep, nodep->filep(), BOTH); userIterateAndNext(nodep->filenamep(), WidthVP{SELF, BOTH}.p()); userIterateAndNext(nodep->modep(), WidthVP{SELF, BOTH}.p()); + nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return } void visit(AstFOpenMcd* nodep) override { - assertAtStatement(nodep); - iterateCheckFileDesc(nodep, nodep->filep(), BOTH); userIterateAndNext(nodep->filenamep(), WidthVP{SELF, BOTH}.p()); + nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return } void visit(AstFClose* nodep) override { assertAtStatement(nodep); @@ -5249,6 +5287,8 @@ private: // Function hasn't been widthed, so make it so. // Would use user1 etc, but V3Width called from too many places to spend a user nodep->doingWidth(true); + VL_RESTORER(m_funcp); + VL_RESTORER(m_ftaskp); m_ftaskp = nodep; // First width the function variable, as if is a recursive function we need data type if (nodep->fvarp()) userIterate(nodep->fvarp(), nullptr); @@ -5268,8 +5308,6 @@ private: nodep->didWidth(true); nodep->doingWidth(false); - m_funcp = nullptr; - m_ftaskp = nullptr; if (nodep->dpiImport() && !nodep->dpiOpenParent() && markHasOpenArray(nodep)) { nodep->dpiOpenParentInc(); // Mark so V3Task will wait for a child to build calling // func @@ -5495,6 +5533,21 @@ private: // For arguments, is assignment-like context; see IEEE rules in AstNodeAssign // Function hasn't been widthed, so make it so. UINFO(5, " FTASKREF " << nodep << endl); + if (nodep->name() == "randomize" || nodep->name() == "srandom") { + // TODO perhaps this should move to V3LinkDot + if (!m_classp) { + nodep->v3error("Calling implicit class method " << nodep->prettyNameQ() + << " without being under class"); + nodep->replaceWith(new AstConst{nodep->fileline(), 0}); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } + if (nodep->name() == "randomize") { + nodep->taskp(V3Randomize::newRandomizeFunc(m_classp)); + } else { + nodep->taskp(V3Randomize::newSRandomFunc(m_classp)); + } + } UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked"); if (nodep->didWidth()) return; userIterate(nodep->taskp(), nullptr); @@ -5599,7 +5652,7 @@ private: } void visit(AstNetlist* nodep) override { // Iterate modules backwards, in bottom-up order. That's faster - userIterateChildrenBackwards(nodep, nullptr); + userIterateChildrenBackwardsConst(nodep, nullptr); } //-------------------- @@ -6240,15 +6293,27 @@ private: } return false; // No change } - + bool isBaseClassRecurse(const AstClass* const cls1p, const AstClass* const cls2p) { + // Returns true if cls1p and cls2p are equal or if cls1p is a base class of cls2p + if (cls1p == cls2p) return true; + for (const AstClassExtends* cextp = cls2p->extendsp(); cextp; + cextp = VN_CAST(cextp->nextp(), ClassExtends)) { + if (isBaseClassRecurse(cls1p, cextp->classp())) return true; + } + return false; + } void checkClassAssign(AstNode* nodep, const char* side, AstNode* rhsp, AstNodeDType* lhsDTypep) { - if (VN_IS(lhsDTypep, ClassRefDType) - && !(rhsp->dtypep() && VN_IS(rhsp->dtypep()->skipRefp(), ClassRefDType))) { - if (auto* const constp = VN_CAST(rhsp, Const)) { + if (AstClassRefDType* const lhsClassRefp = VN_CAST(lhsDTypep, ClassRefDType)) { + UASSERT_OBJ(rhsp->dtypep(), rhsp, "Node has no type"); + AstNodeDType* const rhsDtypep = rhsp->dtypep()->skipRefp(); + if (AstClassRefDType* const rhsClassRefp = VN_CAST(rhsDtypep, ClassRefDType)) { + if (isBaseClassRecurse(lhsClassRefp->classp(), rhsClassRefp->classp())) return; + } else if (auto* const constp = VN_CAST(rhsp, Const)) { if (constp->num().isNull()) return; } - nodep->v3error(side << " expects a " << lhsDTypep->prettyTypeName()); + nodep->v3error(side << " expects a " << lhsDTypep->prettyTypeName() << ", got " + << rhsDtypep->prettyTypeName()); } } static bool similarDTypeRecurse(AstNodeDType* node1p, AstNodeDType* node2p) { @@ -6342,9 +6407,8 @@ private: constp->fileline(), lhsDTypep, new AstConst{constp->fileline(), AstConst::WidthedValue{}, 8, 0}}; for (int aindex = arrayp->lo(); aindex <= arrayp->hi(); ++aindex) { - int cindex = arrayp->declRange().littleEndian() - ? (arrayp->hi() - aindex) - : (aindex - arrayp->lo()); + int cindex = arrayp->declRange().ascending() ? (arrayp->hi() - aindex) + : (aindex - arrayp->lo()); V3Number selected{constp, 8}; selected.opSel(constp->num(), cindex * 8 + 7, cindex * 8); UINFO(0, " aindex=" << aindex << " cindex=" << cindex @@ -6961,7 +7025,7 @@ private: case VAttrType::DIM_LOW: val = !declRange.ranged() ? 0 : declRange.lo(); break; case VAttrType::DIM_RIGHT: val = !declRange.ranged() ? 0 : declRange.right(); break; case VAttrType::DIM_INCREMENT: - val = (declRange.ranged() && declRange.littleEndian()) ? -1 : 1; + val = (declRange.ranged() && declRange.ascending()) ? -1 : 1; break; case VAttrType::DIM_SIZE: val = !declRange.ranged() ? 0 : declRange.elements(); break; default: nodep->v3fatalSrc("Missing DIM ATTR type case"); break; @@ -7342,12 +7406,12 @@ private: iterateChildren(nodep); } } - void userIterateChildrenBackwards(AstNode* nodep, WidthVP* vup) { + void userIterateChildrenBackwardsConst(AstNode* nodep, WidthVP* vup) { if (!nodep) return; { VL_RESTORER(m_vup); m_vup = vup; - iterateChildrenBackwards(nodep); + iterateChildrenBackwardsConst(nodep); } } diff --git a/src/V3WidthSel.cpp b/src/V3WidthSel.cpp index 20b2741b7..c6308416b 100644 --- a/src/V3WidthSel.cpp +++ b/src/V3WidthSel.cpp @@ -19,7 +19,7 @@ // Replace SELEXTRACT with SEL // Replace SELBIT with SEL or ARRAYSEL // -// This code was once in V3LinkResolve, but little endian bit vectors won't +// This code was once in V3LinkResolve, but ascending bit range vectors won't // work that early. It was considered for V3Width and V3Param, but is // fairly ugly both places as the nodes change in too strongly // interconnected ways. @@ -162,7 +162,7 @@ private: // vector without range, or 0 lsb is ok, for example a INTEGER x; y = x[21:0]; return underp; } else { - if (fromRange.littleEndian()) { + if (fromRange.ascending()) { // reg [1:3] was swapped to [3:1] (lsbEndianedp==3) and needs a SUB(3,under) return newSubNeg(fromRange.hi(), underp); } else { @@ -180,7 +180,7 @@ private: } else { // Need a slice data type, which is an array of the extracted // type, but with (presumably) different size - const VNumRange newRange{msb, lsb, nodep->declRange().littleEndian()}; + const VNumRange newRange{msb, lsb, nodep->declRange().ascending()}; AstNodeDType* const vardtypep = new AstPackArrayDType{nodep->fileline(), nodep->subDTypep(), // Need to strip off array reference @@ -190,6 +190,18 @@ private: } } + AstNodeExpr* selQueueBackness(AstNode* nodep) { + if (VN_IS(nodep, Unbounded)) { // e.g. "[$]" + return new AstConst{nodep->fileline(), AstConst::Signed32{}, 0}; + } else if (VN_IS(nodep, Sub) && VN_IS(VN_CAST(nodep, Sub)->lhsp(), Unbounded)) { + // e.g. "q[$ - 1]", where 1 is subnodep + AstNodeExpr* subrhsp = VN_CAST(nodep, Sub)->rhsp()->unlinkFrBack(); + return subrhsp; + } else { + return nullptr; + } + } + void warnTri(AstNode* nodep) { if (VN_IS(nodep, Const) && VN_AS(nodep, Const)->num().isFourState()) { nodep->v3error( @@ -227,7 +239,7 @@ private: } else if (const AstPackArrayDType* const adtypep = VN_CAST(ddtypep, PackArrayDType)) { // SELBIT(array, index) -> SEL(array, index*width-of-subindex, width-of-subindex) AstNodeExpr* subp = rhsp; - if (fromRange.littleEndian()) { + if (fromRange.ascending()) { subp = newSubNeg(fromRange.hi(), subp); } else { subp = newSubNeg(subp, fromRange.lo()); @@ -250,8 +262,7 @@ private: VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (const AstAssocArrayDType* const adtypep = VN_CAST(ddtypep, AssocArrayDType)) { // SELBIT(array, index) -> ASSOCSEL(array, index) - AstNodeExpr* const subp = rhsp; - AstAssocSel* const newp = new AstAssocSel{nodep->fileline(), fromp, subp}; + AstAssocSel* const newp = new AstAssocSel{nodep->fileline(), fromp, rhsp}; newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference if (debug() >= 9) newp->dumpTree("- SELBTn: "); nodep->replaceWith(newp); @@ -259,24 +270,26 @@ private: } else if (const AstWildcardArrayDType* const adtypep = VN_CAST(ddtypep, WildcardArrayDType)) { // SELBIT(array, index) -> WILDCARDSEL(array, index) - AstNodeExpr* const subp = rhsp; - AstWildcardSel* const newp = new AstWildcardSel{nodep->fileline(), fromp, subp}; + AstWildcardSel* const newp = new AstWildcardSel{nodep->fileline(), fromp, rhsp}; newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference if (debug() >= 9) newp->dumpTree("- SELBTn: "); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (const AstDynArrayDType* const adtypep = VN_CAST(ddtypep, DynArrayDType)) { // SELBIT(array, index) -> CMETHODCALL(queue, "at", index) - AstNodeExpr* const subp = rhsp; - AstCMethodHard* const newp = new AstCMethodHard{nodep->fileline(), fromp, "at", subp}; + AstCMethodHard* const newp = new AstCMethodHard{nodep->fileline(), fromp, "at", rhsp}; newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off queue reference if (debug() >= 9) newp->dumpTree("- SELBTq: "); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (const AstQueueDType* const adtypep = VN_CAST(ddtypep, QueueDType)) { // SELBIT(array, index) -> CMETHODCALL(queue, "at", index) - AstNodeExpr* const subp = rhsp; - AstCMethodHard* const newp = new AstCMethodHard{nodep->fileline(), fromp, "at", subp}; + AstCMethodHard* newp; + if (AstNodeExpr* const backnessp = selQueueBackness(rhsp)) { + newp = new AstCMethodHard{nodep->fileline(), fromp, "atBack", backnessp}; + } else { + newp = new AstCMethodHard{nodep->fileline(), fromp, "at", rhsp}; + } newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off queue reference if (debug() >= 9) newp->dumpTree("- SELBTq: "); nodep->replaceWith(newp); @@ -338,19 +351,43 @@ private: V3Const::constifyParamsEdit(nodep->leftp()); // May relink pointed to node V3Const::constifyParamsEdit(nodep->rightp()); // May relink pointed to node // if (debug() >= 9) nodep->dumpTree("- SELEX3: "); + AstNodeExpr* const fromp = nodep->fromp()->unlinkFrBack(); + const FromData fromdata = fromDataForArray(nodep, fromp); + AstNodeDType* const ddtypep = fromdata.m_dtypep; + const VNumRange fromRange = fromdata.m_fromRange; + if (VN_IS(ddtypep, QueueDType)) { + AstNodeExpr* const qleftp = nodep->rhsp()->unlinkFrBack(); + AstNodeExpr* const qrightp = nodep->thsp()->unlinkFrBack(); + AstNodeExpr* const qleftBacknessp = selQueueBackness(qleftp); + AstNodeExpr* const qrightBacknessp = selQueueBackness(qrightp); + // Use special methods to refer to back rather than math using + // queue size, this allows a single queue reference, to support + // for equations in side effects that select the queue to + // operate upon. + std::string name = (qleftBacknessp ? "sliceBackBack" + : qrightBacknessp ? "sliceFrontBack" + : "slice"); + auto* const newp = new AstCMethodHard{nodep->fileline(), fromp, name, + qleftBacknessp ? qleftBacknessp : qleftp}; + newp->addPinsp(qrightBacknessp ? qrightBacknessp : qrightp); + newp->dtypep(ddtypep); + newp->didWidth(true); + newp->protect(false); + UINFO(6, " new " << newp << endl); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } + // Non-queue checkConstantOrReplace(nodep->leftp(), "First value of [a:b] isn't a constant, maybe you want +: or -:"); checkConstantOrReplace(nodep->rightp(), "Second value of [a:b] isn't a constant, maybe you want +: or -:"); - AstNodeExpr* const fromp = nodep->fromp()->unlinkFrBack(); AstNodeExpr* const msbp = nodep->rhsp()->unlinkFrBack(); AstNodeExpr* const lsbp = nodep->thsp()->unlinkFrBack(); int32_t msb = VN_AS(msbp, Const)->toSInt(); int32_t lsb = VN_AS(lsbp, Const)->toSInt(); const int32_t elem = (msb > lsb) ? (msb - lsb + 1) : (lsb - msb + 1); - const FromData fromdata = fromDataForArray(nodep, fromp); - AstNodeDType* const ddtypep = fromdata.m_dtypep; - const VNumRange fromRange = fromdata.m_fromRange; if (VN_IS(ddtypep, UnpackArrayDType)) { // Slice extraction if (fromRange.elements() == elem @@ -375,8 +412,8 @@ private: adtypep, "Array extraction with width miscomputed " << adtypep->width() << "/" << fromRange.elements()); - if (fromRange.littleEndian()) { - // Below code assumes big bit endian; just works out if we swap + if (fromRange.ascending()) { + // Below code assumes descending bit range; just works out if we swap const int x = msb; msb = lsb; lsb = x; @@ -385,7 +422,7 @@ private: nodep->v3warn( SELRANGE, "[" << msb << ":" << lsb - << "] Range extract has backward bit ordering, perhaps you wanted [" << lsb + << "] Slice range has ascending bit ordering, perhaps you wanted [" << lsb << ":" << msb << "]"); const int x = msb; msb = lsb; @@ -405,8 +442,8 @@ private: nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (VN_IS(ddtypep, BasicDType)) { - if (fromRange.littleEndian()) { - // Below code assumes big bit endian; just works out if we swap + if (fromRange.ascending()) { + // Below code assumes descending bit range; just works out if we swap const int x = msb; msb = lsb; lsb = x; @@ -415,7 +452,7 @@ private: nodep->v3warn( SELRANGE, "[" << msb << ":" << lsb - << "] Range extract has backward bit ordering, perhaps you wanted [" << lsb + << "] Slice range has ascending bit ordering, perhaps you wanted [" << lsb << ":" << msb << "]"); const int x = msb; msb = lsb; @@ -432,12 +469,12 @@ private: nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (VN_IS(ddtypep, NodeUOrStructDType)) { - // Classes aren't little endian + // Classes don't have an ascending range if (lsb > msb) { nodep->v3warn( SELRANGE, "[" << msb << ":" << lsb - << "] Range extract has backward bit ordering, perhaps you wanted [" << lsb + << "] Slice range has ascending bit ordering, perhaps you wanted [" << lsb << ":" << msb << "]"); const int x = msb; msb = lsb; @@ -453,15 +490,6 @@ private: // if (debug() >= 9) newp->dumpTree("- SELEXnew: "); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); - } else if (VN_IS(ddtypep, QueueDType)) { - auto* const newp = new AstCMethodHard{nodep->fileline(), fromp, "slice", msbp}; - msbp->addNext(lsbp); - newp->dtypep(ddtypep); - newp->didWidth(true); - newp->protect(false); - UINFO(6, " new " << newp << endl); - nodep->replaceWith(newp); - VL_DO_DANGLING(pushDeletep(nodep), nodep); } else { // nullptr=bad extract, or unknown node type nodep->v3error("Illegal range select; type already selected, or bad dimension: " << "data type is " << fromdata.m_errp->prettyDTypeNameQ()); @@ -485,7 +513,8 @@ private: V3Width::widthParamsEdit(nodep->rhsp()); // constifyEdit doesn't ensure widths finished V3Const::constifyEdit(nodep->rhsp()); // May relink pointed to node, ok if not const V3Const::constifyParamsEdit(nodep->thsp()); // May relink pointed to node - checkConstantOrReplace(nodep->thsp(), "Width of :+ or :- bit extract isn't a constant"); + checkConstantOrReplace(nodep->thsp(), + "Width of :+ or :- bit slice range isn't a constant"); if (debug() >= 9) nodep->dumpTree("- SELPM3: "); // Now replace it with an AstSel AstNodeExpr* const fromp = nodep->fromp()->unlinkFrBack(); @@ -521,7 +550,7 @@ private: const int32_t msb = VN_IS(nodep, SelPlus) ? rhs + width - 1 : rhs; const int32_t lsb = VN_IS(nodep, SelPlus) ? rhs : rhs - width + 1; AstSliceSel* const newp = new AstSliceSel{ - nodep->fileline(), fromp, VNumRange{msb, lsb, fromRange.littleEndian()}}; + nodep->fileline(), fromp, VNumRange{msb, lsb, fromRange.ascending()}}; nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else { @@ -539,7 +568,7 @@ private: } AstNodeExpr* newlsbp = nullptr; if (VN_IS(nodep, SelPlus)) { - if (fromRange.littleEndian()) { + if (fromRange.ascending()) { // SELPLUS(from,lsb,width) -> SEL(from, (vector_msb-width+1)-sel, width) newlsbp = newSubNeg((fromRange.hi() - width + 1), rhsp); } else { @@ -547,7 +576,7 @@ private: newlsbp = newSubNeg(rhsp, fromRange.lo()); } } else if (VN_IS(nodep, SelMinus)) { - if (fromRange.littleEndian()) { + if (fromRange.ascending()) { // SELMINUS(from,msb,width) -> SEL(from, msb-[bit]) newlsbp = newSubNeg(fromRange.hi(), rhsp); } else { diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 7ee1fb9e1..6c6940b0c 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -125,12 +125,6 @@ static void process() { // Convert parseref's to varrefs, and other directly post parsing fixups V3LinkParse::linkParse(v3Global.rootp()); - if (v3Global.opt.debugExitUvm()) { - V3Error::abortIfErrors(); - cout << "--debug-exit-uvm: Exiting after UVM-supported pass\n"; - std::exit(0); - } - // Cross-link signal names // Cross-link dotted hierarchical references V3LinkDot::linkDotPrimary(v3Global.rootp()); @@ -148,11 +142,17 @@ static void process() { V3Error::abortIfErrors(); if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "Link"); + if (v3Global.opt.debugExitUvm()) { + V3Error::abortIfErrors(); + cout << "--debug-exit-uvm: Exiting after UVM-supported pass\n"; + std::exit(0); + } // Remove parameters by cloning modules to de-parameterized versions // This requires some width calculations and constant propagation V3Param::param(v3Global.rootp()); V3LinkDot::linkDotParamed(v3Global.rootp()); // Cleanup as made new modules + V3LinkLValue::linkLValue(v3Global.rootp()); // Resolve new VarRefs V3Error::abortIfErrors(); // Remove any modules that were parameterized and are no longer referenced. @@ -566,6 +566,13 @@ static void process() { reportStatsIfEnabled(); if (!v3Global.opt.lintOnly() && !v3Global.opt.xmlOnly() && !v3Global.opt.dpiHdrOnly()) { + size_t src_f_cnt = 0; + for (AstNode* nodep = v3Global.rootp()->filesp(); nodep; nodep = nodep->nextp()) { + if (const AstCFile* cfilep = VN_CAST(nodep, CFile)) + src_f_cnt += cfilep->source() ? 1 : 0; + } + if (src_f_cnt >= V3EmitMk::PARALLEL_FILE_CNT_THRESHOLD) v3Global.useParallelBuild(true); + // Makefile must be after all other emitters if (v3Global.opt.main()) V3EmitCMain::emit(); if (v3Global.opt.cmake()) V3EmitCMake::emit(); @@ -591,6 +598,8 @@ static void verilate(const string& argString) { v3fatalSrc("VERILATOR_DEBUG_SKIP_IDENTICAL w/ --skip-identical: Changes found\n"); } // LCOV_EXCL_STOP + // Disable mutexes in single-thread verilation + V3MutexConfig::s().configure(v3Global.opt.verilateJobs() > 1 /*enable*/); // Adjust thread pool size V3ThreadPool::s().resize(v3Global.opt.verilateJobs()); @@ -618,6 +627,7 @@ static void verilate(const string& argString) { // Read first filename v3Global.readFiles(); + v3Global.removeStd(); // Link, etc, if needed if (!v3Global.opt.preprocOnly()) { // diff --git a/src/VlcSource.h b/src/VlcSource.h index f08188e1e..788945787 100644 --- a/src/VlcSource.h +++ b/src/VlcSource.h @@ -43,7 +43,7 @@ private: public: // CONSTRUCTORS - VlcSourceCount(int lineno) + explicit VlcSourceCount(int lineno) : m_lineno{lineno} {} ~VlcSourceCount() = default; diff --git a/src/VlcTop.cpp b/src/VlcTop.cpp index 728813622..db60851b0 100644 --- a/src/VlcTop.cpp +++ b/src/VlcTop.cpp @@ -288,7 +288,7 @@ void VlcTop::annotateOutputFiles(const string& dirname) { const auto lit = lines.find(lineno); if (lit == lines.end()) { os << " " << line << '\n'; - } else if (lit != lines.end()) { + } else { VlcSourceCount& sc = lit->second; // UINFO(0,"Source // "<cloneTree(cloneNextLink) : nullptr; }} - Ast{t}* clonep() const VL_MT_SAFE {{ return static_cast(AstNode::clonep()); }} + Ast{t}* clonep() const {{ return static_cast(AstNode::clonep()); }} Ast{t}* addNext(Ast{t}* nodep) {{ return static_cast(AstNode::addNext(this, nodep)); }} ''', t=node.name) if node.isLeaf: emitBlock('''\ - void accept(VNVisitor& v) override {{ v.visit(this); }} + void accept(VNVisitorConst& v) override {{ v.visit(this); }} AstNode* clone() override {{ return new Ast{t}(*this); }} ''', t=node.name) @@ -952,7 +952,7 @@ def write_ast_macros(filename): "op{n}p()").format(n=n, kind=kind) if monad == "List": emitBlock('''\ - Ast{kind}* {name}() const VL_MT_SAFE {{ return {retrieve}; }} + Ast{kind}* {name}() const VL_MT_STABLE {{ return {retrieve}; }} void add{Name}(Ast{kind}* nodep) {{ addNOp{n}p(reinterpret_cast(nodep)); }} ''', kind=kind, @@ -962,7 +962,7 @@ def write_ast_macros(filename): retrieve=retrieve) elif monad == "Optional": emitBlock('''\ - Ast{kind}* {name}() const VL_MT_SAFE {{ return {retrieve}; }} + Ast{kind}* {name}() const VL_MT_STABLE {{ return {retrieve}; }} void {name}(Ast{kind}* nodep) {{ setNOp{n}p(reinterpret_cast(nodep)); }} ''', kind=kind, @@ -971,7 +971,7 @@ def write_ast_macros(filename): retrieve=retrieve) else: emitBlock('''\ - Ast{kind}* {name}() const VL_MT_SAFE {{ return {retrieve}; }} + Ast{kind}* {name}() const VL_MT_STABLE {{ return {retrieve}; }} void {name}(Ast{kind}* nodep) {{ setOp{n}p(reinterpret_cast(nodep)); }} ''', kind=kind, @@ -1343,7 +1343,7 @@ if Args.classes: # Write Ast code write_forward_class_decls("Ast", AstNodeList) write_visitor_decls("Ast", AstNodeList) - write_visitor_defns("Ast", AstNodeList, "VNVisitor") + write_visitor_defns("Ast", AstNodeList, "VNVisitorConst") write_type_enum("Ast", AstNodeList) write_type_tests("Ast", AstNodeList) write_ast_macros("V3Ast__gen_macros.h") diff --git a/src/verilog.l b/src/verilog.l index 7793e2205..eec6be4ed 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -498,8 +498,8 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "assume" { FL; return yASSUME; } "before" { FL; return yBEFORE; } "bind" { FL; return yBIND; } - "bins" { ERROR_RSVD_WORD("SystemVerilog 2005"); } - "binsof" { ERROR_RSVD_WORD("SystemVerilog 2005"); } + "bins" { FL; return yBINS; } + "binsof" { FL; return yBINSOF; } "bit" { FL; return yBIT; } "break" { FL; return yBREAK; } "byte" { FL; return yBYTE; } @@ -511,41 +511,41 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "context" { FL; return yCONTEXT; } "continue" { FL; return yCONTINUE; } "cover" { FL; return yCOVER; } - "covergroup" { ERROR_RSVD_WORD("SystemVerilog 2005"); } - "coverpoint" { ERROR_RSVD_WORD("SystemVerilog 2005"); } - "cross" { ERROR_RSVD_WORD("SystemVerilog 2005"); } + "covergroup" { FL; return yCOVERGROUP; } + "coverpoint" { FL; return yCOVERPOINT; } + "cross" { FL; return yCROSS; } "dist" { FL; return yDIST; } "do" { FL; return yDO; } "endclass" { FL; return yENDCLASS; } "endclocking" { FL; return yENDCLOCKING; } - "endgroup" { ERROR_RSVD_WORD("SystemVerilog 2005"); } + "endgroup" { FL; return yENDGROUP; } "endinterface" { FL; return yENDINTERFACE; } "endpackage" { FL; return yENDPACKAGE; } "endprogram" { FL; return yENDPROGRAM; } "endproperty" { FL; return yENDPROPERTY; } "endsequence" { FL; return yENDSEQUENCE; } "enum" { FL; return yENUM; } - "expect" { ERROR_RSVD_WORD("SystemVerilog 2005"); } + "expect" { FL; return yEXPECT; } "export" { FL; return yEXPORT; } "extends" { FL; return yEXTENDS; } "extern" { FL; return yEXTERN; } "final" { FL; return yFINAL; } - "first_match" { ERROR_RSVD_WORD("SystemVerilog 2005"); } + "first_match" { FL; return yFIRST_MATCH; } "forkjoin" { FL; return yFORKJOIN; } "iff" { FL; return yIFF; } - "ignore_bins" { ERROR_RSVD_WORD("SystemVerilog 2005"); } - "illegal_bins" { ERROR_RSVD_WORD("SystemVerilog 2005"); } + "ignore_bins" { FL; return yIGNORE_BINS; } + "illegal_bins" { FL; return yILLEGAL_BINS; } "import" { FL; return yIMPORT; } "inside" { FL; return yINSIDE; } "int" { FL; return yINT; } "interface" { FL; return yINTERFACE; } - "intersect" { ERROR_RSVD_WORD("SystemVerilog 2005"); } + "intersect" { FL; return yINTERSECT; } "join_any" { FL; return yJOIN_ANY; } "join_none" { FL; return yJOIN_NONE; } "local" { FL; return yLOCAL__LEX; } "logic" { FL; return yLOGIC; } "longint" { FL; return yLONGINT; } - "matches" { ERROR_RSVD_WORD("SystemVerilog 2005"); } + "matches" { FL; return yMATCHES; } "modport" { FL; return yMODPORT; } "new" { FL; return yNEW__LEX; } "null" { FL; return yNULL; } @@ -564,7 +564,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "ref" { FL; return yREF; } "restrict" { FL; return yRESTRICT; } "return" { FL; return yRETURN; } - "sequence" { ERROR_RSVD_WORD("SystemVerilog 2005"); } + "sequence" { FL; return ySEQUENCE; } "shortint" { FL; return ySHORTINT; } "shortreal" { FL; return ySHORTREAL; } "solve" { FL; return ySOLVE; } @@ -574,7 +574,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "super" { FL; return ySUPER; } "tagged" { ERROR_RSVD_WORD("SystemVerilog 2005"); } "this" { FL; return yTHIS; } - "throughout" { ERROR_RSVD_WORD("SystemVerilog 2005"); } + "throughout" { FL; return yTHROUGHOUT; } "timeprecision" { FL; return yTIMEPRECISION; } "timeunit" { FL; return yTIMEUNIT; } "type" { FL; return yTYPE__LEX; } @@ -584,37 +584,37 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "var" { FL; return yVAR; } "virtual" { FL; return yVIRTUAL__LEX; } "void" { FL; return yVOID; } - "wait_order" { ERROR_RSVD_WORD("SystemVerilog 2005"); } - "wildcard" { ERROR_RSVD_WORD("SystemVerilog 2005"); } + "wait_order" { FL; return yWAIT_ORDER; } + "wildcard" { FL; return yWILDCARD; } "with" { FL; return yWITH__LEX; } - "within" { ERROR_RSVD_WORD("SystemVerilog 2005"); } + "within" { FL; return yWITHIN; } } /* SystemVerilog 2009 */ { /* Keywords */ - "accept_on" { ERROR_RSVD_WORD("SystemVerilog 2009"); } + "accept_on" { FL; return yACCEPT_ON; } "checker" { FL; return yCHECKER; } "endchecker" { FL; return yENDCHECKER; } - "eventually" { ERROR_RSVD_WORD("SystemVerilog 2009"); } + "eventually" { FL; return yEVENTUALLY; } "global" { FL; return yGLOBAL__LEX; } - "implies" { ERROR_RSVD_WORD("SystemVerilog 2009"); } - "let" { ERROR_RSVD_WORD("SystemVerilog 2009"); } - "nexttime" { ERROR_RSVD_WORD("SystemVerilog 2009"); } - "reject_on" { ERROR_RSVD_WORD("SystemVerilog 2009"); } - "s_always" { ERROR_RSVD_WORD("SystemVerilog 2009"); } - "s_eventually" { ERROR_RSVD_WORD("SystemVerilog 2009"); } - "s_nexttime" { ERROR_RSVD_WORD("SystemVerilog 2009"); } - "s_until" { ERROR_RSVD_WORD("SystemVerilog 2009"); } - "s_until_with" { ERROR_RSVD_WORD("SystemVerilog 2009"); } - "strong" { ERROR_RSVD_WORD("SystemVerilog 2009"); } - "sync_accept_on" { ERROR_RSVD_WORD("SystemVerilog 2009"); } - "sync_reject_on" { ERROR_RSVD_WORD("SystemVerilog 2009"); } + "implies" { FL; return yIMPLIES; } + "let" { FL; return yLET; } + "nexttime" { FL; return yNEXTTIME; } + "reject_on" { FL; return yREJECT_ON; } + "s_always" { FL; return yS_ALWAYS; } + "s_eventually" { FL; return yS_EVENTUALLY; } + "s_nexttime" { FL; return yS_NEXTTIME; } + "s_until" { FL; return yS_UNTIL; } + "s_until_with" { FL; return yS_UNTIL_WITH; } + "strong" { FL; return ySTRONG; } + "sync_accept_on" { FL; return ySYNC_ACCEPT_ON; } + "sync_reject_on" { FL; return ySYNC_REJECT_ON; } "unique0" { FL; return yUNIQUE0; } - "until" { ERROR_RSVD_WORD("SystemVerilog 2009"); } - "until_with" { ERROR_RSVD_WORD("SystemVerilog 2009"); } + "until" { FL; return yUNTIL; } + "until_with" { FL; return yUNTIL_WITH; } "untyped" { FL; return yUNTYPED; } - "weak" { ERROR_RSVD_WORD("SystemVerilog 2009"); } + "weak" { FL; return yWEAK; } } /* System Verilog 2012 */ @@ -875,6 +875,8 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} ">>>=" { FL; return yP_SSRIGHTEQ; } "->>" { FL; return yP_MINUSGTGT; } "##" { FL; return yP_POUNDPOUND; } + "#-#" { FL; return yP_POUNDMINUSPD; } + "#=#" { FL; return yP_POUNDEQPD; } "@@" { FL; return yP_ATAT; } "::" { FL; return yP_COLONCOLON; } ":=" { FL; return yP_COLONEQ; } @@ -883,8 +885,9 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "|=>" { FL; return yP_OREQGT; } /* Some simulators allow whitespace here. Grr */ "["{ws}*"*" { FL; return yP_BRASTAR; } - "["{ws}*"=" { FL; return yP_BRAEQ; } + "["{ws}*"+"{ws}*"]" { FL; return yP_BRAPLUSKET; } "["{ws}*"->" { FL; return yP_BRAMINUSGT; } + "["{ws}*"=" { FL; return yP_BRAEQ; } } /* SystemVerilog 2009 Operators */ @@ -910,7 +913,10 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} /* "# 1'b0" is a delay value so must lex as "#" "1" "'b0" */ if (PARSEP->lexPrevToken()=='#') { int shortlen = 0; - while (std::isdigit(yytext[shortlen])) ++shortlen; + if (std::isdigit(yytext[shortlen])) { + while (std::isdigit(yytext[shortlen]) + || yytext[shortlen]=='_') ++shortlen; + } if (shortlen) { // Push rest past numbers for later parse PARSEP->lexUnputString(yytext + shortlen, yyleng - shortlen); diff --git a/src/verilog.y b/src/verilog.y index fbf1142a9..c7d17f3fd 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -96,6 +96,7 @@ public: bool m_pinAnsi = false; // In ANSI port list bool m_tracingParse = true; // Tracing disable for parser bool m_inImplements = false; // Is inside class implements list + bool m_insideClass = false; // Is inside a class body bool m_insideProperty = false; // Is inside property declaration bool m_typedPropertyPort = false; // Typed property port occurred on port lists bool m_modportImpExpActive @@ -536,7 +537,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) // Double underscores "yX__Y" means token X followed by Y, // and "yX__ETC" means X folled by everything but Y(s). %token ya1STEP "1step" -//UNSUP %token yACCEPT_ON "accept_on" +%token yACCEPT_ON "accept_on" %token yALIAS "alias" %token yALWAYS "always" %token yALWAYS_COMB "always_comb" @@ -550,8 +551,8 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yBEFORE "before" %token yBEGIN "begin" %token yBIND "bind" -//UNSUP %token yBINS "bins" -//UNSUP %token yBINSOF "binsof" +%token yBINS "bins" +%token yBINSOF "binsof" %token yBIT "bit" %token yBREAK "break" %token yBUF "buf" @@ -564,20 +565,18 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yCHANDLE "chandle" %token yCHECKER "checker" %token yCLASS "class" -//UNSUP %token yCLOCK "clock" %token yCLOCKING "clocking" %token yCMOS "cmos" %token yCONSTRAINT "constraint" %token yCONST__ETC "const" %token yCONST__LEX "const-in-lex" -//UNSUP %token yCONST__LOCAL "const-then-local" %token yCONST__REF "const-then-ref" %token yCONTEXT "context" %token yCONTINUE "continue" %token yCOVER "cover" -//UNSUP %token yCOVERGROUP "covergroup" -//UNSUP %token yCOVERPOINT "coverpoint" -//UNSUP %token yCROSS "cross" +%token yCOVERGROUP "covergroup" +%token yCOVERPOINT "coverpoint" +%token yCROSS "cross" %token yDEASSIGN "deassign" %token yDEFAULT "default" %token yDEFPARAM "defparam" @@ -593,7 +592,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yENDCLOCKING "endclocking" %token yENDFUNCTION "endfunction" %token yENDGENERATE "endgenerate" -//UNSUP %token yENDGROUP "endgroup" +%token yENDGROUP "endgroup" %token yENDINTERFACE "endinterface" %token yENDMODULE "endmodule" %token yENDPACKAGE "endpackage" @@ -606,13 +605,13 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yENDTASK "endtask" %token yENUM "enum" %token yEVENT "event" -//UNSUP %token yEVENTUALLY "eventually" -//UNSUP %token yEXPECT "expect" +%token yEVENTUALLY "eventually" +%token yEXPECT "expect" %token yEXPORT "export" %token yEXTENDS "extends" %token yEXTERN "extern" %token yFINAL "final" -//UNSUP %token yFIRST_MATCH "first_match" +%token yFIRST_MATCH "first_match" %token yFOR "for" %token yFORCE "force" %token yFOREACH "foreach" @@ -620,9 +619,6 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yFORK "fork" %token yFORKJOIN "forkjoin" %token yFUNCTION "function" -//UNSUP %token yFUNCTION__ETC "function" -//UNSUP %token yFUNCTION__LEX "function-in-lex" -//UNSUP %token yFUNCTION__aPUREV "function-is-pure-virtual" %token yGENERATE "generate" %token yGENVAR "genvar" %token yGLOBAL__CLOCKING "global-then-clocking" @@ -632,10 +628,10 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yHIGHZ1 "highz1" %token yIF "if" %token yIFF "iff" -//UNSUP %token yIGNORE_BINS "ignore_bins" -//UNSUP %token yILLEGAL_BINS "illegal_bins" +%token yIGNORE_BINS "ignore_bins" +%token yILLEGAL_BINS "illegal_bins" %token yIMPLEMENTS "implements" -//UNSUP %token yIMPLIES "implies" +%token yIMPLIES "implies" %token yIMPORT "import" %token yINITIAL "initial" %token yINOUT "inout" @@ -645,18 +641,18 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yINTEGER "integer" %token yINTERCONNECT "interconnect" %token yINTERFACE "interface" -//UNSUP %token yINTERSECT "intersect" +%token yINTERSECT "intersect" %token yJOIN "join" %token yJOIN_ANY "join_any" %token yJOIN_NONE "join_none" -//UNSUP %token yLET "let" +%token yLET "let" %token yLOCALPARAM "localparam" %token yLOCAL__COLONCOLON "local-then-::" %token yLOCAL__ETC "local" %token yLOCAL__LEX "local-in-lex" %token yLOGIC "logic" %token yLONGINT "longint" -//UNSUP %token yMATCHES "matches" +%token yMATCHES "matches" %token yMODPORT "modport" %token yMODULE "module" %token yNAND "nand" @@ -665,7 +661,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yNEW__ETC "new" %token yNEW__LEX "new-in-lex" %token yNEW__PAREN "new-then-paren" -//UNSUP %token yNEXTTIME "nexttime" +%token yNEXTTIME "nexttime" %token yNMOS "nmos" %token yNOR "nor" %token yNOT "not" @@ -699,7 +695,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yREALTIME "realtime" %token yREF "ref" %token yREG "reg" -//UNSUP %token yREJECT_ON "reject_on" +%token yREJECT_ON "reject_on" %token yRELEASE "release" %token yREPEAT "repeat" %token yRESTRICT "restrict" @@ -710,7 +706,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yRTRANIF0 "rtranif0" %token yRTRANIF1 "rtranif1" %token ySCALARED "scalared" -//UNSUP %token ySEQUENCE "sequence" +%token ySEQUENCE "sequence" %token ySHORTINT "shortint" %token ySHORTREAL "shortreal" %token ySIGNED "signed" @@ -722,28 +718,25 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token ySTATIC__ETC "static" %token ySTATIC__LEX "static-in-lex" %token ySTRING "string" -//UNSUP %token ySTRONG "strong" +%token ySTRONG "strong" %token ySTRONG0 "strong0" %token ySTRONG1 "strong1" %token ySTRUCT "struct" %token ySUPER "super" %token ySUPPLY0 "supply0" %token ySUPPLY1 "supply1" -//UNSUP %token ySYNC_ACCEPT_ON "sync_accept_on" -//UNSUP %token ySYNC_REJECT_ON "sync_reject_on" -//UNSUP %token yS_ALWAYS "s_always" -//UNSUP %token yS_EVENTUALLY "s_eventually" -//UNSUP %token yS_NEXTTIME "s_nexttime" -//UNSUP %token yS_UNTIL "s_until" -//UNSUP %token yS_UNTIL_WITH "s_until_with" +%token ySYNC_ACCEPT_ON "sync_accept_on" +%token ySYNC_REJECT_ON "sync_reject_on" +%token yS_ALWAYS "s_always" +%token yS_EVENTUALLY "s_eventually" +%token yS_NEXTTIME "s_nexttime" +%token yS_UNTIL "s_until" +%token yS_UNTIL_WITH "s_until_with" %token yTABLE "table" //UNSUP %token yTAGGED "tagged" %token yTASK "task" -//UNSUP %token yTASK__ETC "task" -//UNSUP %token yTASK__LEX "task-in-lex" -//UNSUP %token yTASK__aPUREV "task-is-pure-virtual" %token yTHIS "this" -//UNSUP %token yTHROUGHOUT "throughout" +%token yTHROUGHOUT "throughout" %token yTIME "time" %token yTIMEPRECISION "timeprecision" %token yTIMEUNIT "timeunit" @@ -765,8 +758,8 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yUNIQUE "unique" %token yUNIQUE0 "unique0" %token yUNSIGNED "unsigned" -//UNSUP %token yUNTIL "until" -//UNSUP %token yUNTIL_WITH "until_with" +%token yUNTIL "until" +%token yUNTIL_WITH "until_with" %token yUNTYPED "untyped" %token yVAR "var" %token yVECTORED "vectored" @@ -777,15 +770,15 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yVIRTUAL__anyID "virtual-then-identifier" %token yVOID "void" %token yWAIT "wait" -//UNSUP %token yWAIT_ORDER "wait_order" +%token yWAIT_ORDER "wait_order" %token yWAND "wand" -//UNSUP %token yWEAK "weak" +%token yWEAK "weak" %token yWEAK0 "weak0" %token yWEAK1 "weak1" %token yWHILE "while" -//UNSUP %token yWILDCARD "wildcard" +%token yWILDCARD "wildcard" %token yWIRE "wire" -//UNSUP %token yWITHIN "within" +%token yWITHIN "within" %token yWITH__BRA "with-then-[" %token yWITH__CUR "with-then-{" %token yWITH__ETC "with" @@ -1005,7 +998,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yP_COLON__BEGIN ":-begin" %token yP_COLON__FORK ":-fork" -//UNSUP %token yP_PAR__IGNORE "(-ignored" // Used when sequence_expr:expr:( is ignored +%token yP_PAR__IGNORE "(-ignored" // Used when sequence_expr:expr:( is ignored %token yP_PAR__STRENGTH "(-for-strength" %token yP_LTMINUSGT "<->" @@ -1017,8 +1010,8 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yP_ASTGT "*>" %token yP_ANDANDAND "&&&" %token yP_POUNDPOUND "##" -//UNSUP %token yP_POUNDMINUSPD "#-#" -//UNSUP %token yP_POUNDEQPD "#=#" +%token yP_POUNDMINUSPD "#-#" +%token yP_POUNDEQPD "#=#" %token yP_DOTSTAR ".*" %token yP_ATAT "@@" @@ -1030,7 +1023,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yP_BRASTAR "[*" %token yP_BRAEQ "[=" %token yP_BRAMINUSGT "[->" -//UNSUP %token yP_BRAPLUSKET "[+]" +%token yP_BRAPLUSKET "[+]" %token yP_PLUSPLUS "++" %token yP_MINUSMINUS "--" @@ -1056,11 +1049,11 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) //UNSUP %token prREDUCTION //UNSUP %token prNEGATION //UNSUP %token prEVENTBEGIN -//UNSUP %token prTAGGED +%token prTAGGED // These prevent other conflicts %left yP_ANDANDAND -//UNSUP %left yMATCHES +%left yMATCHES //UNSUP %left prTAGGED //UNSUP %left prSEQ_CLOCKING @@ -1068,21 +1061,20 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) // Lowest precedence // These are in IEEE 17.7.1 -//UNSUP %nonassoc yALWAYS yS_ALWAYS yEVENTUALLY yS_EVENTUALLY yACCEPT_ON yREJECT_ON ySYNC_ACCEPT_ON ySYNC_REJECT_ON +%nonassoc yALWAYS yS_ALWAYS yEVENTUALLY yS_EVENTUALLY yACCEPT_ON yREJECT_ON ySYNC_ACCEPT_ON ySYNC_REJECT_ON -%right yP_ORMINUSGT yP_OREQGT -//UNSUP %right yP_ORMINUSGT yP_OREQGT yP_POUNDMINUSPD yP_POUNDEQPD -//UNSUP %right yUNTIL yS_UNTIL yUNTIL_WITH yS_UNTIL_WITH yIMPLIES -//UNSUP %right yIFF -//UNSUP %left yOR -//UNSUP %left yAND -//UNSUP %nonassoc yNOT yNEXTTIME yS_NEXTTIME -//UNSUP %left yINTERSECT -//UNSUP %left yWITHIN -//UNSUP %right yTHROUGHOUT -//UNSUP %left prPOUNDPOUND_MULTI -//UNSUP %left yP_POUNDPOUND -//UNSUP %left yP_BRASTAR yP_BRAEQ yP_BRAMINUSGT yP_BRAPLUSKET +%right yP_ORMINUSGT yP_OREQGT yP_POUNDMINUSPD yP_POUNDEQPD +%right yUNTIL yS_UNTIL yUNTIL_WITH yS_UNTIL_WITH yIMPLIES +%right yIFF +%left yOR +%left yAND +%nonassoc yNOT yNEXTTIME yS_NEXTTIME +%left yINTERSECT +%left yWITHIN +%right yTHROUGHOUT +%left prPOUNDPOUND_MULTI +%left yP_POUNDPOUND +%left yP_BRASTAR yP_BRAEQ yP_BRAMINUSGT yP_BRAPLUSKET // Not specified, but needed higher than yOR, lower than normal non-pexpr expressions //UNSUP %left yPOSEDGE yNEGEDGE yEDGE @@ -1270,7 +1262,7 @@ package_or_generate_item_declaration: // ==IEEE: package_or_generate_i // // class_constructor_declaration is part of function_declaration // // local_parameter_declaration under parameter_declaration | parameter_declaration ';' { $$ = $1; } - //UNSUP covergroup_declaration { $$ = $1; } + | covergroup_declaration { $$ = $1; } | assertion_item_declaration { $$ = $1; } | ';' { $$ = nullptr; } ; @@ -1426,18 +1418,18 @@ parameter_port_listE: // IEEE: parameter_port_list + empty == paramete paramPortDeclOrArgList: // IEEE: list_of_param_assignments + { parameter_port_declaration } paramPortDeclOrArg { $$ = $1; } | paramPortDeclOrArgList ',' paramPortDeclOrArg { $$ = $1->addNext($3); } - | paramPortDeclOrArgList sigAttrScope {$$ = $1;} + | paramPortDeclOrArgList sigAttrScope { $$ = $1; } ; paramPortDeclOrArg: // IEEE: param_assignment + parameter_port_declaration // // We combine the two as we can't tell which follows a comma - paramPortDeclOrArgSub {$$ = $1;} + paramPortDeclOrArgSub { $$ = $1; } | vlTag { $$ = nullptr; } ; paramPortDeclOrArgSub: parameter_port_declarationFrontE param_assignment { $$ = $2; } | parameter_port_declarationTypeFrontE type_assignment { $$ = $2; } - | sigAttrScope paramPortDeclOrArgSub {$$ = $2; } + | sigAttrScope paramPortDeclOrArgSub { $$ = $2; } ; portsStarE: // IEEE: .* + list_of_ports + list_of_port_declarations + empty @@ -1548,16 +1540,7 @@ port: // ==IEEE: port // // // Note implicit rules looks just line declaring additional followon port // // No VARDECL("port") for implicit, as we don't want to declare variables for them - //UNSUP portDirNetE data_type '.' portSig '(' portAssignExprE ')' sigAttrListE - //UNSUP { UNSUP } - //UNSUP portDirNetE yVAR data_type '.' portSig '(' portAssignExprE ')' sigAttrListE - //UNSUP { UNSUP } - //UNSUP portDirNetE yVAR implicit_type '.' portSig '(' portAssignExprE ')' sigAttrListE - //UNSUP { UNSUP } - //UNSUP portDirNetE signingE rangeList '.' portSig '(' portAssignExprE ')' sigAttrListE - //UNSUP { UNSUP } - //UNSUP portDirNetE /*implicit*/ '.' portSig '(' portAssignExprE ')' sigAttrListE - //UNSUP { UNSUP } + // // IEEE: portDirNetE data_type '.' portSig -> handled with AstDot in expr. // | portDirNetE data_type portSig variable_dimensionListE sigAttrListE { $$ = $3; VARDTYPE($2); addNextNull($$, VARDONEP($$, $4, $5)); } @@ -1653,7 +1636,7 @@ interface_item: // IEEE: interface_item + non_port_interface_ite port_declaration ';' { $$ = $1; } // // IEEE: non_port_interface_item // // IEEE: generate_region - | interface_generate_region { $$ = $1; } + | i_generate_region { $$ = $1; } | interface_or_generate_item { $$ = $1; } | program_declaration { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: program decls within interface decls"); } @@ -1666,11 +1649,6 @@ interface_item: // IEEE: interface_item + non_port_interface_ite | module_common_item { $$ = $1; } ; -interface_generate_region: // ==IEEE: generate_region - yGENERATE interface_itemList yENDGENERATE { $$ = $2; } - | yGENERATE yENDGENERATE { $$ = nullptr; } - ; - interface_or_generate_item: // ==IEEE: interface_or_generate_item // // module_common_item in interface_item, as otherwise duplicated // // with module_or_generate_item's module_common_item @@ -1701,7 +1679,7 @@ anonymous_program_item: // ==IEEE: anonymous_program_item task_declaration { $$ = $1; } | function_declaration { $$ = $1; } | class_declaration { $$ = $1; } - //UNSUP covergroup_declaration { $$ = $1; } + | covergroup_declaration { $$ = $1; } // // class_constructor_declaration is part of function_declaration | ';' { $$ = nullptr; } ; @@ -1825,11 +1803,11 @@ modportPortsDecl: // // IEEE: modport_simple_ports_declaration | modportSimplePortOrTFPort { $$ = GRAMMARP->m_modportImpExpActive ? static_cast( - new AstModportFTaskRef( - $1, *$1, GRAMMARP->m_modportImpExpLastIsExport) ) : + new AstModportFTaskRef{ + $1, *$1, GRAMMARP->m_modportImpExpLastIsExport} ) : static_cast( - new AstModportVarRef( - $1, *$1, GRAMMARP->m_varIO) ); } + new AstModportVarRef{ + $1, *$1, GRAMMARP->m_varIO} ); } ; modportSimplePortOrTFPort:// IEEE: modport_simple_port or modport_tf_port, depending what keyword was earlier @@ -1837,6 +1815,7 @@ modportSimplePortOrTFPort:// IEEE: modport_simple_port or modport_tf_port, | '.' idAny '(' ')' { $$ = $2; BBUNSUP($1, "Unsupported: Modport dotted port name"); } | '.' idAny '(' expr ')' { $$ = $2; BBUNSUP($1, "Unsupported: Modport dotted port name"); } ; + //************************************************ // Variable Declarations @@ -1965,7 +1944,12 @@ net_type: // ==IEEE: net_type ; varParamReset: - yPARAMETER { VARRESET_NONLIST(GPARAM); } + yPARAMETER + { if (GRAMMARP->m_insideClass) { + VARRESET_NONLIST(LPARAM); + } else { + VARRESET_NONLIST(GPARAM); + } } | yLOCALPARAM { VARRESET_NONLIST(LPARAM); } ; @@ -2498,6 +2482,9 @@ net_type_declaration: // IEEE: net_type_declaration { $$ = nullptr; BBUNSUP($1, "Unsupported: nettype"); } | yNETTYPE packageClassScopeE id/*net_type_identifier*/ idAny/*net_type_identifier*/ ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: nettype"); } + | yNETTYPE packageClassScopeE id/*net_type_identifier*/ idAny/*net_type_identifier*/ + /*cont*/ yWITH__ETC packageClassScopeE id/*tf_identifier*/ ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: nettype"); } ; implicit_typeE: // IEEE: part of *data_type_or_implicit @@ -2510,10 +2497,11 @@ implicit_typeE: // IEEE: part of *data_type_or_implicit { $$ = new AstBasicDType{$1, LOGIC_IMPLICIT, $1}; } ; -//UNSUPassertion_variable_declaration: // IEEE: assertion_variable_declaration -//UNSUP // // IEEE: var_data_type expanded -//UNSUP var_data_type list_of_variable_decl_assignments ';' { } -//UNSUP ; +assertion_variable_declaration: // IEEE: assertion_variable_declaration + // // IEEE: var_data_type expanded + var_data_type { VARRESET_NONLIST(VAR); VARDTYPE_NDECL($1); } + /*cont*/ list_of_variable_decl_assignments ';' { $$ = $3; } + ; type_declaration: // ==IEEE: type_declaration // Data_type expanded @@ -2741,6 +2729,10 @@ c_generate_region: // IEEE: generate_region (for checkers) BISONPRE_COPY(generate_region,{s/~c~/c_/g}) // {copied} ; +i_generate_region: // IEEE: generate_region (for interface) + BISONPRE_COPY(generate_region,{s/~c~/i_/g}) // {copied} + ; + generate_block_or_null: // IEEE: generate_block_or_null (called from gencase/genif/genfor) // ';' // is included in // // IEEE: generate_block @@ -2773,6 +2765,10 @@ c_genItemBegin: // IEEE: part of generate_block (for checkers) BISONPRE_COPY(genItemBegin,{s/~c~/c_/g}) // {copied} ; +i_genItemBegin: // IEEE: part of generate_block (for interfaces) + BISONPRE_COPY(genItemBegin,{s/~c~/i_/g}) // {copied} + ; + genItemOrBegin: // Not in IEEE, but our begin isn't under generate_item ~c~generate_item { $$ = $1; } | ~c~genItemBegin { $$ = $1; } @@ -2782,6 +2778,11 @@ c_genItemOrBegin: // (for checkers) BISONPRE_COPY(genItemOrBegin,{s/~c~/c_/g}) // {copied} ; +i_genItemOrBegin: // (for interfaces) + interface_item { $$ = $1; } + | i_genItemBegin { $$ = $1; } + ; + genItemList: ~c~genItemOrBegin { $$ = $1; } | ~c~genItemList ~c~genItemOrBegin { $$ = addNextNull($1, $2); } @@ -2791,6 +2792,10 @@ c_genItemList: // (for checkers) BISONPRE_COPY(genItemList,{s/~c~/c_/g}) // {copied} ; +i_genItemList: // (for interfaces) + BISONPRE_COPY(genItemList,{s/~c~/i_/g}) // {copied} + ; + generate_item: // IEEE: module_or_interface_or_generate_item // // Only legal when in a generate under a module (or interface under a module) module_or_generate_item { $$ = $1; } @@ -2958,7 +2963,7 @@ delay_control: //== IEEE: delay_control | '#' '(' minTypMax ',' minTypMax ')' { $$ = new AstDelay{$1, $3, false}; RISEFALLDLYUNSUP($3); DEL($5); } | '#' '(' minTypMax ',' minTypMax ',' minTypMax ')' - { $$ = new AstDelay{$1, $3, false}; RISEFALLDLYUNSUP($5); DEL($3); DEL($7); } + { $$ = new AstDelay{$1, $5, false}; RISEFALLDLYUNSUP($5); DEL($3); DEL($7); } ; delay_value: // ==IEEE:delay_value @@ -3109,6 +3114,8 @@ list_of_defparam_assignments: //== IEEE: list_of_defparam_assignments defparam_assignment: // ==IEEE: defparam_assignment idAny '.' idAny '=' expr { $$ = new AstDefParam{$4, *$1, *$3, $5}; } + | idAny '=' expr + { $$ = nullptr; BBUNSUP($2, "Unsupported: defparam with no dot"); } | idAny '.' idAny '.' { $$ = nullptr; BBUNSUP($4, "Unsupported: defparam with more than one dot"); } ; @@ -3228,12 +3235,23 @@ cellparamItemE: // IEEE: named_parameter_assignment + empty | '.' idAny '(' exprOrDataType ')' { $$ = new AstPin{$2, PINNUMINC(), *$2, $4}; $$->svDotName(true); } - //UNSUP '.' idAny '(' exprOrDataType/*expr*/ ':' expr ')' { } - //UNSUP '.' idAny '(' exprOrDataType/*expr*/ ':' expr ':' expr ')' { } + //UNSUP | '.' idAny '(' exprOrDataType/*expr*/ ':' expr ')' + //UNSUP { MINTYPMAXDLYUNSUP($4); DEL($4); + //UNSUP $$ = new AstPin{$2, PINNUMINC(), *$2, $6}; + //UNSUP $$->svDotName(true); } + //UNSUP | '.' idAny '(' exprOrDataType/*expr*/ ':' expr ':' expr ')' + //UNSUP { MINTYPMAXDLYUNSUP($4); DEL($4); DEL($8); + //UNSUP $$ = new AstPin{$2, PINNUMINC(), *$2, $6}; + //UNSUP $$->svDotName(true); } // // data_type for 'parameter type' hookups - | exprOrDataType { $$ = new AstPin{FILELINE_OR_CRE($1), PINNUMINC(), "", $1}; } - //UNSUP exprOrDataType/*expr*/ ':' expr { } - //UNSUP exprOrDataType/*expr*/ ':' expr ':' expr { } + | exprOrDataType + { $$ = new AstPin{FILELINE_OR_CRE($1), PINNUMINC(), "", $1}; } + //UNSUP | exprOrDataType/*expr*/ ':' expr + //UNSUP { MINTYPMAXDLYUNSUP($1); DEL($1); + //UNSUP $$ = new AstPin{FILELINE_OR_CRE($3), PINNUMINC(), "", $3}; } + //UNSUP | exprOrDataType/*expr*/ ':' expr ':' expr + //UNSUP { MINTYPMAXDLYUNSUP($1); DEL($1); DEL($5); + //UNSUP $$ = new AstPin{FILELINE_OR_CRE($3), PINNUMINC(), "", $3}; } ; cellpinItemE: // IEEE: named_port_connection + empty @@ -3308,8 +3326,11 @@ event_expression: // IEEE: event_expression - split over several senitem: // IEEE: part of event_expression, non-'OR' ',' terms senitemEdge { $$ = $1; } - | expr { $$ = new AstSenItem{$1, VEdgeType::ET_CHANGED, $1}; } - //UNSUP expr yIFF expr { UNSUP } + | expr + { $$ = new AstSenItem{$1, VEdgeType::ET_CHANGED, $1}; } + | expr yIFF expr + { $$ = new AstSenItem{$1, VEdgeType::ET_CHANGED, $1}; + if ($2) BBUNSUP($2, "Unsupported: event expression 'iff'"); } ; senitemVar: @@ -3317,12 +3338,21 @@ senitemVar: ; senitemEdge: // IEEE: part of event_expression - yPOSEDGE expr { $$ = new AstSenItem{$1, VEdgeType::ET_POSEDGE, $2}; } - | yNEGEDGE expr { $$ = new AstSenItem{$1, VEdgeType::ET_NEGEDGE, $2}; } - | yEDGE expr { $$ = new AstSenItem{$1, VEdgeType::ET_BOTHEDGE, $2}; } - //UNSUP yPOSEDGE expr yIFF expr { UNSUP } - //UNSUP yNEGEDGE expr yIFF expr { UNSUP } - //UNSUP yEDGE expr yIFF expr { UNSUP } + yPOSEDGE expr + { $$ = new AstSenItem{$1, VEdgeType::ET_POSEDGE, $2}; } + | yNEGEDGE expr + { $$ = new AstSenItem{$1, VEdgeType::ET_NEGEDGE, $2}; } + | yEDGE expr + { $$ = new AstSenItem{$1, VEdgeType::ET_BOTHEDGE, $2}; } + | yPOSEDGE expr yIFF expr + { $$ = new AstSenItem{$1, VEdgeType::ET_POSEDGE, $2}; + BBUNSUP($3, "Unsupported: event expression 'iff'"); } + | yNEGEDGE expr yIFF expr + { $$ = new AstSenItem{$1, VEdgeType::ET_NEGEDGE, $2}; + BBUNSUP($3, "Unsupported: event expression 'iff'"); } + | yEDGE expr yIFF expr + { $$ = new AstSenItem{$1, VEdgeType::ET_BOTHEDGE, $2}; + BBUNSUP($3, "Unsupported: event expression 'iff'"); } ; //************************************************ @@ -3421,7 +3451,7 @@ block_item_declarationList: // IEEE: [ block_item_declaration ] block_item_declaration: // ==IEEE: block_item_declaration data_declaration { $$ = $1; } | parameter_declaration ';' { $$ = $1; } - //UNSUP let_declaration { $$ = $1; } + | let_declaration { $$ = $1; } ; stmtList: @@ -3472,7 +3502,9 @@ statement_item: // IEEE: statement_item if ($1 == uniq_UNIQUE) $2->uniquePragma(true); if ($1 == uniq_UNIQUE0) $2->unique0Pragma(true); if ($1 == uniq_PRIORITY) $2->priorityPragma(true); } - //UNSUP caseStart caseAttrE yMATCHES case_patternListE yENDCASE { } + // &&& is part of expr so case_patternListE aliases to case_itemListE + | unique_priorityE caseStart caseAttrE yMATCHES case_patternListE yENDCASE + { $$ = nullptr; BBUNSUP($4, "Unsupported: matches (for tagged union)"); } | unique_priorityE caseStart caseAttrE yINSIDE case_insideListE yENDCASE { $$ = $2; if ($5) $2->addItemsp($5); if (!$2->caseSimple()) $2->v3error("Illegal to have inside on a casex/casez"); @@ -3495,9 +3527,8 @@ statement_item: // IEEE: statement_item if ($1 == uniq_UNIQUE0) newp->unique0Pragma(true); if ($1 == uniq_PRIORITY) newp->priorityPragma(true); } // - | finc_or_dec_expression ';' { $$ = $1; } // // IEEE: inc_or_dec_expression - // // Below under expr + | finc_or_dec_expression ';' { $$ = $1; } // // // IEEE: subroutine_call_statement // // IEEE says we then expect a function call @@ -3522,25 +3553,17 @@ statement_item: // IEEE: statement_item FileLine* const newfl = new FileLine{$$->fileline()}; newfl->warnOff(V3ErrorCode::IGNOREDRETURN, true); $$->fileline(newfl); } - // // Expr included here to resolve our not knowing what is a method call - // // Expr here must result in a subroutine_call - | task_subroutine_callNoMethod ';' { $$ = $1->makeStmt(); } - //UNSUP fexpr '.' array_methodNoRoot ';' { UNSUP } - | fexpr '.' task_subroutine_callNoMethod ';' { $$ = (new AstDot{$2, false, $1, $3})->makeStmt(); } - | system_t_call ';' { $$ = $1; } - //UNSUP fexprScope ';' { UNSUP } - // // Not here in IEEE; from class_constructor_declaration - // // Because we've joined class_constructor_declaration into generic functions - // // Way over-permissive; - // // IEEE: [ ySUPER '.' yNEW [ '(' list_of_arguments ')' ] ';' ] - | fexpr '.' class_new ';' { $$ = (new AstDot{$2, false, $1, $3})->makeStmt(); } + // + | task_subroutine_callNoSemi ';' { $$ = $1; } // | statementVerilatorPragmas { $$ = $1; } // // // IEEE: disable_statement | yDISABLE yFORK ';' { $$ = new AstDisableFork{$1}; } - | yDISABLE idAny/*hierarchical_identifier-task_or_block*/ ';' + | yDISABLE idAny/*UNSUP: hierarchical_identifier-task_or_block*/ ';' { $$ = new AstDisable{$1, *$2}; } + | yDISABLE idAny '.' idDotted ';' + { $$ = nullptr; BBUNSUP($4, "Unsupported: disable with '.'"); } // // IEEE: event_trigger | yP_MINUSGT idDotted/*hierarchical_identifier-event*/ ';' { $$ = new AstFireEvent{$1, $2, false}; } @@ -3590,7 +3613,13 @@ statement_item: // IEEE: statement_item // // IEEE: wait_statement | yWAIT '(' expr ')' stmtBlock { $$ = new AstWait{$1, $3, $5}; } | yWAIT yFORK ';' { $$ = new AstWaitFork{$1}; } - //UNSUP yWAIT_ORDER '(' hierarchical_identifierList ')' action_block { UNSUP } + // // action_block expanded here + | yWAIT_ORDER '(' vrdList ')' stmt %prec prLOWER_THAN_ELSE + { $$ = nullptr; BBUNSUP($4, "Unsupported: wait_order"); } + | yWAIT_ORDER '(' vrdList ')' stmt yELSE stmt + { $$ = nullptr; BBUNSUP($4, "Unsupported: wait_order"); } + | yWAIT_ORDER '(' vrdList ')' yELSE stmt + { $$ = nullptr; BBUNSUP($4, "Unsupported: wait_order"); } // // // IEEE: procedural_assertion_statement | procedural_assertion_statement { $$ = $1; } @@ -3600,7 +3629,14 @@ statement_item: // IEEE: statement_item // // IEEE: randcase_statement | yRANDCASE rand_case_itemList yENDCASE { $$ = new AstRandCase{$1, $2}; } // - //UNSUP expect_property_statement { $$ = $1; } + // // IEEE: expect_property_statement + // // action_block expanded here + | yEXPECT '(' property_spec ')' stmt %prec prLOWER_THAN_ELSE + { $$ = nullptr; BBUNSUP($1, "Unsupported: expect"); } + | yEXPECT '(' property_spec ')' stmt yELSE stmt + { $$ = nullptr; BBUNSUP($1, "Unsupported: expect"); } + | yEXPECT '(' property_spec ')' yELSE stmt + { $$ = nullptr; BBUNSUP($1, "Unsupported: expect"); } // | error ';' { $$ = nullptr; } ; @@ -3619,27 +3655,9 @@ statementVerilatorPragmas: { $$ = new AstPragma{$1, VPragmaType::COVERAGE_BLOCK_OFF}; } ; -//UNSUPoperator_assignment: // IEEE: operator_assignment -//UNSUP ~f~exprLvalue '=' delay_or_event_controlE expr { } -//UNSUP | ~f~exprLvalue yP_PLUSEQ expr { } -//UNSUP | ~f~exprLvalue yP_MINUSEQ expr { } -//UNSUP | ~f~exprLvalue yP_TIMESEQ expr { } -//UNSUP | ~f~exprLvalue yP_DIVEQ expr { } -//UNSUP | ~f~exprLvalue yP_MODEQ expr { } -//UNSUP | ~f~exprLvalue yP_ANDEQ expr { } -//UNSUP | ~f~exprLvalue yP_OREQ expr { } -//UNSUP | ~f~exprLvalue yP_XOREQ expr { } -//UNSUP | ~f~exprLvalue yP_SLEFTEQ expr { } -//UNSUP | ~f~exprLvalue yP_SRIGHTEQ expr { } -//UNSUP | ~f~exprLvalue yP_SSRIGHTEQ expr { } -//UNSUP ; - foperator_assignment: // IEEE: operator_assignment (for first part of expression) fexprLvalue '=' delay_or_event_controlE expr { $$ = new AstAssign{$2, $1, $4, $3}; } - | fexprLvalue '=' yD_FOPEN '(' expr ')' { $$ = new AstFOpenMcd{$3, $1, $5}; } - | fexprLvalue '=' yD_FOPEN '(' expr ',' expr ')' { $$ = new AstFOpen{$3, $1, $5, $7}; } // - //UNSUP ~f~exprLvalue yP_PLUS(etc) expr { UNSUP } | fexprLvalue yP_PLUSEQ expr { $$ = new AstAssign{$2, $1, new AstAdd{$2, $1->cloneTree(true), $3}}; } | fexprLvalue yP_MINUSEQ expr @@ -3662,8 +3680,6 @@ foperator_assignment: // IEEE: operator_assignment (for first part of { $$ = new AstAssign{$2, $1, new AstShiftR{$2, $1->cloneTree(true), $3}}; } | fexprLvalue yP_SSRIGHTEQ expr { $$ = new AstAssign{$2, $1, new AstShiftRS{$2, $1->cloneTree(true), $3}}; } - //UNSUP replace above with: - //UNSUP BISONPRE_COPY(operator_assignment,{s/~f~/f/g}) // {copied} ; inc_or_dec_expression: // ==IEEE: inc_or_dec_expression @@ -3687,19 +3703,19 @@ finc_or_dec_expression: // ==IEEE: inc_or_dec_expression BISONPRE_COPY(inc_or_dec_expression,{s/~l~/f/g}) // {copied} ; -//UNSUPsinc_or_dec_expression: // IEEE: inc_or_dec_expression (for sequence_expression) -//UNSUP BISONPRE_COPY(inc_or_dec_expression,{s/~l~/s/g}) // {copied} -//UNSUP ; +sinc_or_dec_expression: // IEEE: inc_or_dec_expression (for sequence_expression) + BISONPRE_COPY(inc_or_dec_expression,{s/~l~/s/g}) // {copied} + ; -//UNSUPpinc_or_dec_expression: // IEEE: inc_or_dec_expression (for property_expression) -//UNSUP BISONPRE_COPY(inc_or_dec_expression,{s/~l~/p/g}) // {copied} -//UNSUP ; +pinc_or_dec_expression: // IEEE: inc_or_dec_expression (for property_expression) + BISONPRE_COPY(inc_or_dec_expression,{s/~l~/p/g}) // {copied} + ; -//UNSUPev_inc_or_dec_expression: // IEEE: inc_or_dec_expression (for ev_expr) +//UNSUPev_inc_or_dec_expression: // IEEE: inc_or_dec_expression (for ev_expr) //UNSUP BISONPRE_COPY(inc_or_dec_expression,{s/~l~/ev_/g}) // {copied} //UNSUP ; -//UNSUPpev_inc_or_dec_expression: // IEEE: inc_or_dec_expression (for pev_expr) +//UNSUPpev_inc_or_dec_expression: // IEEE: inc_or_dec_expression (for pev_expr) //UNSUP BISONPRE_COPY(inc_or_dec_expression,{s/~l~/pev_/g}) // {copied} //UNSUP ; @@ -3740,10 +3756,9 @@ caseAttrE: | caseAttrE yVL_PARALLEL_CASE { GRAMMARP->m_caseAttrp->parallelPragma(true); } ; -//UNSUPcase_patternListE: // IEEE: case_pattern_item -//UNSUP // &&& is part of expr so aliases to case_itemList -//UNSUP case_itemListE { $$ = $1; } -//UNSUP ; +case_patternListE: // IEEE: case_pattern_item + case_itemListE { $$ = $1; } + ; case_itemListE: // IEEE: [ { case_item } ] /* empty */ { $$ = nullptr; } @@ -3793,10 +3808,11 @@ value_range: // ==IEEE: value_range | '[' expr ':' expr ']' { $$ = new AstInsideRange{$1, $2, $4}; } ; -//UNSUPcovergroup_value_range: // ==IEEE-2012: covergroup_value_range -//UNSUP cgexpr { $$ = $1; } -//UNSUP | '[' cgexpr ':' cgexpr ']' { } -//UNSUP ; +covergroup_value_range: // ==IEEE-2012: covergroup_value_range + cgexpr { $$ = $1; } + | '[' cgexpr ':' cgexpr ']' + { $$ = nullptr; BBUNSUP($1, "Unsupported: covergroup value range"); } + ; caseCondList: // IEEE: part of case_item exprTypeCompare { $$ = $1; } @@ -3884,8 +3900,7 @@ for_initialization: // ==IEEE: for_initialization + for_variable_dec for_initializationItemList: // IEEE: [for_variable_declaration...] for_initializationItem { $$ = $1; } - | for_initializationItemList ',' for_initializationItem - { $$ = $1; BBUNSUP($2, "Unsupported: for loop initialization after the first comma"); } + | for_initializationItemList ',' for_initializationItem { $$ = addNextNull($1, $3); } ; for_initializationItem: // IEEE: variable_assignment + for_variable_declaration @@ -3918,18 +3933,11 @@ for_step: // IEEE: for_step | for_step ',' for_step_assignment { $$ = addNextNull($1, $3); } ; -for_step_assignment: // ==IEEE: for_step_assignment - //UNSUP operator_assignment { $$ = $1; } - // - //UNSUP inc_or_dec_expression { $$ = $1; } - // // IEEE: subroutine_call - //UNSUP function_subroutine_callNoMethod { $$ = $1; } - // // method_call:array_method requires a '.' - //UNSUP expr '.' array_methodNoRoot { } - //UNSUP exprScope { $$ = $1; } - //UNSUP remove below - genvar_iteration { $$ = $1; } - //UNSUP remove above +for_step_assignment: // ==IEEE: for_step_assignment + foperator_assignment { $$ = $1; } + | finc_or_dec_expression { $$ = $1; } + // // IEEE: function_subroutine_call + | task_subroutine_callNoSemi { $$ = $1; } ; loop_variables: // IEEE: loop_variables @@ -3966,6 +3974,19 @@ funcRef: // IEEE: part of tf_call //UNSUP: idDotted is really just id to allow dotted method calls ; +task_subroutine_callNoSemi: // similar to IEEE task_subroutine_call but without ';' + // // Expr included here to resolve our not knowing what is a method call + // // Expr here must result in a subroutine_call + task_subroutine_callNoMethod { $$ = $1->makeStmt(); } + | fexpr '.' task_subroutine_callNoMethod { $$ = (new AstDot{$2, false, $1, $3})->makeStmt(); } + | system_t_call { $$ = $1; } + // // Not here in IEEE; from class_constructor_declaration + // // Because we've joined class_constructor_declaration into generic functions + // // Way over-permissive; + // // IEEE: [ ySUPER '.' yNEW [ '(' list_of_arguments ')' ] ';' ] + | fexpr '.' class_new { $$ = (new AstDot{$2, false, $1, $3})->makeStmt(); } + ; + task_subroutine_callNoMethod: // function_subroutine_callNoMethod (as task) // // IEEE: tf_call taskRef { $$ = $1; } @@ -4203,6 +4224,8 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task | yD_FERROR '(' expr ',' idClassSel ')' { $$ = new AstFError{$1, $3, $5}; } | yD_FGETC '(' expr ')' { $$ = new AstFGetC{$1, $3}; } | yD_FGETS '(' expr ',' expr ')' { $$ = new AstFGetS{$1, $3, $5}; } + | yD_FOPEN '(' expr ')' { $$ = new AstFOpenMcd{$1, $3}; } + | yD_FOPEN '(' expr ',' expr ')' { $$ = new AstFOpen{$1, $3, $5}; } | yD_FREAD '(' expr ',' expr ')' { $$ = new AstFRead{$1, $3, $5, nullptr, nullptr}; } | yD_FREAD '(' expr ',' expr ',' expr ')' { $$ = new AstFRead{$1, $3, $5, $7, nullptr}; } | yD_FREAD '(' expr ',' expr ',' expr ',' expr ')' { $$ = new AstFRead{$1, $3, $5, $7, $9}; } @@ -4300,7 +4323,7 @@ elaboration_system_task_guts: // IEEE: part of elaboration_system_task | yD_FATAL '(' expr ',' exprListE ')' { $$ = new AstElabDisplay{$1, VDisplayType::DT_FATAL, $5}; DEL($3); } ; -//UNSUPproperty_actual_arg: // ==IEEE: property_actual_arg +//UNSUPproperty_actual_arg: // ==IEEE: property_actual_arg //UNSUP // // IEEE: property_expr //UNSUP // // IEEE: sequence_actual_arg //UNSUP pev_expr { $$ = $1; } @@ -4849,8 +4872,10 @@ expr: // IEEE: part of expression/constant_expression/ // // IEEE: cond_pattern - here to avoid reduce problems // // "expr yMATCHES pattern" // // IEEE: pattern - expanded here to avoid conflicts - //UNSUP ~l~expr yMATCHES patternNoExpr { UNSUP } - //UNSUP ~l~expr yMATCHES ~r~expr { UNSUP } + | ~l~expr yMATCHES patternNoExpr { $$ = new AstConst{$2, AstConst::BitFalse{}}; + BBUNSUP($2, "Unsupported: matches operator"); } + | ~l~expr yMATCHES ~r~expr { $$ = new AstConst{$2, AstConst::BitFalse{}}; + BBUNSUP($2, "Unsupported: matches operator"); } // // // IEEE: expression_or_dist - here to avoid reduce problems // // "expr yDIST '{' dist_list '}'" @@ -4861,7 +4886,7 @@ fexpr: // For use as first part of statement (disam BISONPRE_COPY(expr,{s/~l~/f/g; s/~r~/f/g; s/~f__IGNORE~/__IGNORE/g;}) // {copied} ; -//UNSUPev_expr: // IEEE: event_expression +//UNSUPev_expr: // IEEE: event_expression //UNSUP // // for yOR/, see event_expression //UNSUP // //UNSUP // // IEEE: [ edge_identifier ] expression [ yIFF expression ] @@ -4879,7 +4904,7 @@ fexpr: // For use as first part of statement (disam //UNSUP // //UNSUP //--------------------- //UNSUP // // IEEE: expr -//UNSUP | BISONPRE_COPY(expr,{s/~l~/ev_/g; s/~r~/ev_/g; s/~p~/ev_/g; s/~noPar__IGNORE~/yP_PAR__IGNORE /g;}) // {copied} +//UNSUP | BISONPRE_COPY(expr,{s/~l~/ev_/g; s/~r~/ev_/g; s/~p~/ev_/g; s/~noPar__IGNORE~'.'/yP_PAR__IGNORE /g;}) // {copied} //UNSUP // //UNSUP // // IEEE: '(' event_expression ')' //UNSUP // // expr:'(' x ')' conflicts with event_expression:'(' event_expression ')' @@ -4920,19 +4945,19 @@ fexprOkLvalue: // exprOkLValue, For use as first part of st BISONPRE_COPY(exprOkLvalue,{s/~l~/f/g}) // {copied} ; -//UNSUPsexprOkLvalue: // exprOkLValue, For use by sequence_expr -//UNSUP BISONPRE_COPY(exprOkLvalue,{s/~l~/s/g}) // {copied} -//UNSUP ; +sexprOkLvalue: // exprOkLValue, For use by sequence_expr + BISONPRE_COPY(exprOkLvalue,{s/~l~/s/g}) // {copied} + ; -//UNSUPpexprOkLvalue: // exprOkLValue, For use by property_expr -//UNSUP BISONPRE_COPY(exprOkLvalue,{s/~l~/p/g}) // {copied} -//UNSUP ; +pexprOkLvalue: // exprOkLValue, For use by property_expr + BISONPRE_COPY(exprOkLvalue,{s/~l~/p/g}) // {copied} + ; -//UNSUPev_exprOkLvalue: // exprOkLValue, For use by ev_expr +//UNSUPev_exprOkLvalue: // exprOkLValue, For use by ev_expr //UNSUP BISONPRE_COPY(exprOkLvalue,{s/~l~/ev_/g}) // {copied} //UNSUP ; -//UNSUPpev_exprOkLvalue: // exprOkLValue, For use by ev_expr +//UNSUPpev_exprOkLvalue: // exprOkLValue, For use by ev_expr //UNSUP BISONPRE_COPY(exprOkLvalue,{s/~l~/pev_/g}) // {copied} //UNSUP ; @@ -4963,19 +4988,19 @@ fexprScope: // exprScope, For use as first part of state BISONPRE_COPY(exprScope,{s/~l~/f/g}) // {copied} ; -//UNSUPsexprScope: // exprScope, For use by sequence_expr -//UNSUP BISONPRE_COPY(exprScope,{s/~l~/s/g}) // {copied} -//UNSUP ; +sexprScope: // exprScope, For use by sequence_expr + BISONPRE_COPY(exprScope,{s/~l~/s/g}) // {copied} + ; -//UNSUPpexprScope: // exprScope, For use by property_expr -//UNSUP BISONPRE_COPY(exprScope,{s/~l~/p/g}) // {copied} -//UNSUP ; +pexprScope: // exprScope, For use by property_expr + BISONPRE_COPY(exprScope,{s/~l~/p/g}) // {copied} + ; -//UNSUPev_exprScope: // exprScope, For use by ev_expr +//UNSUPev_exprScope: // exprScope, For use by ev_expr //UNSUP BISONPRE_COPY(exprScope,{s/~l~/ev_/g}) // {copied} //UNSUP ; -//UNSUPpev_exprScope: // exprScope, For use by ev_expr +//UNSUPpev_exprScope: // exprScope, For use by ev_expr //UNSUP BISONPRE_COPY(exprScope,{s/~l~/pev_/g}) // {copied} //UNSUP ; @@ -5040,7 +5065,7 @@ argsExprListE: // IEEE: part of list_of_arguments | argsExprListE ',' argsExprOneE { $$ = $1->addNext($3); } ; -//UNSUPpev_argsExprListE: // IEEE: part of list_of_arguments - pev_expr at bottom +//UNSUPpev_argsExprListE: // IEEE: part of list_of_arguments - pev_expr at bottom //UNSUP pev_argsExprOneE { $$ = $1; } //UNSUP | pev_argsExprListE ',' pev_argsExprOneE { $$ = addNextNull($1, $3); } //UNSUP ; @@ -5050,7 +5075,7 @@ argsExprOneE: // IEEE: part of list_of_arguments | expr { $$ = new AstArg{$1->fileline(), "", $1}; } ; -//UNSUPpev_argsExprOneE: // IEEE: part of list_of_arguments - pev_expr at bottom +//UNSUPpev_argsExprOneE: // IEEE: part of list_of_arguments - pev_expr at bottom //UNSUP /*empty*/ { $$ = nullptr; } // ,, is legal in list_of_arguments //UNSUP | pev_expr { $$ = $1; } //UNSUP ; @@ -5060,7 +5085,7 @@ argsDottedList: // IEEE: part of list_of_arguments | argsDottedList ',' argsDotted { $$ = addNextNull($1, $3); } ; -//UNSUPpev_argsDottedList: // IEEE: part of list_of_arguments - pev_expr at bottom +//UNSUPpev_argsDottedList: // IEEE: part of list_of_arguments - pev_expr at bottom //UNSUP pev_argsDotted { $$ = $1; } //UNSUP | pev_argsDottedList ',' pev_argsDotted { $$ = addNextNull($1, $3); } //UNSUP ; @@ -5070,7 +5095,7 @@ argsDotted: // IEEE: part of list_of_arguments | '.' idAny '(' expr ')' { $$ = new AstArg{$2, *$2, $4}; } ; -//UNSUPpev_argsDotted: // IEEE: part of list_of_arguments - pev_expr at bottom +//UNSUPpev_argsDotted: // IEEE: part of list_of_arguments - pev_expr at bottom //UNSUP '.' idAny '(' ')' { $$ = new AstArg{$2, *$2, nullptr}; } //UNSUP | '.' idAny '(' pev_expr ')' { $$ = new AstArg{$2, *$2, $4}; } //UNSUP ; @@ -5127,6 +5152,49 @@ stream_expressionOrDataType: // IEEE: from streaming_concatenation { $$ = $1; BBUNSUP($2, "Unsupported: with[] stream expression"); } ; +//************************************************ +// Let + +letId: // IEEE: pert of let_declaration + idAny/*let_identifieer*/ + { $$ = $1; + $$ = nullptr; + // No unsupported message as caller has one, for now just use a func + $$ = new AstFunc{$$, *$1, nullptr, nullptr}; + SYMP->pushNewUnderNodeOrCurrent($$, $$); } + ; + +let_declaration: // IEEE: let_declaration + yLET letId '=' expr ';' + { $$ = nullptr; + SYMP->popScope($2); + BBUNSUP($1, "Unsupported: let"); } + | yLET letId '(' let_port_listE ')' '=' expr ';' + { $$ = nullptr; + SYMP->popScope($2); + BBUNSUP($1, "Unsupported: let"); } + ; + +let_port_listE: // IEEE: [ let_port_list ] + /*empty*/ { $$ = nullptr; } + | let_port_list { $$ = $1; } + ; + +let_port_list: // IEEE: let_port_list + let_port_item { $$ = $1; } + | let_port_list ',' let_port_item { $$ = addNextNull($1, $3); } + ; + +let_port_item: // IEEE: let_port_Item + // // IEEE: Expanded let_formal_type + yUNTYPED idAny/*formal_port_identifier*/ variable_dimensionListE exprEqE + { $$ = nullptr; BBUNSUP($1, "Unsupported: let untyped ports"); } + | data_type id/*formal_port_identifier*/ variable_dimensionListE exprEqE + { $$ = nullptr; BBUNSUP($1, "Unsupported: let ports"); } + | implicit_typeE id/*formal_port_identifier*/ variable_dimensionListE exprEqE + { $$ = nullptr; BBUNSUP($1, "Unsupported: let ports"); } + ; + //************************************************ // Gate declarations @@ -5458,7 +5526,7 @@ variable_lvalueConcList: // IEEE: part of variable_lvalue: '{' variab | variable_lvalueConcList ',' variable_lvalue { $$ = new AstConcat{$2, $1, $3}; } ; -//UNSUPvariable_lvalueList: // IEEE: part of variable_lvalue: variable_lvalue { ',' variable_lvalue } +//UNSUPvariable_lvalueList: // IEEE: part of variable_lvalue: variable_lvalue { ',' variable_lvalue } //UNSUP variable_lvalue { $$ = $1; } //UNSUP | variable_lvalueList ',' variable_lvalue { $$ = addNextNull($1, $3); } //UNSUP ; @@ -5672,8 +5740,8 @@ cycle_delay: // IEEE: cycle_delay assertion_item_declaration: // ==IEEE: assertion_item_declaration property_declaration { $$ = $1; } -//UNSUP | sequence_declaration { $$ = $1; } -//UNSUP | let_declaration { $$ = $1; } + | sequence_declaration { $$ = $1; } + | let_declaration { $$ = $1; } ; assertion_item: // ==IEEE: assertion_item @@ -5702,7 +5770,7 @@ immediate_assertion_statement: // ==IEEE: immediate_assertion_statement ; simple_immediate_assertion_statement: // ==IEEE: simple_immediate_assertion_statement - // // action_block expanded here, for compatibility with AstAssert + // // action_block expanded here, for compatibility with AstAssert assertOrAssume '(' expr ')' stmtBlock %prec prLOWER_THAN_ELSE { $$ = new AstAssert{$1, $3, $5, nullptr, true}; } | assertOrAssume '(' expr ')' yELSE stmtBlock @@ -5727,27 +5795,16 @@ final_zero: // IEEE: part of deferred_immediate_assertion_st deferred_immediate_assertion_statement: // ==IEEE: deferred_immediate_assertion_statement // // IEEE: deferred_immediate_assert_statement - yASSERT final_zero '(' expr ')' stmtBlock %prec prLOWER_THAN_ELSE + assertOrAssume final_zero '(' expr ')' stmtBlock %prec prLOWER_THAN_ELSE { $$ = new AstAssert{$1, $4, $6, nullptr, true}; } - | yASSERT final_zero '(' expr ')' yELSE stmtBlock + | assertOrAssume final_zero '(' expr ')' yELSE stmtBlock { $$ = new AstAssert{$1, $4, nullptr, $7, true}; } - | yASSERT final_zero '(' expr ')' stmtBlock yELSE stmtBlock - { $$ = new AstAssert{$1, $4, $6, $8, true}; } - // // IEEE: deferred_immediate_assume_statement - | yASSUME final_zero '(' expr ')' stmtBlock %prec prLOWER_THAN_ELSE - { $$ = new AstAssert{$1, $4, $6, nullptr, true}; } - | yASSUME final_zero '(' expr ')' yELSE stmtBlock - { $$ = new AstAssert{$1, $4, nullptr, $7, true}; } - | yASSUME final_zero '(' expr ')' stmtBlock yELSE stmtBlock + | assertOrAssume final_zero '(' expr ')' stmtBlock yELSE stmtBlock { $$ = new AstAssert{$1, $4, $6, $8, true}; } // // IEEE: deferred_immediate_cover_statement | yCOVER final_zero '(' expr ')' stmt { $$ = new AstCover{$1, $4, $6, true}; } ; -//UNSUPexpect_property_statement: // ==IEEE: expect_property_statement -//UNSUP yEXPECT '(' property_spec ')' action_block { } -//UNSUP ; - concurrent_assertion_item: // IEEE: concurrent_assertion_item concurrent_assertion_statement { $$ = $1; } | id/*block_identifier*/ ':' concurrent_assertion_statement @@ -5758,48 +5815,48 @@ concurrent_assertion_item: // IEEE: concurrent_assertion_item concurrent_assertion_statement: // ==IEEE: concurrent_assertion_statement // // IEEE: assert_property_statement - //UNSUP remove below: - yASSERT yPROPERTY '(' property_spec ')' elseStmtBlock - { $$ = new AstAssert{$1, new AstSampled{$1, $4}, nullptr, $6, false}; } - //UNSUP yASSERT yPROPERTY '(' property_spec ')' action_block { } // // IEEE: assume_property_statement - | yASSUME yPROPERTY '(' property_spec ')' elseStmtBlock - { $$ = new AstAssert{$1, new AstSampled{$1, $4}, nullptr, $6, false}; } - //UNSUP yASSUME yPROPERTY '(' property_spec ')' action_block { } + // // action_block expanded here + assertOrAssume yPROPERTY '(' property_spec ')' stmt %prec prLOWER_THAN_ELSE + { $$ = new AstAssert{$1, new AstSampled{$1, $4}, $6, nullptr, false}; } + | assertOrAssume yPROPERTY '(' property_spec ')' stmt yELSE stmt + { $$ = new AstAssert{$1, new AstSampled{$1, $4}, $6, $8, false}; } + | assertOrAssume yPROPERTY '(' property_spec ')' yELSE stmt + { $$ = new AstAssert{$1, new AstSampled{$1, $4}, nullptr, $7, false}; } // // IEEE: cover_property_statement - | yCOVER yPROPERTY '(' property_spec ')' stmtBlock { $$ = new AstCover{$1, $4, $6, false}; } + | yCOVER yPROPERTY '(' property_spec ')' stmtBlock + { $$ = new AstCover{$1, $4, $6, false}; } // // IEEE: cover_sequence_statement - //UNSUP yCOVER ySEQUENCE '(' sexpr ')' stmt { } + | yCOVER ySEQUENCE '(' sexpr ')' stmt + { $$ = nullptr; BBUNSUP($2, "Unsupported: cover sequence"); } // // IEEE: yCOVER ySEQUENCE '(' clocking_event sexpr ')' stmt // // sexpr already includes "clocking_event sexpr" - //UNSUP yCOVER ySEQUENCE '(' clocking_event yDISABLE yIFF '(' expr/*expression_or_dist*/ ')' sexpr ')' stmt { } - //UNSUP yCOVER ySEQUENCE '(' yDISABLE yIFF '(' expr/*expression_or_dist*/ ')' sexpr ')' stmt { } + | yCOVER ySEQUENCE '(' clocking_event yDISABLE yIFF '(' expr/*expression_or_dist*/ ')' sexpr ')' stmt + { $$ = nullptr; BBUNSUP($2, "Unsupported: cover sequence"); } + | yCOVER ySEQUENCE '(' yDISABLE yIFF '(' expr/*expression_or_dist*/ ')' sexpr ')' stmt + { $$ = nullptr; BBUNSUP($2, "Unsupported: cover sequence"); } // // IEEE: restrict_property_statement - | yRESTRICT yPROPERTY '(' property_spec ')' ';' { $$ = new AstRestrict{$1, $4}; } - ; - -elseStmtBlock: // Part of concurrent_assertion_statement - ';' { $$ = nullptr; } - | yELSE stmtBlock { $$ = $2; } + | yRESTRICT yPROPERTY '(' property_spec ')' ';' + { $$ = new AstRestrict{$1, $4}; } ; property_declaration: // ==IEEE: property_declaration property_declarationFront property_port_listE ';' property_declarationBody yENDPROPERTY endLabelE - { $$ = $1; - $$->addStmtsp($2); - $$->addStmtsp($4); - SYMP->popScope($$); - GRAMMARP->endLabel($6, $$, $6); - GRAMMARP->m_insideProperty = false; - GRAMMARP->m_typedPropertyPort = false; } + { $$ = $1; + $$->addStmtsp($2); + $$->addStmtsp($4); + SYMP->popScope($$); + GRAMMARP->endLabel($6, $$, $6); + GRAMMARP->m_insideProperty = false; + GRAMMARP->m_typedPropertyPort = false; } ; property_declarationFront: // IEEE: part of property_declaration yPROPERTY idAny/*property_identifier*/ - { $$ = new AstProperty{$1, *$2, nullptr}; - GRAMMARP->m_insideProperty = true; - SYMP->pushNewUnderNodeOrCurrent($$, nullptr); } + { $$ = new AstProperty{$2, *$2, nullptr}; + GRAMMARP->m_insideProperty = true; + SYMP->pushNewUnderNodeOrCurrent($$, nullptr); } ; property_port_listE: // IEEE: [ ( [ property_port_list ] ) ] @@ -5813,12 +5870,12 @@ property_port_list: // ==IEEE: property_port_list ; property_port_item: // IEEE: property_port_item/sequence_port_item -//UNSUP // // Merged in sequence_port_item -//UNSUP // // IEEE: property_lvar_port_direction ::= yINPUT -//UNSUP // // prop IEEE: [ yLOCAL [ yINPUT ] ] property_formal_type -//UNSUP // // id {variable_dimension} [ '=' property_actual_arg ] -//UNSUP // // seq IEEE: [ yLOCAL [ sequence_lvar_port_direction ] ] sequence_formal_type -//UNSUP // // id {variable_dimension} [ '=' sequence_actual_arg ] + // // Merged in sequence_port_item + // // IEEE: property_lvar_port_direction ::= yINPUT + // // prop IEEE: [ yLOCAL [ yINPUT ] ] property_formal_type + // // id {variable_dimension} [ '=' property_actual_arg ] + // // seq IEEE: [ yLOCAL [ sequence_lvar_port_direction ] ] sequence_formal_type + // // id {variable_dimension} [ '=' sequence_actual_arg ] property_port_itemFront property_port_itemAssignment { $$ = $2; } ; @@ -5835,71 +5892,88 @@ property_port_itemFront: // IEEE: part of property_port_item/sequence_port_item property_port_itemAssignment: // IEEE: part of property_port_item/sequence_port_item/checker_port_direction id variable_dimensionListE { $$ = VARDONEA($1, *$1, $2, nullptr); } -//UNSUP | id variable_dimensionListE '=' property_actual_arg -//UNSUP { VARDONE($1, $1, $2, $4); PINNUMINC(); } + //UNSUP|id variable_dimensionListE '=' property_actual_arg + //UNSUP { VARDONE($1, $1, $2, $4); PINNUMINC(); } ; property_port_itemDirE: - /* empty */ { GRAMMARP->m_pinAnsi = true; VARIO(INPUT); } -//UNSUP | yLOCAL__ETC { GRAMMARP->m_pinAnsi = true; VARIO(INPUT); } -//UNSUP | yLOCAL__ETC yINPUT { GRAMMARP->m_pinAnsi = true; VARIO(INPUT); } + /* empty */ + { GRAMMARP->m_pinAnsi = true; VARIO(INPUT); } + | yLOCAL__ETC + { GRAMMARP->m_pinAnsi = true; VARIO(INPUT); + BBUNSUP($1, "Unsupported: property port 'local'"); } + | yLOCAL__ETC yINPUT + { GRAMMARP->m_pinAnsi = true; VARIO(INPUT); + BBUNSUP($1, "Unsupported: property port 'local'"); } ; property_declarationBody: // IEEE: part of property_declaration -//UNSUP assertion_variable_declarationList property_statement_spec { } -//UNSUP // // IEEE-2012: Incorectly hasyCOVER ySEQUENCE then property_spec here. -//UNSUP // // Fixed in IEEE 1800-2017 + //UNSUP assertion_variable_declarationList property_statement_spec {} + // // IEEE-2012: Incorectly hasyCOVER ySEQUENCE then property_spec here. + // // Fixed in IEEE 1800-2017 property_spec { $$ = $1; } | property_spec ';' { $$ = $1; } ; -//UNSUPassertion_variable_declarationList: // IEEE: part of assertion_variable_declaration -//UNSUP assertion_variable_declaration { $$ = $1; } -//UNSUP | assertion_variable_declarationList assertion_variable_declaration { } -//UNSUP ; +assertion_variable_declarationList: // IEEE: part of assertion_variable_declaration + assertion_variable_declaration { $$ = $1; } + | assertion_variable_declarationList assertion_variable_declaration + { $$ = addNextNull($1, $2); } + ; -//UNSUPsequence_declaration: // ==IEEE: sequence_declaration -//UNSUP sequence_declarationFront sequence_port_listE ';' sequence_declarationBody -//UNSUP yENDSEQUENCE endLabelE -//UNSUP { SYMP->popScope($$); } -//UNSUP ; +sequence_declaration: // ==IEEE: sequence_declaration + sequence_declarationFront sequence_port_listE ';' sequence_declarationBody + /*cont*/ yENDSEQUENCE endLabelE + { $$ = $1; + $$->addStmtsp($2); + $$->addStmtsp($4); + SYMP->popScope($$); + GRAMMARP->endLabel($6, $$, $6); } + ; -//UNSUPsequence_declarationFront: // IEEE: part of sequence_declaration -//UNSUP ySEQUENCE idAny/*new_sequence*/ -//UNSUP { SYMP->pushNew($$); } -//UNSUP ; +sequence_declarationFront: // IEEE: part of sequence_declaration + ySEQUENCE idAny/*new_sequence*/ + { BBUNSUP($1, "Unsupported: sequence"); + $$ = new AstProperty{$2, *$2, nullptr}; + SYMP->pushNewUnderNodeOrCurrent($$, nullptr); } + ; -//UNSUPsequence_port_listE: // IEEE: [ ( [ sequence_port_list ] ) ] -//UNSUP // // IEEE: sequence_lvar_port_direction ::= yINPUT | yINOUT | yOUTPUT -//UNSUP // // IEEE: [ yLOCAL [ sequence_lvar_port_direction ] ] sequence_formal_type -//UNSUP // // id {variable_dimension} [ '=' sequence_actual_arg ] -//UNSUP // // All this is almost identically the same as a property. -//UNSUP // // Difference is only yINOUT/yOUTPUT (which might be added to 1800-2012) -//UNSUP // // and yPROPERTY. So save some work. -//UNSUP property_port_listE { $$ = $1; } -//UNSUP ; +sequence_port_listE: // IEEE: [ ( [ sequence_port_list ] ) ] + // // IEEE: sequence_lvar_port_direction ::= yINPUT | yINOUT | yOUTPUT + // // IEEE: [ yLOCAL [ sequence_lvar_port_direction ] ] sequence_formal_type + // // id {variable_dimension} [ '=' sequence_actual_arg ] + // // All this is almost identically the same as a property. + // // Difference is only yINOUT/yOUTPUT (which might be added to 1800-2012) + // // and yPROPERTY. So save some work. + property_port_listE { $$ = $1; } + ; property_formal_typeNoDt: // IEEE: property_formal_type (w/o implicit) sequence_formal_typeNoDt { $$ = $1; } -//UNSUP | yPROPERTY { } + | yPROPERTY + { $$ = nullptr; GRAMMARP->m_typedPropertyPort = false; + BBUNSUP($1, "Unsupported: property argument data type"); } ; sequence_formal_typeNoDt: // ==IEEE: sequence_formal_type (w/o data_type_or_implicit) -// // IEEE: data_type_or_implicit -// // implicit expanded where used -//UNSUP ySEQUENCE { } -// // IEEE-2009: yEVENT -// // already part of data_type. Removed in 1800-2012. - yUNTYPED { $$ = nullptr; GRAMMARP->m_typedPropertyPort = false; } + // IEEE: data_type_or_implicit + // implicit expanded where used + ySEQUENCE + { $$ = nullptr; GRAMMARP->m_typedPropertyPort = false; + BBUNSUP($1, "Unsupported: sequence argument data type"); } + // IEEE-2009: yEVENT + // already part of data_type. Removed in 1800-2012. + | yUNTYPED + { $$ = nullptr; GRAMMARP->m_typedPropertyPort = false; } ; -//UNSUPsequence_declarationBody: // IEEE: part of sequence_declaration -//UNSUP // // 1800-2012 makes ';' optional -//UNSUP assertion_variable_declarationList sexpr { } -//UNSUP | assertion_variable_declarationList sexpr ';' { } -//UNSUP | sexpr { $$ = $1; } -//UNSUP | sexpr ';' { $$ = $1; } -//UNSUP ; +sequence_declarationBody: // IEEE: part of sequence_declaration + // // 1800-2012 makes ';' optional + assertion_variable_declarationList sexpr { $$ = addNextNull($1, $2); } + | assertion_variable_declarationList sexpr ';' { $$ = addNextNull($1, $2); } + | sexpr { $$ = $1; } + | sexpr ';' { $$ = $1; } + ; property_spec: // IEEE: property_spec //UNSUP: This rule has been super-specialized to what is supported now @@ -5932,27 +6006,33 @@ property_spec: // IEEE: property_spec //UNSUP | property_statementCaseIf { $$ = $1; } //UNSUP ; -//UNSUPproperty_statementCaseIf: // IEEE: property_statement - minus pexpr -//UNSUP yCASE '(' expr/*expression_or_dist*/ ')' property_case_itemList yENDCASE { } -//UNSUP | yCASE '(' expr/*expression_or_dist*/ ')' yENDCASE { } -//UNSUP | yIF '(' expr/*expression_or_dist*/ ')' pexpr %prec prLOWER_THAN_ELSE { } -//UNSUP | yIF '(' expr/*expression_or_dist*/ ')' pexpr yELSE pexpr { } -//UNSUP ; +property_statementCaseIf: // IEEE: property_statement - minus pexpr + yCASE '(' expr/*expression_or_dist*/ ')' property_case_itemList yENDCASE + { $$ = new AstConst{$1, AstConst::BitFalse{}}; + BBUNSUP($1, "Unsupported: property case expression"); } + | yCASE '(' expr/*expression_or_dist*/ ')' yENDCASE + { $$ = new AstConst{$1, AstConst::BitFalse{}}; + BBUNSUP($1, "Unsupported: property case expression"); } + | yIF '(' expr/*expression_or_dist*/ ')' pexpr %prec prLOWER_THAN_ELSE + { $$ = $5; BBUNSUP($1, "Unsupported: property case expression"); } + | yIF '(' expr/*expression_or_dist*/ ')' pexpr yELSE pexpr + { $$ = $5; BBUNSUP($1, "Unsupported: property case expression"); } + ; -//UNSUPproperty_case_itemList: // IEEE: {property_case_item} -//UNSUP property_case_item { $$ = $1; } -//UNSUP | property_case_itemList ',' property_case_item { $$ = addNextNull($1, $3); } -//UNSUP ; +property_case_itemList: // IEEE: {property_case_item} + property_case_item { $$ = $1; } + | property_case_itemList ',' property_case_item { $$ = addNextNull($1, $3); } + ; -//UNSUPproperty_case_item: // ==IEEE: property_case_item -//UNSUP // // IEEE: expression_or_dist { ',' expression_or_dist } ':' property_statement -//UNSUP // // IEEE 1800-2012 changed from property_statement to property_expr -//UNSUP // // IEEE 1800-2017 changed to require the semicolon -//UNSUP caseCondList ':' pexpr { } -//UNSUP | caseCondList ':' pexpr ';' { } -//UNSUP | yDEFAULT pexpr { } -//UNSUP | yDEFAULT ':' pexpr ';' { } -//UNSUP ; +property_case_item: // ==IEEE: property_case_item + // // IEEE: expression_or_dist { ',' expression_or_dist } ':' property_statement + // // IEEE 1800-2012 changed from property_statement to property_expr + // // IEEE 1800-2017 changed to require the semicolon + caseCondList ':' pexpr { $$ = new AstCaseItem{$2, $1, $3}; } + | caseCondList ':' pexpr ';' { $$ = new AstCaseItem{$2, $1, $3}; } + | yDEFAULT pexpr { $$ = new AstCaseItem{$1, nullptr, $2}; } + | yDEFAULT ':' pexpr ';' { $$ = new AstCaseItem{$1, nullptr, $3}; } + ; //UNSUPpev_expr: // IEEE: property_actual_arg | expr //UNSUP // // which expands to pexpr | event_expression @@ -5977,29 +6057,10 @@ property_spec: // IEEE: property_spec //UNSUP | BISONPRE_COPY_ONCE(sexpr,{s/~p~s/pev_/g; }) // {copied} //UNSUP // //UNSUP //============= expr rules copied for pev_expr -//UNSUP | BISONPRE_COPY_ONCE(expr,{s/~l~/pev_/g; s/~p~/pev_/g; s/~noPar__IGNORE~/yP_PAR__IGNORE /g; }) // {copied} +//UNSUP | BISONPRE_COPY_ONCE(expr,{s/~l~/pev_/g; s/~p~/pev_/g; s/~noPar__IGNORE~'.'/yP_PAR__IGNORE /g; }) // {copied} //UNSUP ; pexpr: // IEEE: property_expr (The name pexpr is important as regexps just add an "p" to expr.) - //UNSUP: This rule has been super-specialized to what is supported now - //UNSUP remove below - // - // This rule is divided between expr and complex_pexpr to avoid an ambiguity in SystemVerilog grammar. - // Both pexpr and expr can consist of parentheses, and so a pexpr "((expr))" can be reduced in two ways: - // 1. ((expr)) -> (pexpr) -> pexpr - // 2. ((expr)) -> (expr) -> pexpr - // The division below forces YACC to always assume the parentheses reduce to expr, unless they enclose - // operators that can only appear in a pexpr. - // - complex_pexpr { $$ = $1; } - | expr { $$ = $1; } - ; - -complex_pexpr: // IEEE: part of property_expr, see comments there - expr yP_ORMINUSGT pexpr { $$ = new AstLogOr{$2, new AstLogNot{$2, $1}, $3}; } - | expr yP_OREQGT pexpr { $$ = new AstImplication{$2, $1, $3}; } - | '(' complex_pexpr ')' { $$ = $2; } - //UNSUP remove above, use below: // // // IEEE: sequence_expr // // Expanded below @@ -6007,45 +6068,71 @@ complex_pexpr: // IEEE: part of property_expr, see comments there // // IEEE: '(' pexpr ')' // // Expanded below // - | yNOT pexpr %prec prNEGATION { $$ = new AstLogNot{$1, $2}; } - //UNSUP ySTRONG '(' sexpr ')' { } - //UNSUP yWEAK '(' sexpr ')' { } + yNOT pexpr %prec prNEGATION { $$ = new AstLogNot{$1, $2}; } + | ySTRONG '(' sexpr ')' + { $$ = $3; BBUNSUP($2, "Unsupported: strong (in property expression)"); } + | yWEAK '(' sexpr ')' + { $$ = $3; BBUNSUP($2, "Unsupported: weak (in property expression)"); } // // IEEE: pexpr yOR pexpr // // IEEE: pexpr yAND pexpr // // Under ~p~sexpr and/or ~p~sexpr // // // IEEE: "sequence_expr yP_ORMINUSGT pexpr" // // Instead we use pexpr to prevent conflicts - //UNSUP ~o~pexpr yP_ORMINUSGT pexpr { } - //UNSUP ~o~pexpr yP_OREQGT pexpr { } + | ~o~pexpr yP_ORMINUSGT pexpr { $$ = new AstLogOr{$2, new AstLogNot{$2, $1}, $3}; } + | ~o~pexpr yP_OREQGT pexpr { $$ = new AstImplication{$2, $1, $3}; } // // // IEEE-2009: property_statement // // IEEE-2012: yIF and yCASE - //UNSUP property_statementCaseIf { } + | property_statementCaseIf { $$ = $1; } // - //UNSUP ~o~pexpr/*sexpr*/ yP_POUNDMINUSPD pexpr { } - //UNSUP ~o~pexpr/*sexpr*/ yP_POUNDEQPD pexpr { } - //UNSUP yNEXTTIME pexpr { } - //UNSUP yS_NEXTTIME pexpr { } - //UNSUP yNEXTTIME '[' expr/*const*/ ']' pexpr %prec yNEXTTIME { } - //UNSUP yS_NEXTTIME '[' expr/*const*/ ']' pexpr %prec yS_NEXTTIME { } - //UNSUP yALWAYS pexpr { } - //UNSUP yALWAYS '[' cycle_delay_const_range_expression ']' pexpr %prec yALWAYS { } - //UNSUP yS_ALWAYS '[' constant_range ']' pexpr %prec yS_ALWAYS { } - //UNSUP yS_EVENTUALLY pexpr { } - //UNSUP yEVENTUALLY '[' constant_range ']' pexpr %prec yEVENTUALLY { } - //UNSUP yS_EVENTUALLY '[' cycle_delay_const_range_expression ']' pexpr %prec yS_EVENTUALLY { } - //UNSUP ~o~pexpr yUNTIL pexpr { } - //UNSUP ~o~pexpr yS_UNTIL pexpr { } - //UNSUP ~o~pexpr yUNTIL_WITH pexpr { } - //UNSUP ~o~pexpr yS_UNTIL_WITH pexpr { } - //UNSUP ~o~pexpr yIMPLIES pexpr { } + | ~o~pexpr/*sexpr*/ yP_POUNDMINUSPD pexpr + { $$ = $3; BBUNSUP($2, "Unsupported: #-# (in property expression)"); } + | ~o~pexpr/*sexpr*/ yP_POUNDEQPD pexpr + { $$ = $3; BBUNSUP($2, "Unsupported: #=# (in property expression)"); } + | yNEXTTIME pexpr + { $$ = $2; BBUNSUP($1, "Unsupported: nexttime (in property expression)"); } + | yS_NEXTTIME pexpr + { $$ = $2; BBUNSUP($1, "Unsupported: s_nexttime (in property expression)"); } + | yNEXTTIME '[' constExpr ']' pexpr %prec yNEXTTIME + { $$ = $5; BBUNSUP($1, "Unsupported: nexttime[] (in property expression)"); } + | yS_NEXTTIME '[' constExpr ']' pexpr %prec yS_NEXTTIME + { $$ = $5; BBUNSUP($1, "Unsupported: s_nexttime[] (in property expression)"); } + | yALWAYS pexpr + { $$ = $2; BBUNSUP($1, "Unsupported: always (in property expression)"); } + | yALWAYS anyrange pexpr %prec yALWAYS + { $$ = $3; BBUNSUP($1, "Unsupported: always[] (in property expression)"); } + | yS_ALWAYS anyrange pexpr %prec yS_ALWAYS + { $$ = $3; BBUNSUP($1, "Unsupported: s_always (in property expression)"); } + | yEVENTUALLY pexpr + { $$ = $2; BBUNSUP($1, "Unsupported: eventually (in property expression)"); } + | yS_EVENTUALLY pexpr + { $$ = $2; BBUNSUP($1, "Unsupported: s_eventually (in property expression)"); } + | yEVENTUALLY '[' constExpr ']' pexpr %prec yEVENTUALLY + { $$ = $5; BBUNSUP($1, "Unsupported: eventually[] (in property expression)"); } + | yS_EVENTUALLY anyrange pexpr %prec yS_EVENTUALLY + { $$ = $3; BBUNSUP($1, "Unsupported: s_eventually[] (in property expression)"); } + | ~o~pexpr yUNTIL pexpr + { $$ = $1; BBUNSUP($2, "Unsupported: until (in property expression)"); } + | ~o~pexpr yS_UNTIL pexpr + { $$ = $1; BBUNSUP($2, "Unsupported: s_until (in property expression)"); } + | ~o~pexpr yUNTIL_WITH pexpr + { $$ = $1; BBUNSUP($2, "Unsupported: until_with (in property expression)"); } + | ~o~pexpr yS_UNTIL_WITH pexpr + { $$ = $1; BBUNSUP($2, "Unsupported: s_until_with (in property expression)"); } + | ~o~pexpr yIMPLIES pexpr + { $$ = $1; BBUNSUP($2, "Unsupported: implies (in property expression)"); } // // yIFF also used by event_expression - //UNSUP ~o~pexpr yIFF ~o~pexpr { } - //UNSUP yACCEPT_ON '(' expr/*expression_or_dist*/ ')' pexpr %prec yACCEPT_ON { } - //UNSUP yREJECT_ON '(' expr/*expression_or_dist*/ ')' pexpr %prec yREJECT_ON { } - //UNSUP ySYNC_ACCEPT_ON '(' expr/*expression_or_dist*/ ')' pexpr %prec ySYNC_ACCEPT_ON { } - //UNSUP ySYNC_REJECT_ON '(' expr/*expression_or_dist*/ ')' pexpr %prec ySYNC_REJECT_ON { } + | ~o~pexpr yIFF pexpr + { $$ = $1; BBUNSUP($2, "Unsupported: iff (in property expression)"); } + | yACCEPT_ON '(' expr/*expression_or_dist*/ ')' pexpr %prec yACCEPT_ON + { $$ = $5; BBUNSUP($2, "Unsupported: accept_on (in property expression)"); } + | yREJECT_ON '(' expr/*expression_or_dist*/ ')' pexpr %prec yREJECT_ON + { $$ = $5; BBUNSUP($2, "Unsupported: reject_on (in property expression)"); } + | ySYNC_ACCEPT_ON '(' expr/*expression_or_dist*/ ')' pexpr %prec ySYNC_ACCEPT_ON + { $$ = $5; BBUNSUP($2, "Unsupported: sync_accept_on (in property expression)"); } + | ySYNC_REJECT_ON '(' expr/*expression_or_dist*/ ')' pexpr %prec ySYNC_REJECT_ON + { $$ = $5; BBUNSUP($2, "Unsupported: sync_reject_on (in property expression)"); } // // // IEEE: "property_instance" // // Looks just like a function/method call @@ -6057,111 +6144,134 @@ complex_pexpr: // IEEE: part of property_expr, see comments there //UNSUP clocking_event yDISABLE yIFF '(' expr ')' pexpr %prec prSEQ_CLOCKING { } // //============= sexpr rules copied for property_expr - //UNSUP BISONPRE_COPY_ONCE(sexpr,{s/~p~s/p/g; }) // {copied} + | BISONPRE_COPY_ONCE(sexpr,{s/~p~s/p/g; }) // {copied} // //============= expr rules copied for property_expr - //UNSUP BISONPRE_COPY_ONCE(expr,{s/~l~/p/g; s/~p~/p/g; s/~noPar__IGNORE~/yP_PAR__IGNORE /g; }) // {copied} + | BISONPRE_COPY_ONCE(expr,{s/~l~/p/g; s/~p~/p/g; s/~noPar__IGNORE~'.'/yP_PAR__IGNORE /g; }) // {copied} ; -//UNSUPsexpr: // ==IEEE: sequence_expr (The name sexpr is important as regexps just add an "s" to expr.) -//UNSUP // // ********* RULES COPIED IN sequence_exprProp -//UNSUP // // For precedence, see IEEE 17.7.1 -//UNSUP // -//UNSUP // // IEEE: "cycle_delay_range sequence_expr { cycle_delay_range sequence_expr }" -//UNSUP // // IEEE: "sequence_expr cycle_delay_range sequence_expr { cycle_delay_range sequence_expr }" -//UNSUP // // Both rules basically mean we can repeat sequences, so make it simpler: -//UNSUP cycle_delay_range sexpr %prec yP_POUNDPOUND { } -//UNSUP | ~p~sexpr cycle_delay_range sexpr %prec prPOUNDPOUND_MULTI { } -//UNSUP // -//UNSUP // // IEEE: expression_or_dist [ boolean_abbrev ] -//UNSUP // // Note expression_or_dist includes "expr"! -//UNSUP // // sexpr/*sexpression_or_dist*/ --- Hardcoded below -//UNSUP | ~p~sexpr/*sexpression_or_dist*/ boolean_abbrev { } -//UNSUP // -//UNSUP // // IEEE: "sequence_instance [ sequence_abbrev ]" -//UNSUP // // version without sequence_abbrev looks just like normal function call -//UNSUP // // version w/sequence_abbrev matches above; -//UNSUP // // expression_or_dist:expr:func boolean_abbrev:sequence_abbrev -//UNSUP // -//UNSUP // // IEEE: '(' expression_or_dist {',' sequence_match_item } ')' [ boolean_abbrev ] -//UNSUP // // IEEE: '(' sexpr {',' sequence_match_item } ')' [ sequence_abbrev ] -//UNSUP // // As sequence_expr includes expression_or_dist, and boolean_abbrev includes sequence_abbrev: -//UNSUP // // '(' sequence_expr {',' sequence_match_item } ')' [ boolean_abbrev ] -//UNSUP // // "'(' sexpr ')' boolean_abbrev" matches "[sexpr:'(' expr ')'] boolean_abbrev" so we can drop it -//UNSUP | '(' ~p~sexpr ')' { $$ = $1; $$ = ...; } -//UNSUP | '(' ~p~sexpr ',' sequence_match_itemList ')' { } -//UNSUP // -//UNSUP // // AND/OR are between pexprs OR sexprs -//UNSUP | ~p~sexpr yAND ~p~sexpr { $$ = $1; $$ = ...; } -//UNSUP | ~p~sexpr yOR ~p~sexpr { $$ = $1; $$ = ...; } -//UNSUP // // Intersect always has an sexpr rhs -//UNSUP | ~p~sexpr yINTERSECT sexpr { $$ = $1; $$ = ...; } -//UNSUP // -//UNSUP | yFIRST_MATCH '(' sexpr ')' { } -//UNSUP | yFIRST_MATCH '(' sexpr ',' sequence_match_itemList ')' { } -//UNSUP | ~p~sexpr/*sexpression_or_dist*/ yTHROUGHOUT sexpr { } -//UNSUP // // Below pexpr's are really sequence_expr, but avoid conflict -//UNSUP // // IEEE: sexpr yWITHIN sexpr -//UNSUP | ~p~sexpr yWITHIN sexpr { $$ = $1; $$ = ...; } -//UNSUP // // Note concurrent_assertion had duplicate rule for below -//UNSUP | clocking_event ~p~sexpr %prec prSEQ_CLOCKING { } -//UNSUP // -//UNSUP //============= expr rules copied for sequence_expr -//UNSUP | BISONPRE_COPY_ONCE(expr,{s/~l~/s/g; s/~p~/s/g; s/~noPar__IGNORE~/yP_PAR__IGNORE /g; }) // {copied} -//UNSUP ; +sexpr: // ==IEEE: sequence_expr (The name sexpr is important as regexps just add an "s" to expr.) + // // ********* RULES COPIED IN sequence_exprProp + // // For precedence, see IEEE 17.7.1 + // + // // IEEE: "cycle_delay_range sequence_expr { cycle_delay_range sequence_expr }" + // // IEEE: "sequence_expr cycle_delay_range sequence_expr { cycle_delay_range sequence_expr }" + // // Both rules basically mean we can repeat sequences, so make it simpler: + cycle_delay_range sexpr %prec yP_POUNDPOUND + { $$ = $2; BBUNSUP($2->fileline(), "Unsupported: ## (in sequence expression)"); } + | ~p~sexpr cycle_delay_range sexpr %prec prPOUNDPOUND_MULTI + { $$ = $1; BBUNSUP($2->fileline(), "Unsupported: ## (in sequence expression)"); } + // + // // IEEE: expression_or_dist [ boolean_abbrev ] + // // Note expression_or_dist includes "expr"! + // // sexpr/*sexpression_or_dist*/ --- Hardcoded below + | ~p~sexpr/*sexpression_or_dist*/ boolean_abbrev + { $$ = $1; BBUNSUP($2->fileline(), "Unsupported: boolean abbrev (in sequence expression)"); } + // + // // IEEE: "sequence_instance [ sequence_abbrev ]" + // // version without sequence_abbrev looks just like normal function call + // // version w/sequence_abbrev matches above; + // // expression_or_dist:expr:func boolean_abbrev:sequence_abbrev + // + // // IEEE: '(' expression_or_dist {',' sequence_match_item } ')' [ boolean_abbrev ] + // // IEEE: '(' sexpr {',' sequence_match_item } ')' [ sequence_abbrev ] + // // As sequence_expr includes expression_or_dist, and boolean_abbrev includes sequence_abbrev: + // // '(' sequence_expr {',' sequence_match_item } ')' [ boolean_abbrev ] + // // "'(' sexpr ')' boolean_abbrev" matches "[sexpr:'(' expr ')'] boolean_abbrev" so we can drop it + | '(' ~p~sexpr ')' { $$ = $2; } + //UNSUP '(' ~p~sexpr ',' sequence_match_itemList ')' { } + // + // // AND/OR are between pexprs OR sexprs + | ~p~sexpr yAND ~p~sexpr + { $$ = new AstLogAnd{$2, $1, $3}; + BBUNSUP($2, "Unsupported: and (in sequence expression)"); } + | ~p~sexpr yOR ~p~sexpr + { $$ = new AstLogOr{$2, $1, $3}; + BBUNSUP($2, "Unsupported: or (in sequence expression)"); } + // // Intersect always has an sexpr rhs + | ~p~sexpr yINTERSECT sexpr + { $$ = $1; BBUNSUP($2, "Unsupported: intersect (in sequence expression)"); } + // + | yFIRST_MATCH '(' sexpr ')' + { $$ = nullptr; BBUNSUP($1, "Unsupported: first_match (in sequence expression)"); } + | yFIRST_MATCH '(' sexpr ',' sequence_match_itemList ')' + { $$ = nullptr; BBUNSUP($1, "Unsupported: first_match (in sequence expression)"); } + | ~p~sexpr/*sexpression_or_dist*/ yTHROUGHOUT sexpr + { $$ = $1; BBUNSUP($2, "Unsupported: throughout (in sequence expression)"); } + // // Below pexpr's are really sequence_expr, but avoid conflict + // // IEEE: sexpr yWITHIN sexpr + | ~p~sexpr yWITHIN sexpr + { $$ = $1; BBUNSUP($2, "Unsupported: within (in sequence expression)"); } + // // Note concurrent_assertion had duplicate rule for below + //UNSUP clocking_event ~p~sexpr %prec prSEQ_CLOCKING { } + // + //============= expr rules copied for sequence_expr + | BISONPRE_COPY_ONCE(expr,{s/~l~/s/g; s/~p~/s/g; s/~noPar__IGNORE~'.'/yP_PAR__IGNORE /g; }) // {copied} + ; -//UNSUPcycle_delay_range: // IEEE: ==cycle_delay_range -//UNSUP // // These three terms in 1800-2005 ONLY -//UNSUP yP_POUNDPOUND yaINTNUM { } -//UNSUP | yP_POUNDPOUND id { } -//UNSUP | yP_POUNDPOUND '(' constExpr ')' { } -//UNSUP // // In 1800-2009 ONLY: -//UNSUP // // IEEE: yP_POUNDPOUND constant_primary -//UNSUP // // UNSUP: This causes a big grammer ambiguity -//UNSUP // // as ()'s mismatch between primary and the following statement -//UNSUP // // the sv-ac committee has been asked to clarify (Mantis 1901) -//UNSUP | yP_POUNDPOUND '[' cycle_delay_const_range_expression ']' { } -//UNSUP | yP_POUNDPOUND yP_BRASTAR ']' { } -//UNSUP | yP_POUNDPOUND yP_BRAPLUSKET { } -//UNSUP ; +cycle_delay_range: // IEEE: ==cycle_delay_range + // // These three terms in 1800-2005 ONLY + yP_POUNDPOUND intnumAsConst + { $$ = $2; + BBUNSUP($1, "Unsupported: ## () cycle delay range expression"); } + | yP_POUNDPOUND id + { $$ = new AstConst{$1, AstConst::BitFalse{}}; + BBUNSUP($1, "Unsupported: ## id cycle delay range expression"); } + | yP_POUNDPOUND '(' constExpr ')' + { $$ = $3; + BBUNSUP($1, "Unsupported: ## () cycle delay range expression"); } + // // In 1800-2009 ONLY: + // // IEEE: yP_POUNDPOUND constant_primary + // // UNSUP: This causes a big grammer ambiguity + // // as ()'s mismatch between primary and the following statement + // // the sv-ac committee has been asked to clarify (Mantis 1901) + | yP_POUNDPOUND anyrange + { $$ = new AstConst{$1, AstConst::BitFalse{}}; + BBUNSUP($1, "Unsupported: ## range cycle delay range expression"); } + | yP_POUNDPOUND yP_BRASTAR ']' + { $$ = new AstConst{$1, AstConst::BitFalse{}}; + BBUNSUP($1, "Unsupported: ## [*] cycle delay range expression"); } + | yP_POUNDPOUND yP_BRAPLUSKET + { $$ = new AstConst{$1, AstConst::BitFalse{}}; + BBUNSUP($1, "Unsupported: ## [+] cycle delay range expression"); } + ; -//UNSUPsequence_match_itemList: // IEEE: [sequence_match_item] part of sequence_expr -//UNSUP sequence_match_item { $$ = $1; } -//UNSUP | sequence_match_itemList ',' sequence_match_item { } -//UNSUP ; +sequence_match_itemList: // IEEE: [sequence_match_item] part of sequence_expr + sequence_match_item { $$ = $1; } + | sequence_match_itemList ',' sequence_match_item { $$ = addNextNull($1, $3); } + ; -//UNSUPsequence_match_item: // ==IEEE: sequence_match_item -//UNSUP // // IEEE says: operator_assignment -//UNSUP // // IEEE says: inc_or_dec_expression -//UNSUP // // IEEE says: subroutine_call -//UNSUP // // This is the same list as... -//UNSUP for_step_assignment { $$ = $1; } -//UNSUP ; +sequence_match_item: // ==IEEE: sequence_match_item + // // IEEE says: operator_assignment + // // IEEE says: inc_or_dec_expression + // // IEEE says: subroutine_call + // // This is the same list as... + for_step_assignment { $$ = $1; } + ; -//UNSUPboolean_abbrev: // ==IEEE: boolean_abbrev -//UNSUP // // IEEE: consecutive_repetition -//UNSUP yP_BRASTAR const_or_range_expression ']' { } -//UNSUP | yP_BRASTAR ']' { } -//UNSUP | yP_BRAPLUSKET { $$ = $1; } -//UNSUP // // IEEE: non_consecutive_repetition -//UNSUP | yP_BRAEQ const_or_range_expression ']' { } -//UNSUP // // IEEE: goto_repetition -//UNSUP | yP_BRAMINUSGT const_or_range_expression ']' { } -//UNSUP ; - -//UNSUPconst_or_range_expression: // ==IEEE: const_or_range_expression -//UNSUP constExpr { $$ = $1; } -//UNSUP | cycle_delay_const_range_expression { } -//UNSUP ; - -//UNSUPconstant_range: // ==IEEE: constant_range -//UNSUP constExpr ':' constExpr { } -//UNSUP ; - -//UNSUPcycle_delay_const_range_expression: // ==IEEE: cycle_delay_const_range_expression -//UNSUP // // Note '$' is part of constExpr -//UNSUP constExpr ':' constExpr { } -//UNSUP ; +boolean_abbrev: // ==IEEE: boolean_abbrev + // // IEEE: consecutive_repetition + yP_BRASTAR constExpr ']' + { $$ = $2; BBUNSUP($1, "Unsupported: [*] boolean abbrev expression"); } + | yP_BRASTAR constExpr ':' constExpr ']' + { $$ = $2; BBUNSUP($1, "Unsupported: [*] boolean abbrev expression"); } + | yP_BRASTAR ']' + { $$ = new AstConst{$1, AstConst::BitFalse{}}; + BBUNSUP($1, "Unsupported: [*] boolean abbrev expression"); } + | yP_BRAPLUSKET + { $$ = new AstConst{$1, AstConst::BitFalse{}}; + BBUNSUP($1, "Unsupported: [+] boolean abbrev expression"); } + // // IEEE: non_consecutive_repetition + | yP_BRAEQ constExpr ']' + { $$ = $2; BBUNSUP($1, "Unsupported: [= boolean abbrev expression"); } + | yP_BRAEQ constExpr ':' constExpr ']' + { $$ = $2; BBUNSUP($1, "Unsupported: [= boolean abbrev expression"); } + // // IEEE: goto_repetition + | yP_BRAMINUSGT constExpr ']' + { $$ = $2; BBUNSUP($1, "Unsupported: [-> boolean abbrev expression"); } + | yP_BRAMINUSGT constExpr ':' constExpr ']' + { $$ = $2; BBUNSUP($1, "Unsupported: [-> boolean abbrev expression"); } + ; //************************************************ // Let @@ -6169,239 +6279,281 @@ complex_pexpr: // IEEE: part of property_expr, see comments there //************************************************ // Covergroup -//UNSUPcovergroup_declaration: // ==IEEE: covergroup_declaration -//UNSUP covergroup_declarationFront coverage_eventE ';' coverage_spec_or_optionListE -//UNSUP yENDGROUP endLabelE -//UNSUP { PARSEP->endgroupCb($5, $5); -//UNSUP SYMP->popScope($$); } -//UNSUP | covergroup_declarationFront '(' tf_port_listE ')' coverage_eventE ';' coverage_spec_or_optionListE -//UNSUP yENDGROUP endLabelE -//UNSUP { PARSEP->endgroupCb($8, $8); -//UNSUP SYMP->popScope($$); } -//UNSUP ; +covergroup_declaration: // ==IEEE: covergroup_declaration + covergroup_declarationFront coverage_eventE ';' + /*cont*/ coverage_spec_or_optionListE + /*cont*/ yENDGROUP endLabelE + { $$ = $1; + SYMP->popScope($$); + GRAMMARP->endLabel($6, $1, $6); } + | covergroup_declarationFront '(' tf_port_listE ')' + /*cont*/ coverage_eventE ';' coverage_spec_or_optionListE + /*cont*/ yENDGROUP endLabelE + { $$ = $1; + SYMP->popScope($$); + GRAMMARP->endLabel($9, $1, $9); } + ; -//UNSUPcovergroup_declarationFront: // IEEE: part of covergroup_declaration -//UNSUP yCOVERGROUP idAny -//UNSUP { SYMP->pushNew($$); -//UNSUP PARSEP->covergroupCb($1, $1, $2); } -//UNSUP ; +covergroup_declarationFront: // IEEE: part of covergroup_declaration + yCOVERGROUP idAny + { $$ = new AstClass{$2, *$2}; + BBUNSUP($1, "Unsupported: covergroup"); + SYMP->pushNew($$); + v3Global.setHasClasses(); } + ; -//UNSUPcgexpr: // IEEE-2012: covergroup_expression, before that just expression -//UNSUP expr { $$ = $1; } -//UNSUP ; +cgexpr: // IEEE-2012: covergroup_expression, before that just expression + expr { $$ = $1; } + ; -//UNSUPcoverage_spec_or_optionListE: // IEEE: [{coverage_spec_or_option}] -//UNSUP /* empty */ { $$ = nullptr; } -//UNSUP | coverage_spec_or_optionList { $$ = $1; } -//UNSUP ; +coverage_spec_or_optionListE: // IEEE: [{coverage_spec_or_option}] + /* empty */ { $$ = nullptr; } + | coverage_spec_or_optionList { $$ = $1; } + ; -//UNSUPcoverage_spec_or_optionList: // IEEE: {coverage_spec_or_option} -//UNSUP coverage_spec_or_option { $$ = $1; } -//UNSUP | coverage_spec_or_optionList coverage_spec_or_option { $$ = addNextNull($1, $2); } -//UNSUP ; +coverage_spec_or_optionList: // IEEE: {coverage_spec_or_option} + coverage_spec_or_option { $$ = $1; } + | coverage_spec_or_optionList coverage_spec_or_option { $$ = addNextNull($1, $2); } + ; -//UNSUPcoverage_spec_or_option: // ==IEEE: coverage_spec_or_option -//UNSUP // // IEEE: coverage_spec -//UNSUP cover_point { $$ = $1; } -//UNSUP | cover_cross { $$ = $1; } -//UNSUP | coverage_option ';' { $$ = $1; } -//UNSUP | error { $$ = nullptr; } -//UNSUP ; +coverage_spec_or_option: // ==IEEE: coverage_spec_or_option + // // IEEE: coverage_spec + cover_point { $$ = $1; } + | cover_cross { $$ = $1; } + | coverage_option ';' { $$ = $1; } + | error { $$ = nullptr; } + ; -//UNSUPcoverage_option: // ==IEEE: coverage_option -//UNSUP // // option/type_option aren't really keywords -//UNSUP id/*yOPTION | yTYPE_OPTION*/ '.' idAny/*member_identifier*/ '=' expr { } -//UNSUP ; +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"); } + ; -//UNSUPcover_point: // ==IEEE: cover_point -//UNSUP /**/ yCOVERPOINT expr iffE bins_or_empty { } -//UNSUP // // IEEE-2012: class_scope before an ID -//UNSUP | /**/ /**/ /**/ id ':' yCOVERPOINT expr iffE bins_or_empty { } -//UNSUP | class_scope_id ':' yCOVERPOINT expr iffE bins_or_empty { } -//UNSUP | class_scope_id id data_type id ':' yCOVERPOINT expr iffE bins_or_empty { } -//UNSUP | class_scope_id id /**/ id ':' yCOVERPOINT expr iffE bins_or_empty { } -//UNSUP | /**/ id /**/ id ':' yCOVERPOINT expr iffE bins_or_empty { } -//UNSUP // // IEEE-2012: -//UNSUP | bins_or_empty { $$ = $1; } -//UNSUP; +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"); } + // // IEEE-2012: class_scope before an ID + | id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty + { $$ = nullptr; BBUNSUP($3, "Unsupported: cover point"); } + // // data_type_or_implicit expansion + | data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty + { $$ = nullptr; BBUNSUP($4, "Unsupported: cover point"); } + | yVAR data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty + { $$ = nullptr; BBUNSUP($5, "Unsupported: cover point"); } + | yVAR implicit_typeE id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty + { $$ = nullptr; BBUNSUP($5, "Unsupported: cover point"); } + | signingE rangeList id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty + { $$ = nullptr; BBUNSUP($5, "Unsupported: cover point"); } + | signing id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty + { $$ = nullptr; BBUNSUP($4, "Unsupported: cover point"); } + // // IEEE-2012: + | bins_or_empty { $$ = $1; } + ; -//UNSUPiffE: // IEEE: part of cover_point, others -//UNSUP /* empty */ { $$ = nullptr; } -//UNSUP | yIFF '(' expr ')' { } -//UNSUP ; +iffE: // IEEE: part of cover_point, others + /* empty */ { $$ = nullptr; } + | yIFF '(' expr ')' + { $$ = nullptr; BBUNSUP($1, "Unsupported: cover 'iff'"); } + ; -//UNSUPbins_or_empty: // ==IEEE: bins_or_empty -//UNSUP '{' bins_or_optionsList '}' { $$ = $2; } -//UNSUP | '{' '}' { $$ = nullptr; } -//UNSUP | ';' { $$ = nullptr; } -//UNSUP ; +bins_or_empty: // ==IEEE: bins_or_empty + '{' bins_or_optionsList '}' { $$ = $2; } + | '{' '}' { $$ = nullptr; } + | ';' { $$ = nullptr; } + ; -//UNSUPbins_or_optionsList: // IEEE: { bins_or_options ';' } -//UNSUP bins_or_options ';' { $$ = $1; } -//UNSUP | bins_or_optionsList bins_or_options ';' { $$ = addNextNull($1, $2); } -//UNSUP ; +bins_or_optionsList: // IEEE: { bins_or_options ';' } + bins_or_options ';' { $$ = $1; } + | bins_or_optionsList bins_or_options ';' { $$ = addNextNull($1, $2); } + ; -//UNSUPbins_or_options: // ==IEEE: bins_or_options -//UNSUP // // Superset of IEEE - we allow []'s in more places -//UNSUP coverage_option { $$ = $1; } -//UNSUP // // Can't use wildcardE as results in conflicts -//UNSUP | /**/ bins_keyword id/*bin_identifier*/ bins_orBraE '=' '{' open_range_list '}' iffE { } -//UNSUP | yWILDCARD bins_keyword id/*bin_identifier*/ bins_orBraE '=' '{' open_range_list '}' iffE { } -//UNSUP | /**/ bins_keyword id/*bin_identifier*/ bins_orBraE '=' '{' open_range_list '}' yWITH__CUR '{' cgexpr ')' iffE { } -//UNSUP | yWILDCARD bins_keyword id/*bin_identifier*/ bins_orBraE '=' '{' open_range_list '}' yWITH__CUR '{' cgexpr ')' iffE { } -//UNSUP // -//UNSUP // // cgexpr part of trans_list -//UNSUP // -//UNSUP | /**/ bins_keyword id/*bin_identifier*/ bins_orBraE '=' trans_list iffE { } -//UNSUP | yWILDCARD bins_keyword id/*bin_identifier*/ bins_orBraE '=' trans_list iffE { } -//UNSUP // -//UNSUP | bins_keyword id/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE { } -//UNSUP // -//UNSUP | bins_keyword id/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE { } -//UNSUP ; +bins_or_options: // ==IEEE: bins_or_options + // // Superset of IEEE - we allow []'s in more places + coverage_option { $$ = $1; } + // // Can't use wildcardE as results in conflicts + | bins_keyword id/*bin_identifier*/ bins_orBraE '=' '{' open_range_list '}' iffE + { $$ = nullptr; BBUNSUP($4, "Unsupported: cover bin specification"); } + | bins_keyword id/*bin_identifier*/ bins_orBraE '=' '{' open_range_list '}' yWITH__CUR '{' cgexpr '}' iffE + { $$ = nullptr; BBUNSUP($8, "Unsupported: cover bin 'with' specification"); } + | yWILDCARD bins_keyword id/*bin_identifier*/ bins_orBraE '=' '{' open_range_list '}' iffE + { $$ = nullptr; BBUNSUP($5, "Unsupported: cover bin 'wildcard' specification"); } + | yWILDCARD bins_keyword id/*bin_identifier*/ bins_orBraE '=' '{' open_range_list '}' yWITH__CUR '{' cgexpr '}' iffE + { $$ = nullptr; BBUNSUP($9, "Unsupported: cover bin 'wildcard' 'with' specification"); } + // + // // cgexpr part of trans_list + | bins_keyword id/*bin_identifier*/ bins_orBraE '=' trans_list iffE + { $$ = nullptr; BBUNSUP($4, "Unsupported: cover bin trans list"); } + | yWILDCARD bins_keyword id/*bin_identifier*/ bins_orBraE '=' trans_list iffE + { $$ = nullptr; BBUNSUP($1, "Unsupported: cover bin 'wildcard' trans list"); } + // + | bins_keyword id/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE + { $$ = nullptr; BBUNSUP($5, "Unsupported: cover bin 'default'"); } + | bins_keyword id/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE + { $$ = nullptr; BBUNSUP($6, "Unsupported: cover bin 'default' 'sequence'"); } + ; -//UNSUPbins_orBraE: // IEEE: part of bins_or_options: -//UNSUP /* empty */ { $$ = nullptr; } -//UNSUP | '[' ']' { } -//UNSUP | '[' cgexpr ']' { } -//UNSUP ; +bins_orBraE: // IEEE: part of bins_or_options: + /* empty */ { $$ = nullptr; } + | '[' ']' { $$ = nullptr; /*UNSUP*/ } + | '[' cgexpr ']' { $$ = nullptr; /*UNSUP*/ } + ; -//UNSUPbins_keyword: // ==IEEE: bins_keyword -//UNSUP yBINS { } -//UNSUP | yILLEGAL_BINS { } -//UNSUP | yIGNORE_BINS { } -//UNSUP ; +bins_keyword: // ==IEEE: bins_keyword + yBINS { $$ = $1; /*UNSUP*/ } + | yILLEGAL_BINS { $$ = $1; /*UNSUP*/ } + | yIGNORE_BINS { $$ = $1; /*UNSUP*/ } + ; -//UNSUPcovergroup_range_list: // ==IEEE: covergroup_range_list -//UNSUP covergroup_value_range { $$ = $1; } -//UNSUP | covergroup_range_list ',' covergroup_value_range { $$ = addNextNull($1, $3); } -//UNSUP ; +trans_list: // ==IEEE: trans_list + '(' trans_set ')' { $$ = $2; } + | trans_list ',' '(' trans_set ')' { $$ = addNextNull($1, $4); } + ; -//UNSUPtrans_list: // ==IEEE: trans_list -//UNSUP '(' trans_set ')' { $$ = $2; } -//UNSUP | trans_list ',' '(' trans_set ')' { } -//UNSUP ; +trans_set: // ==IEEE: trans_set + trans_range_list { $$ = $1; } + // // Note the { => } in the grammer, this is really a list + | trans_set yP_EQGT trans_range_list + { $$ = $1; BBUNSUP($2, "Unsupported: cover trans set '=>'"); } + ; -//UNSUPtrans_set: // ==IEEE: trans_set -//UNSUP trans_range_list { $$ = $1; } -//UNSUP // // Note the { => } in the grammer, this is really a list -//UNSUP | trans_set yP_EQGT trans_range_list { } -//UNSUP ; +trans_range_list: // ==IEEE: trans_range_list + trans_item { $$ = $1; } + | trans_item yP_BRASTAR cgexpr ']' + { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[*'"); } + | trans_item yP_BRASTAR cgexpr ':' cgexpr ']' + { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[*'"); } + | trans_item yP_BRAMINUSGT cgexpr ']' + { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[->'"); } + | trans_item yP_BRAMINUSGT cgexpr ':' cgexpr ']' + { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[->'"); } + | trans_item yP_BRAEQ cgexpr ']' + { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[='"); } + | trans_item yP_BRAEQ cgexpr ':' cgexpr ']' + { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[='"); } + ; -//UNSUPtrans_range_list: // ==IEEE: trans_range_list -//UNSUP trans_item { $$ = $1; } -//UNSUP | trans_item yP_BRASTAR repeat_range ']' { } -//UNSUP | trans_item yP_BRAMINUSGT repeat_range ']' { } -//UNSUP | trans_item yP_BRAEQ repeat_range ']' { } -//UNSUP ; +trans_item: // ==IEEE: range_list + covergroup_range_list { $$ = $1; } + ; -//UNSUPtrans_item: // ==IEEE: range_list -//UNSUP covergroup_range_list { $$ = $1; } -//UNSUP ; +covergroup_range_list: // ==IEEE: covergroup_range_list + covergroup_value_range { $$ = $1; } + | covergroup_range_list ',' covergroup_value_range { $$ = addNextNull($1, $3); } + ; -//UNSUPrepeat_range: // ==IEEE: repeat_range -//UNSUP cgexpr { $$ = $1; } -//UNSUP | cgexpr ':' cgexpr { $$ = addNextNull($1, $3); } -//UNSUP ; -//UNSUPcover_cross: // ==IEEE: cover_cross -//UNSUP id/*cover_point_identifier*/ ':' yCROSS list_of_cross_items iffE cross_body { } -//UNSUP | /**/ yCROSS list_of_cross_items iffE cross_body { } -//UNSUP ; +cover_cross: // ==IEEE: cover_cross + id/*cover_point_identifier*/ ':' yCROSS list_of_cross_items iffE cross_body + { $$ = nullptr; BBUNSUP($3, "Unsupported: cross"); } + | yCROSS list_of_cross_items iffE cross_body + { $$ = nullptr; BBUNSUP($1, "Unsupported: cross"); } + ; -//UNSUPlist_of_cross_items: // ==IEEE: list_of_cross_items -//UNSUP cross_item ',' cross_item { $$ = addNextNull($1, $3); } -//UNSUP | cross_item ',' cross_item ',' cross_itemList -//UNSUP { $$ = addNextNull(addNextNull($1, $3), $5); } -//UNSUP ; +list_of_cross_items: // ==IEEE: list_of_cross_items + cross_item ',' cross_item { $$ = addNextNull($1, $3); } + | cross_item ',' cross_item ',' cross_itemList + { $$ = addNextNull(addNextNull($1, $3), $5); } + ; -//UNSUPcross_itemList: // IEEE: part of list_of_cross_items -//UNSUP cross_item { $$ = nullptr; } -//UNSUP | cross_itemList ',' cross_item { $$ = addNextNull($1, $3); } -//UNSUP ; +cross_itemList: // IEEE: part of list_of_cross_items + cross_item { $$ = $1; } + | cross_itemList ',' cross_item { $$ = addNextNull($1, $3); } + ; -//UNSUPcross_item: // ==IEEE: cross_item -//UNSUP idAny/*cover_point_identifier or variable_identifier*/ { $$ = $1; } -//UNSUP ; +cross_item: // ==IEEE: cross_item + idAny/*cover_point_identifier or variable_identifier*/ { $$ = nullptr; /*UNSUP*/ } + ; -//UNSUPcross_body: // ==IEEE: cross_body -//UNSUP '{' '}' { $$ = nullptr; } -//UNSUP // // IEEE-2012: No semicolon here, mistake in spec -//UNSUP | '{' cross_body_itemSemiList '}' { $$ = $1; } -//UNSUP | ';' { $$ = nullptr; } -//UNSUP ; +cross_body: // ==IEEE: cross_body + '{' '}' { $$ = nullptr; } + // // IEEE-2012: No semicolon here, mistake in spec + | '{' cross_body_itemSemiList '}' { $$ = $2; } + | ';' { $$ = nullptr; } + ; -//UNSUPcross_body_itemSemiList: // IEEE: part of cross_body -//UNSUP cross_body_item ';' { $$ = $1; } -//UNSUP | cross_body_itemSemiList cross_body_item ';' { $$ = addNextNull($1, $2); } -//UNSUP ; +cross_body_itemSemiList: // IEEE: part of cross_body + cross_body_item ';' { $$ = $1; } + | cross_body_itemSemiList cross_body_item ';' { $$ = addNextNull($1, $2); } + ; -//UNSUPcross_body_item: // ==IEEE: cross_body_item -//UNSUP // // IEEE: our semicolon is in the list -//UNSUP bins_selection_or_option { $$ = $1; } -//UNSUP | function_declaration { $$ = $1; } -//UNSUP ; +cross_body_item: // ==IEEE: cross_body_item + // // IEEE: our semicolon is in the list + // // IEEE: bins_selection_or_option + coverage_option { $$ = $1; } + // // IEEE: bins_selection + | function_declaration + { $$ = $1; BBUNSUP($1->fileline(), "Unsupported: coverage cross 'function' declaration"); } + | bins_keyword idAny/*new-bin_identifier*/ '=' select_expression iffE + { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage cross bin"); } + ; -//UNSUPbins_selection_or_option: // ==IEEE: bins_selection_or_option -//UNSUP coverage_option { $$ = $1; } -//UNSUP | bins_selection { $$ = $1; } -//UNSUP ; +select_expression: // ==IEEE: select_expression + // // IEEE: select_condition expanded here + yBINSOF '(' bins_expression ')' + { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage select expression 'binsof'"); } + | '!' yBINSOF '(' bins_expression ')' + { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage select expression 'binsof'"); } + | yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}' + { $$ = nullptr; BBUNSUP($5, "Unsupported: coverage select expression 'intersect'"); } + | '!' yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}' { } + { $$ = nullptr; BBUNSUP($5, "Unsupported: coverage select expression 'intersect'"); } + | yWITH__PAREN '(' cgexpr ')' + { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage select expression with"); } + | '!' yWITH__PAREN '(' cgexpr ')' + { $$ = nullptr; BBUNSUP($1, "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 + //UNSUP '!' yWITH__PAREN '(' cgexpr ')' yMATCHES cgexpr { } + // + | '(' select_expression ')' { $$ = $2; } + | select_expression yP_ANDAND select_expression + { $$ = nullptr; BBUNSUP($2, "Unsupported: coverage select expression '&&'"); } + | select_expression yP_OROR select_expression + { $$ = nullptr; BBUNSUP($2, "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"); } + // + // // Need precedence fix + //UNSUP cgexpr yMATCHES cgexpr {..} + ; -//UNSUPbins_selection: // ==IEEE: bins_selection -//UNSUP bins_keyword idAny/*new-bin_identifier*/ '=' select_expression iffE { } -//UNSUP ; +bins_expression: // ==IEEE: bins_expression + // // "cover_point_identifier" and "variable_identifier" look identical + id/*variable_identifier or cover_point_identifier*/ { $$ = nullptr; /*UNSUP*/ } + | id/*cover_point_identifier*/ '.' idAny/*bins_identifier*/ { $$ = nullptr; /*UNSUP*/ } + ; -//UNSUPselect_expression: // ==IEEE: select_expression -//UNSUP // // IEEE: select_condition expanded here -//UNSUP yBINSOF '(' bins_expression ')' { } -//UNSUP | yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}' { } -//UNSUP | yWITH__PAREN '(' cgexpr ')' { } -//UNSUP // // IEEE-2012: Need clarification as to precedence -//UNSUP //UNSUP yWITH__PAREN '(' cgexpr ')' yMATCHES cgexpr { } -//UNSUP | '!' yBINSOF '(' bins_expression ')' { } -//UNSUP | '!' yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}' { } -//UNSUP | '!' yWITH__PAREN '(' cgexpr ')' { } -//UNSUP // // IEEE-2012: Need clarification as to precedence -//UNSUP //UNSUP '!' yWITH__PAREN '(' cgexpr ')' yMATCHES cgexpr { } -//UNSUP | select_expression yP_ANDAND select_expression { } -//UNSUP | select_expression yP_OROR select_expression { } -//UNSUP | '(' select_expression ')' { $$ = $2; } -//UNSUP // // IEEE-2012: cross_identifier -//UNSUP // // Part of covergroup_expression - generic identifier -//UNSUP // // IEEE-2012: Need clarification as to precedence -//UNSUP //UNSUP covergroup_expression [ yMATCHES covergroup_expression ] -//UNSUP ; +coverage_eventE: // IEEE: [ coverage_event ] + /* empty */ { $$ = nullptr; } + | clocking_event + { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage clocking event"); } + | yWITH__ETC yFUNCTION idAny/*"sample"*/ '(' tf_port_listE ')' + { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage 'with' 'function'"); } + | yP_ATAT '(' block_event_expression ')' + { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage '@@' events"); } + ; -//UNSUPbins_expression: // ==IEEE: bins_expression -//UNSUP // // "cover_point_identifier" and "variable_identifier" look identical -//UNSUP id/*variable_identifier or cover_point_identifier*/ { $$ = $1; } -//UNSUP | id/*cover_point_identifier*/ '.' idAny/*bins_identifier*/ { } -//UNSUP ; +block_event_expression: // ==IEEE: block_event_expression + block_event_expressionTerm { $$ = nullptr; /*UNSUP @@*/ } + | block_event_expression yOR block_event_expressionTerm { $$ = nullptr; /*UNSUP @@*/ } + ; -//UNSUPcoverage_eventE: // IEEE: [ coverage_event ] -//UNSUP /* empty */ { $$ = nullptr; } -//UNSUP | clocking_event { $$ = $1; } -//UNSUP | yWITH__ETC function idAny/*"sample"*/ '(' tf_port_listE ')' { } -//UNSUP | yP_ATAT '(' block_event_expression ')' { } -//UNSUP ; +block_event_expressionTerm: // IEEE: part of block_event_expression + yBEGIN hierarchical_btf_identifier { $$ = nullptr; /*UNSUP @@*/ } + | yEND hierarchical_btf_identifier { $$ = nullptr; /*UNSUP @@*/ } + ; -//UNSUPblock_event_expression: // ==IEEE: block_event_expression -//UNSUP block_event_expressionTerm { $$ = $1; } -//UNSUP | block_event_expression yOR block_event_expressionTerm { } -//UNSUP ; - -//UNSUPblock_event_expressionTerm: // IEEE: part of block_event_expression -//UNSUP yBEGIN hierarchical_btf_identifier { } -//UNSUP | yEND hierarchical_btf_identifier { } -//UNSUP ; - -//UNSUPhierarchical_btf_identifier: // ==IEEE: hierarchical_btf_identifier -//UNSUP // // hierarchical_tf_identifier + hierarchical_block_identifier -//UNSUP hierarchical_identifier/*tf_or_block*/ { $$ = $1; } -//UNSUP // // method_identifier -//UNSUP | hierarchical_identifier class_scope_id { } -//UNSUP | hierarchical_identifier id { } -//UNSUP ; +hierarchical_btf_identifier: // ==IEEE: hierarchical_btf_identifier + // // hierarchical_tf_identifier + hierarchical_block_identifier + // // method_identifier + packageClassScopeE idAny { $$ = nullptr; /*UNSUP*/ } + ; //********************************************************************** // Randsequence @@ -6582,7 +6734,7 @@ checker_or_generate_item_declaration: // ==IEEE: checker_or_generate_ite | checker_declaration { $$ = nullptr; BBUNSUP($1, "Unsupported: recursive checker"); } | assertion_item_declaration { $$ = $1; } - //UNSUP covergroup_declaration { $$ = $1; } + | covergroup_declaration { $$ = $1; } // // IEEE deprecated: overload_declaration | genvar_declaration { $$ = $1; } | clocking_declaration { $$ = $1; } @@ -6619,13 +6771,16 @@ class_declaration: // ==IEEE: part of class_declaration classFront parameter_port_listE classExtendsE classImplementsE ';' /*mid*/ { // Allow resolving types declared in base extends class if ($3) SYMP->importExtends($3); + GRAMMARP->m_insideClass = true; } /*cont*/ class_itemListE yENDCLASS endLabelE { $$ = $1; $1->addMembersp($2); + if ($2) $1->isParameterized(true); $1->addExtendsp($3); $1->addExtendsp($4); $1->addMembersp($7); SYMP->popScope($$); + GRAMMARP->m_insideClass = false; GRAMMARP->endLabel($9, $1, $9); } ; @@ -6823,7 +6978,8 @@ class_item: // ==IEEE: class_item | class_declaration { $$ = nullptr; BBUNSUP($1, "Unsupported: class within class"); } | timeunits_declaration { $$ = $1; } - //UNSUP covergroup_declaration { $$ = $1; } + | covergroup_declaration + { $$ = nullptr; BBUNSUP($1, "Unsupported: covergroup within class"); } // // local_parameter_declaration under parameter_declaration | parameter_declaration ';' { $$ = $1; } | ';' { $$ = nullptr; } @@ -6882,9 +7038,13 @@ class_constraint: // ==IEEE: class_constraint // // IEEE: constraint_declaration // // UNSUP: We have the unsupported warning on the randomize() call, so don't bother on // // constraint blocks. When we support randomize we need to make AST nodes for below rules - constraintStaticE yCONSTRAINT idAny constraint_block { $$ = nullptr; /*UNSUP*/ } + constraintStaticE yCONSTRAINT idAny constraint_block + { // Variable so we can link and later ignore constraint_mode() methods + $$ = new AstVar{$3, VVarType::MEMBER, *$3, VFlagBitPacked{}, 1}; + $2->v3warn(CONSTRAINTIGN, "Constraint ignored (unsupported)"); } // // IEEE: constraint_prototype + constraint_prototype_qualifier - | constraintStaticE yCONSTRAINT idAny ';' { $$ = nullptr; } + | constraintStaticE yCONSTRAINT idAny ';' + { $$ = nullptr; } | yEXTERN constraintStaticE yCONSTRAINT idAny ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: extern constraint"); } | yPURE constraintStaticE yCONSTRAINT idAny ';' diff --git a/test_regress/driver.pl b/test_regress/driver.pl index 2a1685e4e..6bf8240c8 100755 --- a/test_regress/driver.pl +++ b/test_regress/driver.pl @@ -2227,6 +2227,7 @@ sub files_identical { $l1[$l] =~ s/CPU Time: +[0-9.]+ seconds[^\n]+/CPU Time: ###/mig; $l1[$l] =~ s/\?v=[0-9.]+/?v=latest/mig; # warning URL $l1[$l] =~ s/_h[0-9a-f]{8}_/_h########_/mg; + $l1[$l] =~ s/ \/[^ ]+\/verilated_std.sv/ verilated_std.sv/mg; if ($l1[$l] =~ s/Exiting due to.*/Exiting due to/mig) { splice @l1, $l+1; # Trunc rest last; diff --git a/test_regress/t/t_a5_attributes_src.pl b/test_regress/t/t_a5_attributes_src.pl index 92a4ca5d6..01c750bdf 100755 --- a/test_regress/t/t_a5_attributes_src.pl +++ b/test_regress/t/t_a5_attributes_src.pl @@ -36,7 +36,7 @@ sub check { tee => 1, cmd => ["python3", "$root/nodist/clang_check_attributes --verilator-root=$root --cxxflags='$clang_args' $precompile_args $srcfiles_str"]); - file_grep($Self->{run_log_filename}, "Number of functions reported unsafe: 27"); + file_grep($Self->{run_log_filename}, "Number of functions reported unsafe: 0"); } run_clang_check(); diff --git a/test_regress/t/t_array_backw_index_bad.out b/test_regress/t/t_array_backw_index_bad.out index 83ee4b870..f49d74019 100644 --- a/test_regress/t/t_array_backw_index_bad.out +++ b/test_regress/t/t_array_backw_index_bad.out @@ -1,16 +1,16 @@ -%Error: t/t_array_backw_index_bad.v:17:19: Slice selection '[1:3]' has backward indexing versus data type's '[3:0]' +%Error: t/t_array_backw_index_bad.v:17:19: Slice selection '[1:3]' has reversed range order versus data type's '[3:0]' : ... In instance t 17 | array_assign[1:3] = '{32'd4, 32'd3, 32'd2}; | ^ -%Error: t/t_array_backw_index_bad.v:18:20: Slice selection '[3:1]' has backward indexing versus data type's '[0:3]' +%Error: t/t_array_backw_index_bad.v:18:20: Slice selection '[3:1]' has reversed range order versus data type's '[0:3]' : ... In instance t 18 | larray_assign[3:1] = '{32'd4, 32'd3, 32'd2}; | ^ -%Error: t/t_array_backw_index_bad.v:19:20: Slice selection '[4:6]' has backward indexing versus data type's '[6:3]' +%Error: t/t_array_backw_index_bad.v:19:20: Slice selection '[4:6]' has reversed range order versus data type's '[6:3]' : ... In instance t 19 | array_assign2[4:6] = '{32'd4, 32'd3, 32'd2}; | ^ -%Error: t/t_array_backw_index_bad.v:20:21: Slice selection '[6:4]' has backward indexing versus data type's '[3:6]' +%Error: t/t_array_backw_index_bad.v:20:21: Slice selection '[6:4]' has reversed range order versus data type's '[3:6]' : ... In instance t 20 | larray_assign2[6:4] = '{32'd4, 32'd3, 32'd2}; | ^ diff --git a/test_regress/t/t_array_packed_endian.v b/test_regress/t/t_array_packed_endian.v index 5a4fa03f0..a0f3300ff 100644 --- a/test_regress/t/t_array_packed_endian.v +++ b/test_regress/t/t_array_packed_endian.v @@ -12,27 +12,27 @@ typedef struct packed { } tb_t; typedef struct packed { - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE logic [0:7] a; - // verilator lint_on LITENDIAN + // verilator lint_on ASCRANGE } tl_t; typedef struct packed { logic [7:0] bb; - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE tb_t [0:1] cbl; tb_t [1:0] cbb; tl_t [0:1] cll; tl_t [1:0] clb; logic [0:7] dl; - // verilator lint_on LITENDIAN + // verilator lint_on ASCRANGE } t2; logic [2:0][31:0] test2l; -// verilator lint_off LITENDIAN +// verilator lint_off ASCRANGE logic [0:2][31:0] test2b; logic [0:2][31:0] test1b; -// verilator lint_on LITENDIAN +// verilator lint_on ASCRANGE logic [2:0][31:0] test1l; module t; diff --git a/test_regress/t/t_array_packed_sysfunct.v b/test_regress/t/t_array_packed_sysfunct.v index 2c3c7520f..4a8ce3f9a 100644 --- a/test_regress/t/t_array_packed_sysfunct.v +++ b/test_regress/t/t_array_packed_sysfunct.v @@ -19,10 +19,10 @@ module t (/*AUTOARG*/ localparam WC = 8; // 2D packed arrays - logic [WA+1:2] [WB+1:2] [WC+1:2] array_bg; // big endian array - /* verilator lint_off LITENDIAN */ - logic [2:WA+1] [2:WB+1] [2:WC+1] array_lt; // little endian array - /* verilator lint_on LITENDIAN */ + logic [WA+1:2] [WB+1:2] [WC+1:2] array_dsc; // descending range array + /* verilator lint_off ASCRANGE */ + logic [2:WA+1] [2:WB+1] [2:WC+1] array_asc; // ascending range array + /* verilator lint_on ASCRANGE */ logic [1:0] array_unpk [3:2][1:0]; @@ -83,84 +83,84 @@ module t (/*AUTOARG*/ $write("cnt[30:4]=%0d slc=%0d dim=%0d wdt=%0d\n", cnt[30:4], slc, dim, wdt); `endif if (cnt[30:4]==1) begin - // big endian + // descending range if (slc==0) begin // full array - `checkh($dimensions (array_bg), 3); - `checkh($bits (array_bg), WA*WB*WC); + `checkh($dimensions (array_dsc), 3); + `checkh($bits (array_dsc), WA*WB*WC); if ((dim>=1)&&(dim<=3)) begin - `checkh($left (array_bg, dim), wdt+1); - `checkh($right (array_bg, dim), 2 ); - `checkh($low (array_bg, dim), 2 ); - `checkh($high (array_bg, dim), wdt+1); - `checkh($increment (array_bg, dim), 1 ); - `checkh($size (array_bg, dim), wdt ); + `checkh($left (array_dsc, dim), wdt+1); + `checkh($right (array_dsc, dim), 2 ); + `checkh($low (array_dsc, dim), 2 ); + `checkh($high (array_dsc, dim), wdt+1); + `checkh($increment (array_dsc, dim), 1 ); + `checkh($size (array_dsc, dim), wdt ); end end else if (slc==1) begin // single array element - `checkh($dimensions (array_bg[2]), 2); - `checkh($bits (array_bg[2]), WB*WC); + `checkh($dimensions (array_dsc[2]), 2); + `checkh($bits (array_dsc[2]), WB*WC); if ((dim>=2)&&(dim<=3)) begin - `checkh($left (array_bg[2], dim-1), wdt+1); - `checkh($right (array_bg[2], dim-1), 2 ); - `checkh($low (array_bg[2], dim-1), 2 ); - `checkh($high (array_bg[2], dim-1), wdt+1); - `checkh($increment (array_bg[2], dim-1), 1 ); - `checkh($size (array_bg[2], dim-1), wdt ); + `checkh($left (array_dsc[2], dim-1), wdt+1); + `checkh($right (array_dsc[2], dim-1), 2 ); + `checkh($low (array_dsc[2], dim-1), 2 ); + `checkh($high (array_dsc[2], dim-1), wdt+1); + `checkh($increment (array_dsc[2], dim-1), 1 ); + `checkh($size (array_dsc[2], dim-1), wdt ); end `ifndef VERILATOR // Unsupported slices don't maintain size correctly end else if (slc==2) begin // half array - `checkh($dimensions (array_bg[WA/2+1:2]), 3); - `checkh($bits (array_bg[WA/2+1:2]), WA/2*WB*WC); + `checkh($dimensions (array_dsc[WA/2+1:2]), 3); + `checkh($bits (array_dsc[WA/2+1:2]), WA/2*WB*WC); if ((dim>=1)&&(dim<=3)) begin - `checkh($left (array_bg[WA/2+1:2], dim), wdt+1); - `checkh($right (array_bg[WA/2+1:2], dim), 2 ); - `checkh($low (array_bg[WA/2+1:2], dim), 2 ); - `checkh($high (array_bg[WA/2+1:2], dim), wdt+1); - `checkh($increment (array_bg[WA/2+1:2], dim), 1 ); - `checkh($size (array_bg[WA/2+1:2], dim), wdt); + `checkh($left (array_dsc[WA/2+1:2], dim), wdt+1); + `checkh($right (array_dsc[WA/2+1:2], dim), 2 ); + `checkh($low (array_dsc[WA/2+1:2], dim), 2 ); + `checkh($high (array_dsc[WA/2+1:2], dim), wdt+1); + `checkh($increment (array_dsc[WA/2+1:2], dim), 1 ); + `checkh($size (array_dsc[WA/2+1:2], dim), wdt); end `endif end end else if (cnt[30:4]==2) begin - // little endian + // ascending range if (slc==0) begin // full array - `checkh($dimensions (array_lt), 3); - `checkh($bits (array_lt), WA*WB*WC); + `checkh($dimensions (array_asc), 3); + `checkh($bits (array_asc), WA*WB*WC); if ((dim>=1)&&(dim<=3)) begin - `checkh($left (array_lt, dim), 2 ); - `checkh($right (array_lt, dim), wdt+1); - `checkh($low (array_lt, dim), 2 ); - `checkh($high (array_lt, dim), wdt+1); - `checkh($increment (array_lt, dim), -1 ); - `checkh($size (array_lt, dim), wdt ); + `checkh($left (array_asc, dim), 2 ); + `checkh($right (array_asc, dim), wdt+1); + `checkh($low (array_asc, dim), 2 ); + `checkh($high (array_asc, dim), wdt+1); + `checkh($increment (array_asc, dim), -1 ); + `checkh($size (array_asc, dim), wdt ); end end else if (slc==1) begin // single array element - `checkh($dimensions (array_lt[2]), 2); - `checkh($bits (array_lt[2]), WB*WC); + `checkh($dimensions (array_asc[2]), 2); + `checkh($bits (array_asc[2]), WB*WC); if ((dim>=2)&&(dim<=3)) begin - `checkh($left (array_lt[2], dim-1), 2 ); - `checkh($right (array_lt[2], dim-1), wdt+1); - `checkh($low (array_lt[2], dim-1), 2 ); - `checkh($high (array_lt[2], dim-1), wdt+1); - `checkh($increment (array_lt[2], dim-1), -1 ); - `checkh($size (array_lt[2], dim-1), wdt ); + `checkh($left (array_asc[2], dim-1), 2 ); + `checkh($right (array_asc[2], dim-1), wdt+1); + `checkh($low (array_asc[2], dim-1), 2 ); + `checkh($high (array_asc[2], dim-1), wdt+1); + `checkh($increment (array_asc[2], dim-1), -1 ); + `checkh($size (array_asc[2], dim-1), wdt ); end `ifndef VERILATOR // Unsupported slices don't maintain size correctly end else if (slc==2) begin // half array - `checkh($dimensions (array_lt[2:WA/2+1]), 3); - `checkh($bits (array_lt[2:WA/2+1]), WA/2*WB*WC); + `checkh($dimensions (array_asc[2:WA/2+1]), 3); + `checkh($bits (array_asc[2:WA/2+1]), WA/2*WB*WC); if ((dim>=1)&&(dim<=3)) begin - `checkh($left (array_lt[2:WA/2+1], dim), 2 ); - `checkh($right (array_lt[2:WA/2+1], dim), wdt+1); - `checkh($low (array_lt[2:WA/2+1], dim), 2 ); - `checkh($high (array_lt[2:WA/2+1], dim), wdt+1); - `checkh($increment (array_lt[2:WA/2+1], dim), -1 ); - `checkh($size (array_lt[2:WA/2+1], dim), wdt ); + `checkh($left (array_asc[2:WA/2+1], dim), 2 ); + `checkh($right (array_asc[2:WA/2+1], dim), wdt+1); + `checkh($low (array_asc[2:WA/2+1], dim), 2 ); + `checkh($high (array_asc[2:WA/2+1], dim), wdt+1); + `checkh($increment (array_asc[2:WA/2+1], dim), -1 ); + `checkh($size (array_asc[2:WA/2+1], dim), wdt ); end `endif end diff --git a/test_regress/t/t_array_packed_write_read.v b/test_regress/t/t_array_packed_write_read.v index 0da8351b4..1e2bf3d15 100644 --- a/test_regress/t/t_array_packed_write_read.v +++ b/test_regress/t/t_array_packed_write_read.v @@ -18,10 +18,10 @@ module t (/*AUTOARG*/ localparam NO = 10; // number of access events // 2D packed arrays - logic [WA-1:0] [WB-1:0] array_bg; // big endian array - /* verilator lint_off LITENDIAN */ - logic [0:WA-1] [0:WB-1] array_lt; // little endian array - /* verilator lint_on LITENDIAN */ + logic [WA-1:0] [WB-1:0] array_dsc; // descending range array + /* verilator lint_off ASCRANGE */ + logic [0:WA-1] [0:WB-1] array_asc; // ascending range array + /* verilator lint_on ASCRANGE */ integer cnt = 0; @@ -41,108 +41,108 @@ module t (/*AUTOARG*/ $finish; end - // big endian + // descending range always @ (posedge clk) if (cnt[1:0]==2'd0) begin // initialize to defaaults (all bits to 0) - if (cnt[30:2]==0) array_bg <= '0; - else if (cnt[30:2]==1) array_bg <= '0; - else if (cnt[30:2]==2) array_bg <= '0; - else if (cnt[30:2]==3) array_bg <= '0; - else if (cnt[30:2]==4) array_bg <= '0; - else if (cnt[30:2]==5) array_bg <= '0; - else if (cnt[30:2]==6) array_bg <= '0; - else if (cnt[30:2]==7) array_bg <= '0; - else if (cnt[30:2]==8) array_bg <= '0; - else if (cnt[30:2]==9) array_bg <= '0; + if (cnt[30:2]==0) array_dsc <= '0; + else if (cnt[30:2]==1) array_dsc <= '0; + else if (cnt[30:2]==2) array_dsc <= '0; + else if (cnt[30:2]==3) array_dsc <= '0; + else if (cnt[30:2]==4) array_dsc <= '0; + else if (cnt[30:2]==5) array_dsc <= '0; + else if (cnt[30:2]==6) array_dsc <= '0; + else if (cnt[30:2]==7) array_dsc <= '0; + else if (cnt[30:2]==8) array_dsc <= '0; + else if (cnt[30:2]==9) array_dsc <= '0; end else if (cnt[1:0]==2'd1) begin // write value to array if (cnt[30:2]==0) begin end - else if (cnt[30:2]==1) array_bg <= {WA *WB +0{1'b1}}; - else if (cnt[30:2]==2) array_bg [WA/2-1:0 ] <= {WA/2*WB +0{1'b1}}; - else if (cnt[30:2]==3) array_bg [WA -1:WA/2] <= {WA/2*WB +0{1'b1}}; - else if (cnt[30:2]==4) array_bg [ 0 ] <= {1 *WB +0{1'b1}}; - else if (cnt[30:2]==5) array_bg [WA -1 ] <= {1 *WB +0{1'b1}}; - else if (cnt[30:2]==6) array_bg [ 0 ][WB/2-1:0 ] <= {1 *WB/2+0{1'b1}}; - else if (cnt[30:2]==7) array_bg [WA -1 ][WB -1:WB/2] <= {1 *WB/2+0{1'b1}}; - else if (cnt[30:2]==8) array_bg [ 0 ][ 0 ] <= {1 *1 +0{1'b1}}; - else if (cnt[30:2]==9) array_bg [WA -1 ][WB -1 ] <= {1 *1 +0{1'b1}}; + else if (cnt[30:2]==1) array_dsc <= {WA *WB +0{1'b1}}; + else if (cnt[30:2]==2) array_dsc [WA/2-1:0 ] <= {WA/2*WB +0{1'b1}}; + else if (cnt[30:2]==3) array_dsc [WA -1:WA/2] <= {WA/2*WB +0{1'b1}}; + else if (cnt[30:2]==4) array_dsc [ 0 ] <= {1 *WB +0{1'b1}}; + else if (cnt[30:2]==5) array_dsc [WA -1 ] <= {1 *WB +0{1'b1}}; + else if (cnt[30:2]==6) array_dsc [ 0 ][WB/2-1:0 ] <= {1 *WB/2+0{1'b1}}; + else if (cnt[30:2]==7) array_dsc [WA -1 ][WB -1:WB/2] <= {1 *WB/2+0{1'b1}}; + else if (cnt[30:2]==8) array_dsc [ 0 ][ 0 ] <= {1 *1 +0{1'b1}}; + else if (cnt[30:2]==9) array_dsc [WA -1 ][WB -1 ] <= {1 *1 +0{1'b1}}; end else if (cnt[1:0]==2'd2) begin // check array value - if (cnt[30:2]==0) begin if (array_bg !== 64'b0000000000000000000000000000000000000000000000000000000000000000) begin $display("%b", array_bg); $stop(); end end - else if (cnt[30:2]==1) begin if (array_bg !== 64'b1111111111111111111111111111111111111111111111111111111111111111) begin $display("%b", array_bg); $stop(); end end - else if (cnt[30:2]==2) begin if (array_bg !== 64'b0000000000000000000000000000000011111111111111111111111111111111) begin $display("%b", array_bg); $stop(); end end - else if (cnt[30:2]==3) begin if (array_bg !== 64'b1111111111111111111111111111111100000000000000000000000000000000) begin $display("%b", array_bg); $stop(); end end - else if (cnt[30:2]==4) begin if (array_bg !== 64'b0000000000000000000000000000000000000000000000000000000011111111) begin $display("%b", array_bg); $stop(); end end - else if (cnt[30:2]==5) begin if (array_bg !== 64'b1111111100000000000000000000000000000000000000000000000000000000) begin $display("%b", array_bg); $stop(); end end - else if (cnt[30:2]==6) begin if (array_bg !== 64'b0000000000000000000000000000000000000000000000000000000000001111) begin $display("%b", array_bg); $stop(); end end - else if (cnt[30:2]==7) begin if (array_bg !== 64'b1111000000000000000000000000000000000000000000000000000000000000) begin $display("%b", array_bg); $stop(); end end - else if (cnt[30:2]==8) begin if (array_bg !== 64'b0000000000000000000000000000000000000000000000000000000000000001) begin $display("%b", array_bg); $stop(); end end - else if (cnt[30:2]==9) begin if (array_bg !== 64'b1000000000000000000000000000000000000000000000000000000000000000) begin $display("%b", array_bg); $stop(); end end + if (cnt[30:2]==0) begin if (array_dsc !== 64'b0000000000000000000000000000000000000000000000000000000000000000) begin $display("%b", array_dsc); $stop(); end end + else if (cnt[30:2]==1) begin if (array_dsc !== 64'b1111111111111111111111111111111111111111111111111111111111111111) begin $display("%b", array_dsc); $stop(); end end + else if (cnt[30:2]==2) begin if (array_dsc !== 64'b0000000000000000000000000000000011111111111111111111111111111111) begin $display("%b", array_dsc); $stop(); end end + else if (cnt[30:2]==3) begin if (array_dsc !== 64'b1111111111111111111111111111111100000000000000000000000000000000) begin $display("%b", array_dsc); $stop(); end end + else if (cnt[30:2]==4) begin if (array_dsc !== 64'b0000000000000000000000000000000000000000000000000000000011111111) begin $display("%b", array_dsc); $stop(); end end + else if (cnt[30:2]==5) begin if (array_dsc !== 64'b1111111100000000000000000000000000000000000000000000000000000000) begin $display("%b", array_dsc); $stop(); end end + else if (cnt[30:2]==6) begin if (array_dsc !== 64'b0000000000000000000000000000000000000000000000000000000000001111) begin $display("%b", array_dsc); $stop(); end end + else if (cnt[30:2]==7) begin if (array_dsc !== 64'b1111000000000000000000000000000000000000000000000000000000000000) begin $display("%b", array_dsc); $stop(); end end + else if (cnt[30:2]==8) begin if (array_dsc !== 64'b0000000000000000000000000000000000000000000000000000000000000001) begin $display("%b", array_dsc); $stop(); end end + else if (cnt[30:2]==9) begin if (array_dsc !== 64'b1000000000000000000000000000000000000000000000000000000000000000) begin $display("%b", array_dsc); $stop(); end end end else if (cnt[1:0]==2'd3) begin // read value from array (not a very good test for now) - if (cnt[30:2]==0) begin if (array_bg !== {WA *WB {1'b0}}) $stop(); end - else if (cnt[30:2]==1) begin if (array_bg !== {WA *WB +0{1'b1}}) $stop(); end - else if (cnt[30:2]==2) begin if (array_bg [WA/2-1:0 ] !== {WA/2*WB +0{1'b1}}) $stop(); end - else if (cnt[30:2]==3) begin if (array_bg [WA -1:WA/2] !== {WA/2*WB +0{1'b1}}) $stop(); end - else if (cnt[30:2]==4) begin if (array_bg [ 0 ] !== {1 *WB +0{1'b1}}) $stop(); end - else if (cnt[30:2]==5) begin if (array_bg [WA -1 ] !== {1 *WB +0{1'b1}}) $stop(); end - else if (cnt[30:2]==6) begin if (array_bg [ 0 ][WB/2-1:0 ] !== {1 *WB/2+0{1'b1}}) $stop(); end - else if (cnt[30:2]==7) begin if (array_bg [WA -1 ][WB -1:WB/2] !== {1 *WB/2+0{1'b1}}) $stop(); end - else if (cnt[30:2]==8) begin if (array_bg [ 0 ][ 0 ] !== {1 *1 +0{1'b1}}) $stop(); end - else if (cnt[30:2]==9) begin if (array_bg [WA -1 ][WB -1 ] !== {1 *1 +0{1'b1}}) $stop(); end + if (cnt[30:2]==0) begin if (array_dsc !== {WA *WB {1'b0}}) $stop(); end + else if (cnt[30:2]==1) begin if (array_dsc !== {WA *WB +0{1'b1}}) $stop(); end + else if (cnt[30:2]==2) begin if (array_dsc [WA/2-1:0 ] !== {WA/2*WB +0{1'b1}}) $stop(); end + else if (cnt[30:2]==3) begin if (array_dsc [WA -1:WA/2] !== {WA/2*WB +0{1'b1}}) $stop(); end + else if (cnt[30:2]==4) begin if (array_dsc [ 0 ] !== {1 *WB +0{1'b1}}) $stop(); end + else if (cnt[30:2]==5) begin if (array_dsc [WA -1 ] !== {1 *WB +0{1'b1}}) $stop(); end + else if (cnt[30:2]==6) begin if (array_dsc [ 0 ][WB/2-1:0 ] !== {1 *WB/2+0{1'b1}}) $stop(); end + else if (cnt[30:2]==7) begin if (array_dsc [WA -1 ][WB -1:WB/2] !== {1 *WB/2+0{1'b1}}) $stop(); end + else if (cnt[30:2]==8) begin if (array_dsc [ 0 ][ 0 ] !== {1 *1 +0{1'b1}}) $stop(); end + else if (cnt[30:2]==9) begin if (array_dsc [WA -1 ][WB -1 ] !== {1 *1 +0{1'b1}}) $stop(); end end - // little endian + // ascending range always @ (posedge clk) if (cnt[1:0]==2'd0) begin // initialize to defaaults (all bits to 0) - if (cnt[30:2]==0) array_lt <= '0; - else if (cnt[30:2]==1) array_lt <= '0; - else if (cnt[30:2]==2) array_lt <= '0; - else if (cnt[30:2]==3) array_lt <= '0; - else if (cnt[30:2]==4) array_lt <= '0; - else if (cnt[30:2]==5) array_lt <= '0; - else if (cnt[30:2]==6) array_lt <= '0; - else if (cnt[30:2]==7) array_lt <= '0; - else if (cnt[30:2]==8) array_lt <= '0; - else if (cnt[30:2]==9) array_lt <= '0; + if (cnt[30:2]==0) array_asc <= '0; + else if (cnt[30:2]==1) array_asc <= '0; + else if (cnt[30:2]==2) array_asc <= '0; + else if (cnt[30:2]==3) array_asc <= '0; + else if (cnt[30:2]==4) array_asc <= '0; + else if (cnt[30:2]==5) array_asc <= '0; + else if (cnt[30:2]==6) array_asc <= '0; + else if (cnt[30:2]==7) array_asc <= '0; + else if (cnt[30:2]==8) array_asc <= '0; + else if (cnt[30:2]==9) array_asc <= '0; end else if (cnt[1:0]==2'd1) begin // write value to array if (cnt[30:2]==0) begin end - else if (cnt[30:2]==1) array_lt <= {WA *WB +0{1'b1}}; - else if (cnt[30:2]==2) array_lt [0 :WA/2-1] <= {WA/2*WB +0{1'b1}}; - else if (cnt[30:2]==3) array_lt [WA/2:WA -1] <= {WA/2*WB +0{1'b1}}; - else if (cnt[30:2]==4) array_lt [0 ] <= {1 *WB +0{1'b1}}; - else if (cnt[30:2]==5) array_lt [ WA -1] <= {1 *WB +0{1'b1}}; - else if (cnt[30:2]==6) array_lt [0 ][0 :WB/2-1] <= {1 *WB/2+0{1'b1}}; - else if (cnt[30:2]==7) array_lt [ WA -1][WB/2:WB -1] <= {1 *WB/2+0{1'b1}}; - else if (cnt[30:2]==8) array_lt [0 ][0 ] <= {1 *1 +0{1'b1}}; - else if (cnt[30:2]==9) array_lt [ WA -1][ WB -1] <= {1 *1 +0{1'b1}}; + else if (cnt[30:2]==1) array_asc <= {WA *WB +0{1'b1}}; + else if (cnt[30:2]==2) array_asc [0 :WA/2-1] <= {WA/2*WB +0{1'b1}}; + else if (cnt[30:2]==3) array_asc [WA/2:WA -1] <= {WA/2*WB +0{1'b1}}; + else if (cnt[30:2]==4) array_asc [0 ] <= {1 *WB +0{1'b1}}; + else if (cnt[30:2]==5) array_asc [ WA -1] <= {1 *WB +0{1'b1}}; + else if (cnt[30:2]==6) array_asc [0 ][0 :WB/2-1] <= {1 *WB/2+0{1'b1}}; + else if (cnt[30:2]==7) array_asc [ WA -1][WB/2:WB -1] <= {1 *WB/2+0{1'b1}}; + else if (cnt[30:2]==8) array_asc [0 ][0 ] <= {1 *1 +0{1'b1}}; + else if (cnt[30:2]==9) array_asc [ WA -1][ WB -1] <= {1 *1 +0{1'b1}}; end else if (cnt[1:0]==2'd2) begin // check array value - if (cnt[30:2]==0) begin if (array_lt !== 64'b0000000000000000000000000000000000000000000000000000000000000000) begin $display("%b", array_lt); $stop(); end end - else if (cnt[30:2]==1) begin if (array_lt !== 64'b1111111111111111111111111111111111111111111111111111111111111111) begin $display("%b", array_lt); $stop(); end end - else if (cnt[30:2]==2) begin if (array_lt !== 64'b1111111111111111111111111111111100000000000000000000000000000000) begin $display("%b", array_lt); $stop(); end end - else if (cnt[30:2]==3) begin if (array_lt !== 64'b0000000000000000000000000000000011111111111111111111111111111111) begin $display("%b", array_lt); $stop(); end end - else if (cnt[30:2]==4) begin if (array_lt !== 64'b1111111100000000000000000000000000000000000000000000000000000000) begin $display("%b", array_lt); $stop(); end end - else if (cnt[30:2]==5) begin if (array_lt !== 64'b0000000000000000000000000000000000000000000000000000000011111111) begin $display("%b", array_lt); $stop(); end end - else if (cnt[30:2]==6) begin if (array_lt !== 64'b1111000000000000000000000000000000000000000000000000000000000000) begin $display("%b", array_lt); $stop(); end end - else if (cnt[30:2]==7) begin if (array_lt !== 64'b0000000000000000000000000000000000000000000000000000000000001111) begin $display("%b", array_lt); $stop(); end end - else if (cnt[30:2]==8) begin if (array_lt !== 64'b1000000000000000000000000000000000000000000000000000000000000000) begin $display("%b", array_lt); $stop(); end end - else if (cnt[30:2]==9) begin if (array_lt !== 64'b0000000000000000000000000000000000000000000000000000000000000001) begin $display("%b", array_lt); $stop(); end end + if (cnt[30:2]==0) begin if (array_asc !== 64'b0000000000000000000000000000000000000000000000000000000000000000) begin $display("%b", array_asc); $stop(); end end + else if (cnt[30:2]==1) begin if (array_asc !== 64'b1111111111111111111111111111111111111111111111111111111111111111) begin $display("%b", array_asc); $stop(); end end + else if (cnt[30:2]==2) begin if (array_asc !== 64'b1111111111111111111111111111111100000000000000000000000000000000) begin $display("%b", array_asc); $stop(); end end + else if (cnt[30:2]==3) begin if (array_asc !== 64'b0000000000000000000000000000000011111111111111111111111111111111) begin $display("%b", array_asc); $stop(); end end + else if (cnt[30:2]==4) begin if (array_asc !== 64'b1111111100000000000000000000000000000000000000000000000000000000) begin $display("%b", array_asc); $stop(); end end + else if (cnt[30:2]==5) begin if (array_asc !== 64'b0000000000000000000000000000000000000000000000000000000011111111) begin $display("%b", array_asc); $stop(); end end + else if (cnt[30:2]==6) begin if (array_asc !== 64'b1111000000000000000000000000000000000000000000000000000000000000) begin $display("%b", array_asc); $stop(); end end + else if (cnt[30:2]==7) begin if (array_asc !== 64'b0000000000000000000000000000000000000000000000000000000000001111) begin $display("%b", array_asc); $stop(); end end + else if (cnt[30:2]==8) begin if (array_asc !== 64'b1000000000000000000000000000000000000000000000000000000000000000) begin $display("%b", array_asc); $stop(); end end + else if (cnt[30:2]==9) begin if (array_asc !== 64'b0000000000000000000000000000000000000000000000000000000000000001) begin $display("%b", array_asc); $stop(); end end end else if (cnt[1:0]==2'd3) begin // read value from array (not a very good test for now) - if (cnt[30:2]==0) begin if (array_lt !== {WA *WB {1'b0}}) $stop(); end - else if (cnt[30:2]==1) begin if (array_lt !== {WA *WB +0{1'b1}}) $stop(); end - else if (cnt[30:2]==2) begin if (array_lt [0 :WA/2-1] !== {WA/2*WB +0{1'b1}}) $stop(); end - else if (cnt[30:2]==3) begin if (array_lt [WA/2:WA -1] !== {WA/2*WB +0{1'b1}}) $stop(); end - else if (cnt[30:2]==4) begin if (array_lt [0 ] !== {1 *WB +0{1'b1}}) $stop(); end - else if (cnt[30:2]==5) begin if (array_lt [ WA -1] !== {1 *WB +0{1'b1}}) $stop(); end - else if (cnt[30:2]==6) begin if (array_lt [0 ][0 :WB/2-1] !== {1 *WB/2+0{1'b1}}) $stop(); end - else if (cnt[30:2]==7) begin if (array_lt [ WA -1][WB/2:WB -1] !== {1 *WB/2+0{1'b1}}) $stop(); end - else if (cnt[30:2]==8) begin if (array_lt [0 ][0 ] !== {1 *1 +0{1'b1}}) $stop(); end - else if (cnt[30:2]==9) begin if (array_lt [ WA -1][ WB -1] !== {1 *1 +0{1'b1}}) $stop(); end + if (cnt[30:2]==0) begin if (array_asc !== {WA *WB {1'b0}}) $stop(); end + else if (cnt[30:2]==1) begin if (array_asc !== {WA *WB +0{1'b1}}) $stop(); end + else if (cnt[30:2]==2) begin if (array_asc [0 :WA/2-1] !== {WA/2*WB +0{1'b1}}) $stop(); end + else if (cnt[30:2]==3) begin if (array_asc [WA/2:WA -1] !== {WA/2*WB +0{1'b1}}) $stop(); end + else if (cnt[30:2]==4) begin if (array_asc [0 ] !== {1 *WB +0{1'b1}}) $stop(); end + else if (cnt[30:2]==5) begin if (array_asc [ WA -1] !== {1 *WB +0{1'b1}}) $stop(); end + else if (cnt[30:2]==6) begin if (array_asc [0 ][0 :WB/2-1] !== {1 *WB/2+0{1'b1}}) $stop(); end + else if (cnt[30:2]==7) begin if (array_asc [ WA -1][WB/2:WB -1] !== {1 *WB/2+0{1'b1}}) $stop(); end + else if (cnt[30:2]==8) begin if (array_asc [0 ][0 ] !== {1 *1 +0{1'b1}}) $stop(); end + else if (cnt[30:2]==9) begin if (array_asc [ WA -1][ WB -1] !== {1 *1 +0{1'b1}}) $stop(); end end endmodule diff --git a/test_regress/t/t_array_pattern_packed.v b/test_regress/t/t_array_pattern_packed.v index 8023dae9f..0c3eff320 100644 --- a/test_regress/t/t_array_pattern_packed.v +++ b/test_regress/t/t_array_pattern_packed.v @@ -11,7 +11,7 @@ module t (/*AUTOARG*/ input clk; - logic [1:0] [3:0] [3:0] array_simp; // big endian array + logic [1:0] [3:0] [3:0] array_simp; // descending range array logic [3:0] array_oned; @@ -61,10 +61,10 @@ module t (/*AUTOARG*/ localparam NO = 11; // number of access events // 2D packed arrays - logic [WA-1:0] [WB-1:0] array_bg; // big endian array - /* verilator lint_off LITENDIAN */ - logic [0:WA-1] [0:WB-1] array_lt; // little endian array - /* verilator lint_on LITENDIAN */ + logic [WA-1:0] [WB-1:0] array_dsc; // descending range array + /* verilator lint_off ASCRANGE */ + logic [0:WA-1] [0:WB-1] array_asc; // ascending range array + /* verilator lint_on ASCRANGE */ integer cnt = 0; @@ -80,74 +80,74 @@ module t (/*AUTOARG*/ $finish; end - // big endian + // descending range always @ (posedge clk) if (cnt[1:0]==2'd0) begin // initialize to defaults (all bits 1'b0) - if (cnt[30:2]== 0) array_bg <= '0; - else if (cnt[30:2]== 1) array_bg <= '0; - else if (cnt[30:2]== 2) array_bg <= '0; - else if (cnt[30:2]== 3) array_bg <= '0; - else if (cnt[30:2]== 4) array_bg <= '0; - else if (cnt[30:2]== 5) array_bg <= '0; - else if (cnt[30:2]== 6) array_bg <= '0; - else if (cnt[30:2]== 7) array_bg <= '0; - else if (cnt[30:2]== 8) array_bg <= '0; - else if (cnt[30:2]== 9) array_bg <= '0; - else if (cnt[30:2]==10) array_bg <= '0; + if (cnt[30:2]== 0) array_dsc <= '0; + else if (cnt[30:2]== 1) array_dsc <= '0; + else if (cnt[30:2]== 2) array_dsc <= '0; + else if (cnt[30:2]== 3) array_dsc <= '0; + else if (cnt[30:2]== 4) array_dsc <= '0; + else if (cnt[30:2]== 5) array_dsc <= '0; + else if (cnt[30:2]== 6) array_dsc <= '0; + else if (cnt[30:2]== 7) array_dsc <= '0; + else if (cnt[30:2]== 8) array_dsc <= '0; + else if (cnt[30:2]== 9) array_dsc <= '0; + else if (cnt[30:2]==10) array_dsc <= '0; end else if (cnt[1:0]==2'd1) begin // write data into whole or part of the array using literals if (cnt[30:2]== 0) begin end - else if (cnt[30:2]== 1) array_bg <= '{ 3 ,2 ,1, 0 }; - else if (cnt[30:2]== 2) array_bg <= '{default:13}; - else if (cnt[30:2]== 3) array_bg <= '{0:4, 1:5, 2:6, 3:7}; - else if (cnt[30:2]== 4) array_bg <= '{2:15, default:13}; - else if (cnt[30:2]== 5) array_bg <= '{WA { {WB/2 {2'b10}} }}; - else if (cnt[30:2]== 6) array_bg <= '{cnt[3:0]+0, cnt[3:0]+1, cnt[3:0]+2, cnt[3:0]+3}; + else if (cnt[30:2]== 1) array_dsc <= '{ 3 ,2 ,1, 0 }; + else if (cnt[30:2]== 2) array_dsc <= '{default:13}; + else if (cnt[30:2]== 3) array_dsc <= '{0:4, 1:5, 2:6, 3:7}; + else if (cnt[30:2]== 4) array_dsc <= '{2:15, default:13}; + else if (cnt[30:2]== 5) array_dsc <= '{WA { {WB/2 {2'b10}} }}; + else if (cnt[30:2]== 6) array_dsc <= '{cnt[3:0]+0, cnt[3:0]+1, cnt[3:0]+2, cnt[3:0]+3}; end else if (cnt[1:0]==2'd2) begin // chack array agains expected value - if (cnt[30:2]== 0) begin if (array_bg !== 16'b0000000000000000) begin $display("%b", array_bg); $stop(); end end - else if (cnt[30:2]== 1) begin if (array_bg !== 16'b0011001000010000) begin $display("%b", array_bg); $stop(); end end - else if (cnt[30:2]== 2) begin if (array_bg !== 16'b1101110111011101) begin $display("%b", array_bg); $stop(); end end - else if (cnt[30:2]== 3) begin if (array_bg !== 16'b0111011001010100) begin $display("%b", array_bg); $stop(); end end - else if (cnt[30:2]== 4) begin if (array_bg !== 16'b1101111111011101) begin $display("%b", array_bg); $stop(); end end - else if (cnt[30:2]== 5) begin if (array_bg !== 16'b1010101010101010) begin $display("%b", array_bg); $stop(); end end - else if (cnt[30:2]== 6) begin if (array_bg !== 16'b1001101010111100) begin $display("%b", array_bg); $stop(); end end + if (cnt[30:2]== 0) begin if (array_dsc !== 16'b0000000000000000) begin $display("%b", array_dsc); $stop(); end end + else if (cnt[30:2]== 1) begin if (array_dsc !== 16'b0011001000010000) begin $display("%b", array_dsc); $stop(); end end + else if (cnt[30:2]== 2) begin if (array_dsc !== 16'b1101110111011101) begin $display("%b", array_dsc); $stop(); end end + else if (cnt[30:2]== 3) begin if (array_dsc !== 16'b0111011001010100) begin $display("%b", array_dsc); $stop(); end end + else if (cnt[30:2]== 4) begin if (array_dsc !== 16'b1101111111011101) begin $display("%b", array_dsc); $stop(); end end + else if (cnt[30:2]== 5) begin if (array_dsc !== 16'b1010101010101010) begin $display("%b", array_dsc); $stop(); end end + else if (cnt[30:2]== 6) begin if (array_dsc !== 16'b1001101010111100) begin $display("%b", array_dsc); $stop(); end end end - // little endian + // ascending range always @ (posedge clk) if (cnt[1:0]==2'd0) begin // initialize to defaults (all bits 1'b0) - if (cnt[30:2]== 0) array_lt <= '0; - else if (cnt[30:2]== 1) array_lt <= '0; - else if (cnt[30:2]== 2) array_lt <= '0; - else if (cnt[30:2]== 3) array_lt <= '0; - else if (cnt[30:2]== 4) array_lt <= '0; - else if (cnt[30:2]== 5) array_lt <= '0; - else if (cnt[30:2]== 6) array_lt <= '0; - else if (cnt[30:2]== 7) array_lt <= '0; - else if (cnt[30:2]== 8) array_lt <= '0; - else if (cnt[30:2]== 9) array_lt <= '0; - else if (cnt[30:2]==10) array_lt <= '0; + if (cnt[30:2]== 0) array_asc <= '0; + else if (cnt[30:2]== 1) array_asc <= '0; + else if (cnt[30:2]== 2) array_asc <= '0; + else if (cnt[30:2]== 3) array_asc <= '0; + else if (cnt[30:2]== 4) array_asc <= '0; + else if (cnt[30:2]== 5) array_asc <= '0; + else if (cnt[30:2]== 6) array_asc <= '0; + else if (cnt[30:2]== 7) array_asc <= '0; + else if (cnt[30:2]== 8) array_asc <= '0; + else if (cnt[30:2]== 9) array_asc <= '0; + else if (cnt[30:2]==10) array_asc <= '0; end else if (cnt[1:0]==2'd1) begin // write data into whole or part of the array using literals if (cnt[30:2]== 0) begin end - else if (cnt[30:2]== 1) array_lt <= '{ 3 ,2 ,1, 0 }; - else if (cnt[30:2]== 2) array_lt <= '{default:13}; - else if (cnt[30:2]== 3) array_lt <= '{3:4, 2:5, 1:6, 0:7}; - else if (cnt[30:2]== 4) array_lt <= '{1:15, default:13}; - else if (cnt[30:2]== 5) array_lt <= '{WA { {WB/2 {2'b10}} }}; - else if (cnt[30:2]==10) array_lt <= '{cnt[3:0]+0, cnt[3:0]+1, cnt[3:0]+2, cnt[3:0]+3}; + else if (cnt[30:2]== 1) array_asc <= '{ 3 ,2 ,1, 0 }; + else if (cnt[30:2]== 2) array_asc <= '{default:13}; + else if (cnt[30:2]== 3) array_asc <= '{3:4, 2:5, 1:6, 0:7}; + else if (cnt[30:2]== 4) array_asc <= '{1:15, default:13}; + else if (cnt[30:2]== 5) array_asc <= '{WA { {WB/2 {2'b10}} }}; + else if (cnt[30:2]==10) array_asc <= '{cnt[3:0]+0, cnt[3:0]+1, cnt[3:0]+2, cnt[3:0]+3}; end else if (cnt[1:0]==2'd2) begin // chack array agains expected value - if (cnt[30:2]== 0) begin if (array_lt !== 16'b0000000000000000) begin $display("%b", array_lt); $stop(); end end - else if (cnt[30:2]== 1) begin if (array_lt !== 16'b0011001000010000) begin $display("%b", array_lt); $stop(); end end - else if (cnt[30:2]== 2) begin if (array_lt !== 16'b1101110111011101) begin $display("%b", array_lt); $stop(); end end - else if (cnt[30:2]== 3) begin if (array_lt !== 16'b0111011001010100) begin $display("%b", array_lt); $stop(); end end - else if (cnt[30:2]== 4) begin if (array_lt !== 16'b1101111111011101) begin $display("%b", array_lt); $stop(); end end - else if (cnt[30:2]== 5) begin if (array_lt !== 16'b1010101010101010) begin $display("%b", array_lt); $stop(); end end - else if (cnt[30:2]==10) begin if (array_lt !== 16'b1001101010111100) begin $display("%b", array_lt); $stop(); end end + if (cnt[30:2]== 0) begin if (array_asc !== 16'b0000000000000000) begin $display("%b", array_asc); $stop(); end end + else if (cnt[30:2]== 1) begin if (array_asc !== 16'b0011001000010000) begin $display("%b", array_asc); $stop(); end end + else if (cnt[30:2]== 2) begin if (array_asc !== 16'b1101110111011101) begin $display("%b", array_asc); $stop(); end end + else if (cnt[30:2]== 3) begin if (array_asc !== 16'b0111011001010100) begin $display("%b", array_asc); $stop(); end end + else if (cnt[30:2]== 4) begin if (array_asc !== 16'b1101111111011101) begin $display("%b", array_asc); $stop(); end end + else if (cnt[30:2]== 5) begin if (array_asc !== 16'b1010101010101010) begin $display("%b", array_asc); $stop(); end end + else if (cnt[30:2]==10) begin if (array_asc !== 16'b1001101010111100) begin $display("%b", array_asc); $stop(); end end end endmodule diff --git a/test_regress/t/t_array_pattern_unpacked.v b/test_regress/t/t_array_pattern_unpacked.v index aaf1db6c4..60e710d92 100644 --- a/test_regress/t/t_array_pattern_unpacked.v +++ b/test_regress/t/t_array_pattern_unpacked.v @@ -6,7 +6,7 @@ module t (/*AUTOARG*/); - logic [3:0] array_simp [1:0] [3:0]; // big endian array + logic [3:0] array_simp [1:0] [3:0]; // descending range array int irep[1:2][1:6]; diff --git a/test_regress/t/t_array_query.v b/test_regress/t/t_array_query.v index b57a10494..963495080 100644 --- a/test_regress/t/t_array_query.v +++ b/test_regress/t/t_array_query.v @@ -40,9 +40,9 @@ module array_test input clk; - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE reg [7:0] a [LEFT:RIGHT]; - // verilator lint_on LITENDIAN + // verilator lint_on ASCRANGE typedef reg [7:0] r_t; diff --git a/test_regress/t/t_array_rev.v b/test_regress/t/t_array_rev.v index 47594bf5f..6d06bbd9d 100644 --- a/test_regress/t/t_array_rev.v +++ b/test_regress/t/t_array_rev.v @@ -12,9 +12,9 @@ module t (/*AUTOARG*/ input clk; integer cyc = 0; - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE logic arrd [0:1] = '{ 1'b1, 1'b0 }; - // verilator lint_on LITENDIAN + // verilator lint_on ASCRANGE logic y0, y1; logic localbkw [1:0]; diff --git a/test_regress/t/t_assert_property_pexpr_unsup.out b/test_regress/t/t_assert_property_pexpr_unsup.out new file mode 100644 index 000000000..882db1882 --- /dev/null +++ b/test_regress/t/t_assert_property_pexpr_unsup.out @@ -0,0 +1,113 @@ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:25:13: Unsupported: strong (in property expression) + 25 | strong(a); + | ^ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:29:11: Unsupported: weak (in property expression) + 29 | weak(a); + | ^ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:33:9: Unsupported: until (in property expression) + 33 | a until b; + | ^~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:37:9: Unsupported: s_until (in property expression) + 37 | a s_until b; + | ^~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:41:9: Unsupported: until_with (in property expression) + 41 | a until_with b; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:45:9: Unsupported: s_until_with (in property expression) + 45 | a s_until_with b; + | ^~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:49:9: Unsupported: implies (in property expression) + 49 | a implies b; + | ^~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:53:9: Unsupported: #-# (in property expression) + 53 | a #-# b; + | ^~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:57:9: Unsupported: #=# (in property expression) + 57 | a #=# b; + | ^~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:61:7: Unsupported: nexttime (in property expression) + 61 | nexttime a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:65:7: Unsupported: nexttime[] (in property expression) + 65 | nexttime [2] a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:69:7: Unsupported: s_nexttime (in property expression) + 69 | s_nexttime a; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:73:7: Unsupported: s_nexttime[] (in property expression) + 73 | s_nexttime [2] a; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:77:16: Unsupported: always (in property expression) + 77 | nexttime always a; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:77:7: Unsupported: nexttime (in property expression) + 77 | nexttime always a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:81:20: Unsupported: always (in property expression) + 81 | nexttime [2] always a; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:81:7: Unsupported: nexttime[] (in property expression) + 81 | nexttime [2] always a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:85:16: Unsupported: eventually (in property expression) + 85 | nexttime eventually a; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:85:7: Unsupported: nexttime (in property expression) + 85 | nexttime eventually a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:89:20: Unsupported: always (in property expression) + 89 | nexttime [2] always a; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:89:7: Unsupported: nexttime[] (in property expression) + 89 | nexttime [2] always a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:93:16: Unsupported: s_eventually (in property expression) + 93 | nexttime s_eventually a; + | ^~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:93:7: Unsupported: nexttime (in property expression) + 93 | nexttime s_eventually a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:97:35: Unsupported: always (in property expression) + 97 | nexttime s_eventually [2:$] always a; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:97:16: Unsupported: s_eventually[] (in property expression) + 97 | nexttime s_eventually [2:$] always a; + | ^~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:97:7: Unsupported: nexttime (in property expression) + 97 | nexttime s_eventually [2:$] always a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:101:17: Unsupported: accept_on (in property expression) + 101 | accept_on (a) b; + | ^ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:105:22: Unsupported: sync_accept_on (in property expression) + 105 | sync_accept_on (a) b; + | ^ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:109:17: Unsupported: reject_on (in property expression) + 109 | reject_on (a) b; + | ^ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:113:22: Unsupported: sync_reject_on (in property expression) + 113 | sync_reject_on (a) b; + | ^ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:117:9: Unsupported: iff (in property expression) + 117 | a iff b; + | ^~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:120:27: Unsupported: property argument data type + 120 | property p_arg_propery(property inprop); + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:123:27: Unsupported: sequence argument data type + 123 | property p_arg_seqence(sequence inseq); + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:128:7: Unsupported: property case expression + 128 | case (a) endcase + | ^~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:131:7: Unsupported: property case expression + 131 | case (a) default: b; endcase + | ^~~~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:134:7: Unsupported: property case expression + 134 | if (a) b + | ^~ +%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:137:7: Unsupported: property case expression + 137 | if (a) b else c + | ^~ +%Error: Exiting due to diff --git a/test_regress/t/t_assert_property_pexpr_unsup.pl b/test_regress/t/t_assert_property_pexpr_unsup.pl new file mode 100755 index 000000000..c9ca914d8 --- /dev/null +++ b/test_regress/t/t_assert_property_pexpr_unsup.pl @@ -0,0 +1,20 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 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 + +scenarios(vlt => 1); + +compile( + expect_filename => $Self->{golden_filename}, + verilator_flags2 => ['--assert --error-limit 1000'], + fails => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_assert_property_pexpr_unsup.v b/test_regress/t/t_assert_property_pexpr_unsup.v new file mode 100644 index 000000000..ec806a492 --- /dev/null +++ b/test_regress/t/t_assert_property_pexpr_unsup.v @@ -0,0 +1,146 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + int a; + int b; + int c; + int cyc = 0; + + always @(posedge clk) begin + cyc <= cyc + 1; + end + + // NOTE this grammar hasn't been checked with other simulators, + // is here just to avoid uncovered code lines in the grammar. + property p_strong; + strong(a); + endproperty + + property p_weak; + weak(a); + endproperty + + property p_until; + a until b; + endproperty + + property p_suntil; + a s_until b; + endproperty + + property p_untilwith; + a until_with b; + endproperty + + property p_suntilwith; + a s_until_with b; + endproperty + + property p_implies; + a implies b; + endproperty + + property p_poundminuspound1; + a #-# b; + endproperty + + property p_poundeqpound; + a #=# b; + endproperty + + property p_nexttime; + nexttime a; + endproperty + + property p_nexttime2; + nexttime [2] a; + endproperty + + property p_snexttime; + s_nexttime a; + endproperty + + property p_snexttime2; + s_nexttime [2] a; + endproperty + + property p_nexttime_always; + nexttime always a; + endproperty + + property p_nexttime_always2; + nexttime [2] always a; + endproperty + + property p_nexttime_eventually; + nexttime eventually a; + endproperty + + property p_nexttime_eventually2; + nexttime [2] always a; + endproperty + + property p_nexttime_seventually; + nexttime s_eventually a; + endproperty + + property p_nexttime_seventually2; + nexttime s_eventually [2:$] always a; + endproperty + + property p_accepton; + accept_on (a) b; + endproperty + + property p_syncaccepton; + sync_accept_on (a) b; + endproperty + + property p_rejecton; + reject_on (a) b; + endproperty + + property p_syncrejecton; + sync_reject_on (a) b; + endproperty + + property p_iff; + a iff b; + endproperty + + property p_arg_propery(property inprop); + inprop; + endproperty + property p_arg_seqence(sequence inseq); + inseq; + endproperty + + property p_case_1; + case (a) endcase + endproperty + property p_case_2; + case (a) default: b; endcase + endproperty + property p_if; + if (a) b + endproperty + property p_ifelse; + if (a) b else c + endproperty + + always @(posedge clk) begin + if (cyc == 10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_assert_recursive_property_unsup.out b/test_regress/t/t_assert_recursive_property_unsup.out index fbca8fd7c..7a2e9e68b 100644 --- a/test_regress/t/t_assert_recursive_property_unsup.out +++ b/test_regress/t/t_assert_recursive_property_unsup.out @@ -1,6 +1,6 @@ -%Error-UNSUPPORTED: t/t_assert_recursive_property_unsup.v:20:4: Unsupported: Recursive property call: 'check' - : ... In instance t +%Error-UNSUPPORTED: t/t_assert_recursive_property_unsup.v:20:13: Unsupported: Recursive property call: 'check' + : ... In instance t 20 | property check(int n); - | ^~~~~~~~ + | ^~~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest %Error: Exiting due to diff --git a/test_regress/t/t_assert_sampled.v b/test_regress/t/t_assert_sampled.v index 49c7cc71e..27b2e9b70 100644 --- a/test_regress/t/t_assert_sampled.v +++ b/test_regress/t/t_assert_sampled.v @@ -4,6 +4,8 @@ // any use, without warranty, 2022 by Antmicro Ltd. // SPDX-License-Identifier: CC0-1.0 +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0); + module t (/*AUTOARG*/ // Inputs clk @@ -14,6 +16,7 @@ module t (/*AUTOARG*/ Test1 t1(clk, a, b); Test2 t2(clk, a, b); + Test3 t3(clk, a, b); initial begin a = 0; @@ -54,3 +57,33 @@ module Test2( assert property (@(posedge clk) a == b); endmodule + +module Test3( + clk, a, b + ); + + input clk; + input [3:0] a, b; + + int hits[10]; + + assert property (@(posedge clk) a == b) hits[1]=1; + assert property (@(posedge clk) a == b) else hits[2]=1; + assert property (@(posedge clk) a == b) hits[3]=1; else hits[4]=1; + + assert property (@(posedge clk) a != b) hits[5]=1; + assert property (@(posedge clk) a != b) else hits[6]=1; + assert property (@(posedge clk) a != b) hits[7]=1; else hits[8]=1; + + final begin + `checkd(hits[1], 1); + `checkd(hits[2], 0); + `checkd(hits[3], 1); + `checkd(hits[4], 0); + `checkd(hits[5], 0); + `checkd(hits[6], 1); + `checkd(hits[7], 0); + `checkd(hits[8], 1); + end + +endmodule diff --git a/test_regress/t/t_assert_synth.v b/test_regress/t/t_assert_synth.v index f9d10ea2d..9eeda9ac7 100644 --- a/test_regress/t/t_assert_synth.v +++ b/test_regress/t/t_assert_synth.v @@ -111,4 +111,9 @@ module t (/*AUTOARG*/ end end + initial begin : test_info + $info ("Start of $info test"); + $info ("Middle of $info test"); + $info ("End of $info test"); + end : test_info endmodule diff --git a/test_regress/t/t_assert_synth_full.out b/test_regress/t/t_assert_synth_full.out index cf081f789..2ba86e3b5 100644 --- a/test_regress/t/t_assert_synth_full.out +++ b/test_regress/t/t_assert_synth_full.out @@ -1,3 +1,6 @@ +[0] -Info: t_assert_synth.v:115: top.t.test_info: Start of $info test +[0] -Info: t_assert_synth.v:116: top.t.test_info: Middle of $info test +[0] -Info: t_assert_synth.v:117: top.t.test_info: End of $info test [40] %Error: t_assert_synth.v:31: Assertion failed in top.t: synthesis full_case, but non-match found %Error: t/t_assert_synth.v:31: Verilog $stop Aborting... diff --git a/test_regress/t/t_assert_synth_full_vlt.out b/test_regress/t/t_assert_synth_full_vlt.out index 72ae3003f..a9e2a1ef4 100644 --- a/test_regress/t/t_assert_synth_full_vlt.out +++ b/test_regress/t/t_assert_synth_full_vlt.out @@ -1,3 +1,6 @@ +[0] -Info: t_assert_synth.v:115: top.t.test_info: Start of $info test +[0] -Info: t_assert_synth.v:116: top.t.test_info: Middle of $info test +[0] -Info: t_assert_synth.v:117: top.t.test_info: End of $info test [40] %Error: t_assert_synth.v:40: Assertion failed in top.t: synthesis full_case, but non-match found %Error: t/t_assert_synth.v:40: Verilog $stop Aborting... diff --git a/test_regress/t/t_assert_synth_parallel.out b/test_regress/t/t_assert_synth_parallel.out index 6bf8c8773..6ba0e76e8 100644 --- a/test_regress/t/t_assert_synth_parallel.out +++ b/test_regress/t/t_assert_synth_parallel.out @@ -1,3 +1,6 @@ +[0] -Info: t_assert_synth.v:115: top.t.test_info: Start of $info test +[0] -Info: t_assert_synth.v:116: top.t.test_info: Middle of $info test +[0] -Info: t_assert_synth.v:117: top.t.test_info: End of $info test [40] %Error: t_assert_synth.v:50: Assertion failed in top.t: synthesis parallel_case, but multiple matches found %Error: t/t_assert_synth.v:50: Verilog $stop Aborting... diff --git a/test_regress/t/t_assert_synth_parallel_vlt.out b/test_regress/t/t_assert_synth_parallel_vlt.out index f0adb9c21..b406ba1b3 100644 --- a/test_regress/t/t_assert_synth_parallel_vlt.out +++ b/test_regress/t/t_assert_synth_parallel_vlt.out @@ -1,3 +1,6 @@ +[0] -Info: t_assert_synth.v:115: top.t.test_info: Start of $info test +[0] -Info: t_assert_synth.v:116: top.t.test_info: Middle of $info test +[0] -Info: t_assert_synth.v:117: top.t.test_info: End of $info test [40] %Error: t_assert_synth.v:55: Assertion failed in top.t: synthesis parallel_case, but multiple matches found %Error: t/t_assert_synth.v:55: Verilog $stop Aborting... diff --git a/test_regress/t/t_class_assign_bad.out b/test_regress/t/t_class_assign_bad.out index f93dffe76..38ab6fe11 100644 --- a/test_regress/t/t_class_assign_bad.out +++ b/test_regress/t/t_class_assign_bad.out @@ -1,17 +1,33 @@ -%Error: t/t_class_assign_bad.v:16:9: Assign RHS expects a CLASSREFDTYPE 'Cls' +%Error: t/t_class_assign_bad.v:25:9: Assign RHS expects a CLASSREFDTYPE 'Cls', got BASICDTYPE 'logic' : ... In instance t - 16 | c = 0; + 25 | c = 0; | ^ -%Error: t/t_class_assign_bad.v:17:9: Assign RHS expects a CLASSREFDTYPE 'Cls' +%Error: t/t_class_assign_bad.v:26:9: Assign RHS expects a CLASSREFDTYPE 'Cls', got BASICDTYPE 'logic' : ... In instance t - 17 | c = 1; + 26 | c = 1; | ^ -%Error: t/t_class_assign_bad.v:18:7: Function Argument expects a CLASSREFDTYPE 'Cls' +%Error: t/t_class_assign_bad.v:27:9: Assign RHS expects a CLASSREFDTYPE 'Cls', got CLASSREFDTYPE 'Cls2' : ... In instance t - 18 | t(0); + 27 | c = c2; + | ^ +%Error: t/t_class_assign_bad.v:28:13: Assign RHS expects a CLASSREFDTYPE 'ClsExt', got CLASSREFDTYPE 'Cls' + : ... In instance t + 28 | c_ext = c; + | ^ +%Error: t/t_class_assign_bad.v:30:7: Function Argument expects a CLASSREFDTYPE 'Cls', got BASICDTYPE 'logic' + : ... In instance t + 30 | t(0); | ^ -%Error: t/t_class_assign_bad.v:19:7: Function Argument expects a CLASSREFDTYPE 'Cls' +%Error: t/t_class_assign_bad.v:31:7: Function Argument expects a CLASSREFDTYPE 'Cls', got BASICDTYPE 'logic' : ... In instance t - 19 | t(1); + 31 | t(1); + | ^ +%Error: t/t_class_assign_bad.v:32:7: Function Argument expects a CLASSREFDTYPE 'Cls', got CLASSREFDTYPE 'Cls2' + : ... In instance t + 32 | t(c2); + | ^ +%Error: t/t_class_assign_bad.v:33:7: Function Argument expects a CLASSREFDTYPE 'ClsExt', got CLASSREFDTYPE 'Cls' + : ... In instance t + 33 | f(c); | ^ %Error: Exiting due to diff --git a/test_regress/t/t_class_assign_bad.v b/test_regress/t/t_class_assign_bad.v index 97b7b9f46..46045ee5c 100644 --- a/test_regress/t/t_class_assign_bad.v +++ b/test_regress/t/t_class_assign_bad.v @@ -7,15 +7,29 @@ class Cls; endclass : Cls +class Cls2; +endclass + +class ClsExt extends Cls; +endclass + module t (/*AUTOARG*/); Cls c; + Cls2 c2; + ClsExt c_ext; task t(Cls c); endtask + function f(ClsExt c); endfunction initial begin c = 0; c = 1; + c = c2; + c_ext = c; + t(0); t(1); + t(c2); + f(c); end endmodule diff --git a/test_regress/t/t_class_extends2.out b/test_regress/t/t_class_extends2.out deleted file mode 100644 index 9b587f905..000000000 --- a/test_regress/t/t_class_extends2.out +++ /dev/null @@ -1,5 +0,0 @@ -%Error-UNSUPPORTED: t/t_class_extends2.v:18:19: Unsupported: Hierarchical class references - 18 | class Ext extends Pkg::Base0; - | ^~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to diff --git a/test_regress/t/t_class_extends2.pl b/test_regress/t/t_class_extends2.pl index 8a9b721f2..696e24d17 100755 --- a/test_regress/t/t_class_extends2.pl +++ b/test_regress/t/t_class_extends2.pl @@ -8,11 +8,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # Version 2.0. # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -scenarios(linter => 1); +scenarios(simulator => 1); -lint( - fails => $Self->{vlt}, - expect_filename => $Self->{golden_filename}, +compile( + make_flags => 'VM_PARALLEL_BUILDS=1', # bug2775 + ); + +execute( + check_finished => 1, ); ok(1); diff --git a/test_regress/t/t_class_extends_colon.out b/test_regress/t/t_class_extends_colon.out deleted file mode 100644 index ae67bbf79..000000000 --- a/test_regress/t/t_class_extends_colon.out +++ /dev/null @@ -1,5 +0,0 @@ -%Error-UNSUPPORTED: t/t_class_extends_colon.v:20:21: Unsupported: Hierarchical class references - 20 | class Cls12 extends Pkg::Icls1; - | ^~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to diff --git a/test_regress/t/t_class_extends_colon.pl b/test_regress/t/t_class_extends_colon.pl index 8a9b721f2..696e24d17 100755 --- a/test_regress/t/t_class_extends_colon.pl +++ b/test_regress/t/t_class_extends_colon.pl @@ -8,11 +8,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # Version 2.0. # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -scenarios(linter => 1); +scenarios(simulator => 1); -lint( - fails => $Self->{vlt}, - expect_filename => $Self->{golden_filename}, +compile( + make_flags => 'VM_PARALLEL_BUILDS=1', # bug2775 + ); + +execute( + check_finished => 1, ); ok(1); diff --git a/test_regress/t/t_class_extends_int_param_bad.out b/test_regress/t/t_class_extends_int_param_bad.out index 8f75bf3af..e16b75da5 100644 --- a/test_regress/t/t_class_extends_int_param_bad.out +++ b/test_regress/t/t_class_extends_int_param_bad.out @@ -1,5 +1,5 @@ %Error: t/t_class_extends_int_param_bad.v:9:23: Attempting to extend using non-class : ... In instance t - 9 | class bar #(type T=int) extends T; + 9 | class Bar #(type T=int) extends T; | ^~~ %Error: Exiting due to diff --git a/test_regress/t/t_class_extends_int_param_bad.v b/test_regress/t/t_class_extends_int_param_bad.v index f5ee566eb..062bb014c 100644 --- a/test_regress/t/t_class_extends_int_param_bad.v +++ b/test_regress/t/t_class_extends_int_param_bad.v @@ -6,10 +6,11 @@ module t (/*AUTOARG*/); - class bar #(type T=int) extends T; + class Bar #(type T=int) extends T; endclass initial begin + Bar#() bar; $stop; end endmodule diff --git a/test_regress/t/t_class_extends_param.v b/test_regress/t/t_class_extends_param.v index 0d5e8cf36..aaaedce8a 100644 --- a/test_regress/t/t_class_extends_param.v +++ b/test_regress/t/t_class_extends_param.v @@ -30,7 +30,7 @@ module t (/*AUTOARG*/ endfunction endclass - class ExtendBar extends Bar; + class ExtendBar extends Bar#(); function int get_x; return super.get_x(); endfunction @@ -39,17 +39,51 @@ module t (/*AUTOARG*/ endfunction endclass - Bar bar_foo_i; + class ExtendBar1 extends Bar #(Foo); + function int get_x; + return super.get_x(); + endfunction + function int get_6; + return 2 * get_3(); + endfunction + endclass + + class ExtendBarBaz extends Bar #(Baz); + function int get_x; + return super.get_x(); + endfunction + function int get_8; + return 2 * get_4(); + endfunction + endclass + + class ExtendExtendBar extends ExtendBar; + function int get_12; + return 4 * get_3(); + endfunction + endclass + + Bar #() bar_foo_i; Bar #(Baz) bar_baz_i; ExtendBar extend_bar_i; + ExtendBar1 extend_bar1_i; + ExtendBarBaz extend_bar_baz_i; + ExtendExtendBar extend_extend_bar_i; initial begin bar_foo_i = new; bar_baz_i = new; extend_bar_i = new; + extend_bar1_i = new; + extend_bar_baz_i = new; + extend_extend_bar_i = new; if (bar_foo_i.get_x() == 1 && bar_foo_i.get_3() == 3 && bar_baz_i.get_x() == 2 && bar_baz_i.get_4() == 4 && - extend_bar_i.get_x() == 1 && extend_bar_i.get_6() == 6) begin + extend_bar_i.get_x() == 1 && extend_bar_i.get_6() == 6 && + extend_bar_i.get_x() == 1 && extend_bar_i.get_6() == 6 && + extend_bar1_i.get_x() == 1 && extend_bar1_i.get_6() == 6 && + extend_bar_baz_i.get_x() == 2 && extend_bar_baz_i.get_8() == 8 && + extend_extend_bar_i.get_x() == 1 && extend_extend_bar_i.get_12() == 12) begin $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_class_extends_param_unused.pl b/test_regress/t/t_class_extends_param_unused.pl new file mode 100755 index 000000000..aabcde63e --- /dev/null +++ b/test_regress/t/t_class_extends_param_unused.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2020 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_class_extends_param_unused.v b/test_regress/t/t_class_extends_param_unused.v new file mode 100644 index 000000000..fcc46a865 --- /dev/null +++ b/test_regress/t/t_class_extends_param_unused.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, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +class Foo#(type T = logic) extends T; +endclass + +module t (/*AUTOARG*/); + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_extends_vsyment.pl b/test_regress/t/t_class_extends_vsyment.pl new file mode 100755 index 000000000..8eeb2a6f6 --- /dev/null +++ b/test_regress/t/t_class_extends_vsyment.pl @@ -0,0 +1,17 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2020 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 + +scenarios(vlt => 1); + +compile( + ); + +ok(1); +1; diff --git a/test_regress/t/t_class_extends_vsyment.v b/test_regress/t/t_class_extends_vsyment.v new file mode 100644 index 000000000..ac17ba1cf --- /dev/null +++ b/test_regress/t/t_class_extends_vsyment.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, 2022 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +class Foo; +endclass + +class Bar extends Foo; + int m_field = get_1(); + function int get_1(); + return 1; + endfunction +endclass diff --git a/test_regress/t/t_class_field_name.pl b/test_regress/t/t_class_field_name.pl new file mode 100755 index 000000000..aabcde63e --- /dev/null +++ b/test_regress/t/t_class_field_name.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2020 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_class_field_name.v b/test_regress/t/t_class_field_name.v new file mode 100644 index 000000000..af7a73828 --- /dev/null +++ b/test_regress/t/t_class_field_name.v @@ -0,0 +1,21 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +class Cls; + int queue; +endclass + +module t (/*AUTOARG*/); + + initial begin + Cls cls = new; + cls.queue = 1; + if (cls.queue == 1) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_class_fwd_cc.v b/test_regress/t/t_class_fwd_cc.v index dce477fdf..cfb6c3696 100644 --- a/test_regress/t/t_class_fwd_cc.v +++ b/test_regress/t/t_class_fwd_cc.v @@ -15,7 +15,7 @@ package Pkg; endfunction endclass class Fwd; - function Fwd m_uvm_get_root(); + static function Fwd m_uvm_get_root(); return null; endfunction endclass diff --git a/test_regress/t/t_class_inc.pl b/test_regress/t/t_class_inc.pl new file mode 100755 index 000000000..859050d63 --- /dev/null +++ b/test_regress/t/t_class_inc.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2023 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_class_inc.v b/test_regress/t/t_class_inc.v new file mode 100644 index 000000000..446df0c3f --- /dev/null +++ b/test_regress/t/t_class_inc.v @@ -0,0 +1,37 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class Base #(type T = integer); + T m_count; + + function void test1(); + if (this.m_count != 0) $stop; + if (this.m_count++ != 0) $stop; + if (this.m_count != 1) $stop; + if (m_count++ != 1) $stop; + if (this.m_count != 2) $stop; + endfunction +endclass + +class Cls #(type T = integer) extends Base #(T); +endclass + +module t; + Cls #(int) c; + + initial begin + c = new; + c.test1(); + + c.m_count = 0; + if (c.m_count != 0) $stop; + if (c.m_count++ != 0) $stop; + if (c.m_count != 1) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_member_bad3.out b/test_regress/t/t_class_member_bad3.out new file mode 100644 index 000000000..4712e0838 --- /dev/null +++ b/test_regress/t/t_class_member_bad3.out @@ -0,0 +1,7 @@ +%Error: t/t_class_member_bad3.v:16:12: Static access to non-static member variable 'member' + 16 | Foo::member = 1; + | ^~~~~~ +%Error: t/t_class_member_bad3.v:17:12: Static access to non-static task/function 'method' + 17 | Foo::method(); + | ^~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_class_member_bad3.pl b/test_regress/t/t_class_member_bad3.pl new file mode 100755 index 000000000..55ca436af --- /dev/null +++ b/test_regress/t/t_class_member_bad3.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2023 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(linter => 1); + +lint( + # fails => 1, # TODO bug4077 + # expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_class_member_bad3.v b/test_regress/t/t_class_member_bad3.v new file mode 100644 index 000000000..ccde97456 --- /dev/null +++ b/test_regress/t/t_class_member_bad3.v @@ -0,0 +1,19 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +class Foo; + int member; + + task method; endtask +endclass + +module t; + initial begin + Foo foo = new; + Foo::member = 1; + Foo::method(); + end +endmodule diff --git a/test_regress/t/t_class_method.v b/test_regress/t/t_class_method.v index feda390fd..0ecf6f832 100644 --- a/test_regress/t/t_class_method.v +++ b/test_regress/t/t_class_method.v @@ -15,6 +15,7 @@ endclass : Cls module t (/*AUTOARG*/); initial begin + int tmp_i; Cls c; if (c != null) $stop; c = new; @@ -24,6 +25,9 @@ module t (/*AUTOARG*/); if (c.get_methoda() != 20) $stop; c.setv_methoda(30); if (c.get_methoda() != 30) $stop; + c.setv_methoda(300); + tmp_i = c.get_methoda; + if (tmp_i != 300) $stop; $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_class_new.v b/test_regress/t/t_class_new.v index f15328470..9d625c5a5 100644 --- a/test_regress/t/t_class_new.v +++ b/test_regress/t/t_class_new.v @@ -7,8 +7,13 @@ class ClsNoArg; const int imembera; // Ok for new() to assign to a const function new(); + int other = other_func(); imembera = 5; + if (other != 6) $stop; endfunction : new + function int other_func(); + return 6; + endfunction endclass class ClsArg; diff --git a/test_regress/t/t_class_new_bad.out b/test_regress/t/t_class_new_bad.out index 425a7898e..454eae5d6 100644 --- a/test_regress/t/t_class_new_bad.out +++ b/test_regress/t/t_class_new_bad.out @@ -14,11 +14,7 @@ : ... In instance t 34 | c1 = new[2]; | ^~~ -%Error: t/t_class_new_bad.v:34:10: Assign RHS expects a CLASSREFDTYPE 'ClsNoArg' - : ... In instance t - 34 | c1 = new[2]; - | ^ -%Error: Internal Error: t/t_class_new_bad.v:34:10: ../V3Width.cpp:#: Node has no type +%Error: Internal Error: t/t_class_new_bad.v:34:12: ../V3Width.cpp:#: Node has no type : ... In instance t 34 | c1 = new[2]; - | ^ + | ^~~ diff --git a/test_regress/t/t_class_param_lvalue.pl b/test_regress/t/t_class_param_lvalue.pl new file mode 100755 index 000000000..13587bf85 --- /dev/null +++ b/test_regress/t/t_class_param_lvalue.pl @@ -0,0 +1,17 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2023 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(linter => 1); + +lint( + ); + +ok(1); +1; diff --git a/test_regress/t/t_class_param_lvalue.v b/test_regress/t/t_class_param_lvalue.v new file mode 100644 index 000000000..879693876 --- /dev/null +++ b/test_regress/t/t_class_param_lvalue.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, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +class Foo; +endclass + +class Bar #(type BASE=Foo) extends BASE; + task body(); + int v = 0; + v = 1; + endtask +endclass diff --git a/test_regress/t/t_class_param_override_local_bad.out b/test_regress/t/t_class_param_override_local_bad.out new file mode 100644 index 000000000..dacafe468 --- /dev/null +++ b/test_regress/t/t_class_param_override_local_bad.out @@ -0,0 +1,14 @@ +%Error-PINNOTFOUND: t/t_class_param_override_local_bad.v:23:30: Parameter pin not found: '__paramNumber1' + 23 | class Cls3 implements Icls1#(2), Icls2#(0); + | ^ + ... For error description see https://verilator.org/warn/PINNOTFOUND?v=latest +%Error-PINNOTFOUND: t/t_class_param_override_local_bad.v:23:41: Parameter pin not found: '__paramNumber1' + 23 | class Cls3 implements Icls1#(2), Icls2#(0); + | ^ +%Error-PINNOTFOUND: t/t_class_param_override_local_bad.v:29:23: Parameter pin not found: '__paramNumber1' + 29 | automatic Cls1#(bit) cls1 = new; + | ^~~ +%Error-PINNOTFOUND: t/t_class_param_override_local_bad.v:30:23: Parameter pin not found: '__paramNumber1' + 30 | automatic Cls2#(1) cls2 = new; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_gen_defparam_unsup_bad.pl b/test_regress/t/t_class_param_override_local_bad.pl similarity index 100% rename from test_regress/t/t_gen_defparam_unsup_bad.pl rename to test_regress/t/t_class_param_override_local_bad.pl diff --git a/test_regress/t/t_class_param_override_local_bad.v b/test_regress/t/t_class_param_override_local_bad.v new file mode 100644 index 000000000..a608fbefa --- /dev/null +++ b/test_regress/t/t_class_param_override_local_bad.v @@ -0,0 +1,34 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +class Cls1; + parameter type T = int; +endclass + +class Cls2; + localparam int P = 0; +endclass + +interface class Icls1; + localparam LP1 = 1; +endclass + +interface class Icls2; + parameter LP1 = 1; +endclass + +class Cls3 implements Icls1#(2), Icls2#(0); +endclass + +module t (/*AUTOARG*/); + + initial begin + automatic Cls1#(bit) cls1 = new; + automatic Cls2#(1) cls2 = new; + automatic Cls3 cls3 = new; + $stop; + end +endmodule diff --git a/test_regress/t/t_class_param_type.v b/test_regress/t/t_class_param_type.v index 1501f0819..24b7da7a7 100644 --- a/test_regress/t/t_class_param_type.v +++ b/test_regress/t/t_class_param_type.v @@ -1,7 +1,7 @@ // DESCRIPTION: Verilator: Verilog Test module // // This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2022 by Arkadiusz Kozdra. +// any use, without warranty, 2023 by Antmicro Ltd. // SPDX-License-Identifier: CC0-1.0 // See also t_class_param.v @@ -18,14 +18,147 @@ class Cls; endfunction endclass +class ParclsDefaultType #(type T=Cls); + static function int get_p; + return T::get_p(); + endfunction +endclass + typedef Cls cls_t; typedef cls_t cls2_t; +class Singleton #(type T=int); + static function Singleton#(T) self; + static Singleton#(T) c = new; + return c; + endfunction + function int get_size; + return $bits(T); + endfunction +endclass + +class SingletonUnusedDefault #(type T=int); + static function SingletonUnusedDefault#(T) self; + static SingletonUnusedDefault#(T) c = new; + return c; + endfunction +endclass + +class Empty; +endclass + +class Foo #(type IF=Empty) extends IF; + typedef Foo foo_t; + int a = 1; +endclass + +class Bar #(type A=int, type B=A) extends Foo#(); + function int get_size_A; + return $bits(A); + endfunction + function int get_size_B; + return $bits(B); + endfunction +endclass + +class Empty2; +endclass + +class Baz #(type T=Empty2) extends Foo#(); +endclass + +class Getter1 extends Baz#(); + function int get_1; + foo_t f = new; + return f.a; + endfunction +endclass + +class MyInt1; + int x = 1; +endclass + +class MyInt2; + int x = 2; +endclass + +class ExtendsMyInt #(type T=MyInt1) extends T; + typedef ExtendsMyInt#(T) this_type; + function int get_this_type_x; + this_type t = new; + return t.x; + endfunction +endclass + +class StaticX; + static int val = 1; +endclass + +class GetStaticXVal #(type T = int); + static function int get; + return T::val; + endfunction +endclass + module t (/*AUTOARG*/); initial begin + automatic ParclsDefaultType#(Cls) pdt1 = new; + automatic ParclsDefaultType#(cls_t) pdt2 = pdt1; + automatic ParclsDefaultType#(cls2_t) pdt3 = pdt2; + automatic Parcls#(Cls) p1 = new; + automatic Parcls#(cls_t) p2 = p1; + automatic Parcls#(cls2_t) p3 = p2; + + automatic Singleton #(int) s_int1 = Singleton#(int)::self(); + automatic Singleton #(int) s_int2 = Singleton#(int)::self(); + automatic Singleton #(bit) s_bit1 = Singleton#(bit)::self(); + automatic Singleton #(bit) s_bit2 = Singleton#(bit)::self(); + automatic SingletonUnusedDefault #(bit) sud1 = SingletonUnusedDefault#(bit)::self(); + automatic SingletonUnusedDefault #(bit) sud2 = SingletonUnusedDefault#(bit)::self(); + + automatic Getter1 getter1 = new; + + automatic ExtendsMyInt#() ext1 = new; + automatic ExtendsMyInt#(MyInt2) ext2 = new; + + automatic GetStaticXVal#(StaticX) get_statix_x_val = new; + + typedef bit my_bit_t; + Bar#(.A(my_bit_t)) bar_a_bit = new; + Bar#(.B(my_bit_t)) bar_b_bit = new; + Bar#() bar_default = new; + + if (bar_a_bit.get_size_A != 1) $stop; + if (bar_a_bit.get_size_B != 1) $stop; + if (bar_b_bit.get_size_A != 32) $stop; + if (bar_b_bit.get_size_B != 1) $stop; + if (bar_default.get_size_A != 32) $stop; + if (bar_default.get_size_B != 32) $stop; + + if (pdt1 != pdt2) $stop; + if (pdt2 != pdt3) $stop; + if (p1 != p2) $stop; + if (p2 != p3) $stop; + + if (s_int1 != s_int2) $stop; + if (s_bit1 != s_bit2) $stop; + if (sud1 != sud2) $stop; + + if (s_int1.get_size() != 32) $stop; + if (s_bit1.get_size() != 1) $stop; + + if (p1.get_p() != 20) $stop; + if (pdt1.get_p() != 20) $stop; if (Parcls#(cls2_t)::get_p() != 20) $stop; + if (getter1.get_1() != 1) $stop; + + if (ext1.get_this_type_x() != 1) $stop; + if (ext2.get_this_type_x() != 2) $stop; + + if (get_statix_x_val.get() != 1) $stop; + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_class_param_unused_default.pl b/test_regress/t/t_class_param_unused_default.pl new file mode 100755 index 000000000..aabcde63e --- /dev/null +++ b/test_regress/t/t_class_param_unused_default.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2020 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_class_param_unused_default.v b/test_regress/t/t_class_param_unused_default.v new file mode 100644 index 000000000..e031fa36f --- /dev/null +++ b/test_regress/t/t_class_param_unused_default.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +class Bar#(type T = int); + T t; + function new; + t = new; + endfunction +endclass + +class Baz; + int x = 1; +endclass + +module t (/*AUTOARG*/); + initial begin + Bar#(Baz) bar_baz = new; + if (bar_baz.t.x != 1) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_ref_as_arg_cast.pl b/test_regress/t/t_class_ref_as_arg_cast.pl new file mode 100755 index 000000000..8c2ca139b --- /dev/null +++ b/test_regress/t/t_class_ref_as_arg_cast.pl @@ -0,0 +1,17 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2023 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + ); + +ok(1); +1; diff --git a/test_regress/t/t_class_ref_as_arg_cast.v b/test_regress/t/t_class_ref_as_arg_cast.v new file mode 100644 index 000000000..d1d5f25d0 --- /dev/null +++ b/test_regress/t/t_class_ref_as_arg_cast.v @@ -0,0 +1,21 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +class Foo; + static task bar(Foo f); + endtask +endclass + +class Qux extends Foo; +endclass + +module t; + initial begin + Qux qux = new; + Foo::bar(qux); + Foo::bar(null); + end +endmodule diff --git a/test_regress/t/t_clk_concat3.v b/test_regress/t/t_clk_concat3.v index 87fb40db8..fe68657cb 100644 --- a/test_regress/t/t_clk_concat3.v +++ b/test_regress/t/t_clk_concat3.v @@ -4,7 +4,7 @@ // without warranty. // SPDX-License-Identifier: CC0-1.0 -/* verilator lint_off LITENDIAN */ +/* verilator lint_off ASCRANGE */ module some_module ( input wrclk ); diff --git a/test_regress/t/t_const_opt.pl b/test_regress/t/t_const_opt.pl index bf8d4b3a3..c01b55613 100755 --- a/test_regress/t/t_const_opt.pl +++ b/test_regress/t/t_const_opt.pl @@ -20,7 +20,7 @@ execute( ); if ($Self->{vlt}) { - file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 16); + file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 39); } ok(1); 1; diff --git a/test_regress/t/t_const_opt.v b/test_regress/t/t_const_opt.v index dcdc1419e..843bd8870 100644 --- a/test_regress/t/t_const_opt.v +++ b/test_regress/t/t_const_opt.v @@ -62,7 +62,7 @@ module t(/*AUTOARG*/ $write("[%0t] cyc==%0d crc=%x sum=%x\n", $time, cyc, crc, sum); if (crc !== 64'hc77bb9b3784ea091) $stop; // What checksum will we end up with (above print should match) -`define EXPECTED_SUM 64'he65eec57cf769267 +`define EXPECTED_SUM 64'hf5498264b93d4b48 if (sum !== `EXPECTED_SUM) $stop; $write("*-* All Finished *-*\n"); @@ -92,10 +92,11 @@ module Test(/*AUTOARG*/ wire bug3399_out1; logic bug3786_out; logic bug3824_out; + logic bug4059_out; output logic o; - logic [13:0] tmp; + logic [14:0] tmp; assign o = ^tmp; always_ff @(posedge clk) begin @@ -125,6 +126,7 @@ module Test(/*AUTOARG*/ tmp[11]<= bug3399_out1; tmp[12]<= bug3786_out; tmp[13]<= bug3824_out; + tmp[14]<= bug4059_out; end bug3182 i_bug3182(.in(d[4:0]), .out(bug3182_out)); @@ -135,6 +137,7 @@ module Test(/*AUTOARG*/ bug3399 i_bug3399(.clk(clk), .in(d), .out0(bug3399_out0), .out1(bug3399_out1)); bug3786 i_bug3786(.clk(clk), .in(d), .out(bug3786_out)); bug3824 i_bug3824(.clk(clk), .in(d), .out(bug3824_out)); + bug4059 i_bug4059(.clk(clk), .in(d), .out(bug4059_out)); endmodule @@ -364,3 +367,32 @@ module bug3824(input wire clk, input wire [31:0] in, output wire out); assign out = d_and ^ d_or ^ d_xor; endmodule + +/// Bug4059 +// Frozen node in an xor tree held unnecessary poloarity. +// In an XOR tree, the entire result is flipped if necessary according to +// total polarity. This bug was introduced when fixing #3445. +module bug4059(input wire clk, input wire [31:0] in, output wire out); + wire [127:0] words_i; + logic [127:0] words_i; + for (genvar i = 0; i < $bits(in); ++i) begin + always_ff @(posedge clk) + words_i[4 * i +: 4] <= {4{in[i]}}; + end + + wire _000_ = ~(words_i[104] ^ words_i[96]); + wire _001_ = ~(words_i[88] ^ words_i[80]); + wire _002_ = ~(_000_ ^ _001_); + wire _003_ = words_i[72] ^ words_i[64]; + wire _004_ = words_i[120] ^ words_i[112]; + wire _005_ = ~(_003_ ^ _004_); + wire _006_ = ~(_002_ ^ _005_); + wire _007_ = words_i[40] ^ words_i[32]; + wire _008_ = ~(words_i[24] ^ words_i[16]); + wire _009_ = ~(_007_ ^ _008_); + wire _010_ = words_i[8] ^ words_i[0]; + wire _011_ = words_i[56] ^ words_i[48]; + wire _012_ = ~(_010_ ^ _011_); + wire _013_ = ~(_009_ ^ _012_); + assign out = ~(_006_ ^ _013_); +endmodule diff --git a/test_regress/t/t_constraint.pl b/test_regress/t/t_constraint.pl new file mode 100755 index 000000000..11d21fc86 --- /dev/null +++ b/test_regress/t/t_constraint.pl @@ -0,0 +1,23 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2019 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 + +scenarios(simulator => 1); + +compile( + verilator_flags2 => ['-Wno-CONSTRAINTIGN'], + ); + +execute( + check_finished => 1, + ); + + +ok(1); +1; diff --git a/test_regress/t/t_constraint.v b/test_regress/t/t_constraint.v new file mode 100644 index 000000000..59dea5195 --- /dev/null +++ b/test_regress/t/t_constraint.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, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class Packet; + rand int one; + + constraint a { one > 0 && one < 2; } + +endclass + +module t (/*AUTOARG*/); + + Packet p; + + int v; + + initial begin + p = new; + v = p.randomize(); + if (v != 1) $stop; +`ifndef VERILATOR + if (p.one != 1) $stop; +`endif + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_constraint_mode.pl b/test_regress/t/t_constraint_mode.pl new file mode 100755 index 000000000..11d21fc86 --- /dev/null +++ b/test_regress/t/t_constraint_mode.pl @@ -0,0 +1,23 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2019 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 + +scenarios(simulator => 1); + +compile( + verilator_flags2 => ['-Wno-CONSTRAINTIGN'], + ); + +execute( + check_finished => 1, + ); + + +ok(1); +1; diff --git a/test_regress/t/t_constraint_mode.v b/test_regress/t/t_constraint_mode.v new file mode 100644 index 000000000..6d18156bc --- /dev/null +++ b/test_regress/t/t_constraint_mode.v @@ -0,0 +1,59 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class Packet; + rand int one; + + constraint a { one > 0 && one < 2; } + + task test1; + // TODO Verilator ignores this setting currently, always returning 1 (rand on) + one.rand_mode(0); + one.rand_mode(1); + if (one.rand_mode() != 1) $stop; + + // TODO Verilator ignores this setting currently, always returning 0 (constraint off) + a.constraint_mode(1); + a.constraint_mode(0); + if (a.constraint_mode() != 0) $stop; + endtask + +endclass + +module t (/*AUTOARG*/); + + Packet p; + + int v; + + initial begin + p = new; + v = p.randomize(); + if (v != 1) $stop; +`ifndef VERILATOR + if (p.one != 1) $stop; +`endif + + // TODO Verilator ignores this setting currently, always returning 1 (rand on) + p.one.rand_mode(0); + p.one.rand_mode(1); + if (p.one.rand_mode() != 1) $stop; + + // TODO Verilator ignores this setting currently, always returning 0 (constraint off) + p.a.constraint_mode(1); + p.a.constraint_mode(0); + if (p.a.constraint_mode() != 0) $stop; + + p.test1(); + + // TODO test can't redefine constraint_mode + // TODO test can't redefine rand_mode + // TODO test can't call constraint_mode on non-constraint + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_constraint_mode_warn_bad.out b/test_regress/t/t_constraint_mode_warn_bad.out new file mode 100644 index 000000000..478853c43 --- /dev/null +++ b/test_regress/t/t_constraint_mode_warn_bad.out @@ -0,0 +1,54 @@ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:10:4: Constraint ignored (unsupported) + 10 | constraint a { one > 0 && one < 2; } + | ^~~~~~~~~~ + ... 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_mode.v:14:11: rand_mode ignored (unsupported) + : ... In instance t + 14 | one.rand_mode(0); + | ^~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:15:11: rand_mode ignored (unsupported) + : ... In instance t + 15 | one.rand_mode(1); + | ^~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:16:15: rand_mode ignored (unsupported) + : ... In instance t + 16 | if (one.rand_mode() != 1) $stop; + | ^~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:19:9: constraint_mode ignored (unsupported) + : ... In instance t + 19 | a.constraint_mode(1); + | ^~~~~~~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:20:9: constraint_mode ignored (unsupported) + : ... In instance t + 20 | a.constraint_mode(0); + | ^~~~~~~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:21:13: constraint_mode ignored (unsupported) + : ... In instance t + 21 | if (a.constraint_mode() != 0) $stop; + | ^~~~~~~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:41:13: rand_mode ignored (unsupported) + : ... In instance t + 41 | p.one.rand_mode(0); + | ^~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:42:13: rand_mode ignored (unsupported) + : ... In instance t + 42 | p.one.rand_mode(1); + | ^~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:43:17: rand_mode ignored (unsupported) + : ... In instance t + 43 | if (p.one.rand_mode() != 1) $stop; + | ^~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:46:11: constraint_mode ignored (unsupported) + : ... In instance t + 46 | p.a.constraint_mode(1); + | ^~~~~~~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:47:11: constraint_mode ignored (unsupported) + : ... In instance t + 47 | p.a.constraint_mode(0); + | ^~~~~~~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:48:15: constraint_mode ignored (unsupported) + : ... In instance t + 48 | if (p.a.constraint_mode() != 0) $stop; + | ^~~~~~~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_constraint_mode_warn_bad.pl b/test_regress/t/t_constraint_mode_warn_bad.pl new file mode 100755 index 000000000..96addf7c7 --- /dev/null +++ b/test_regress/t/t_constraint_mode_warn_bad.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2019 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 + +scenarios(vlt => 1); + +top_filename("t/t_constraint_mode.v"); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_covergroup_unsup.out b/test_regress/t/t_covergroup_unsup.out new file mode 100644 index 000000000..6a6747e11 --- /dev/null +++ b/test_regress/t/t_covergroup_unsup.out @@ -0,0 +1,380 @@ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:25:4: 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 + 28 | covergroup cg_opt; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:29:7: Unsupported: coverage option + 29 | type_option.weight = 1; + | ^~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:30:7: Unsupported: coverage option + 30 | type_option.goal = 99; + | ^~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:31:7: Unsupported: coverage option + 31 | type_option.comment = "type_option_comment"; + | ^~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:32:7: Unsupported: coverage option + 32 | type_option.strobe = 0; + | ^~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:33:7: Unsupported: coverage option + 33 | type_option.merge_instances = 1; + | ^~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:34:7: Unsupported: coverage option + 34 | type_option.distribuge_first = 1; + | ^~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:35:7: Unsupported: coverage option + 35 | option.name = "the_name"; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:36:7: Unsupported: coverage option + 36 | option.weight = 1; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:37:7: Unsupported: coverage option + 37 | option.goal = 98; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:38:7: Unsupported: coverage option + 38 | option.comment = "option_comment"; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:39:7: Unsupported: coverage option + 39 | option.at_least = 20; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:40:7: Unsupported: coverage option + 40 | option.auto_bin_max = 10; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:41:7: Unsupported: coverage option + 41 | option.cross_num_print_missing = 2; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:42:7: Unsupported: coverage option + 42 | option.detect_overlap = 1; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:43:7: Unsupported: coverage option + 43 | option.per_instance = 1; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:44:7: Unsupported: coverage option + 44 | option.get_inst_coverage = 1; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:47:4: Unsupported: covergroup + 47 | covergroup cg_clockingevent() @(posedge clk); + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:47:34: Unsupported: coverage clocking event + 47 | covergroup cg_clockingevent() @(posedge clk); + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:49:4: Unsupported: covergroup + 49 | covergroup cg_withfunction() with function sample (a); + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:49:33: Unsupported: coverage 'with' 'function' + 49 | covergroup cg_withfunction() with function sample (a); + | ^~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:51:4: Unsupported: covergroup + 51 | covergroup cg_atat() @@ (begin funca or end funcb); + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:51:25: Unsupported: coverage '@@' events + 51 | covergroup cg_atat() @@ (begin funca or end funcb); + | ^~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:53:4: Unsupported: covergroup + 53 | covergroup cg_bracket; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:56:4: Unsupported: covergroup + 56 | covergroup cg_bracket; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:57:9: Unsupported: coverage option + 57 | { option.name = "option"; } + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:59:4: Unsupported: covergroup + 59 | covergroup cg_cp; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:60:7: Unsupported: cover point + 60 | coverpoint a; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:62:4: Unsupported: covergroup + 62 | covergroup cg_cp_iff; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:63:20: Unsupported: cover 'iff' + 63 | coverpoint a iff (b); + | ^~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:63:7: Unsupported: cover point + 63 | coverpoint a iff (b); + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:65:4: Unsupported: covergroup + 65 | covergroup cg_id_cp_iff; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:66:24: Unsupported: cover 'iff' + 66 | id: coverpoint a iff (b); + | ^~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:66:11: Unsupported: cover point + 66 | id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:68:4: Unsupported: covergroup + 68 | covergroup cg_id_cp_id1; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:69:28: Unsupported: cover 'iff' + 69 | int id: coverpoint a iff (b); + | ^~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:69:15: Unsupported: cover point + 69 | int id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:71:4: Unsupported: covergroup + 71 | covergroup cg_id_cp_id2; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:72:32: Unsupported: cover 'iff' + 72 | var int id: coverpoint a iff (b); + | ^~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:72:19: Unsupported: cover point + 72 | var int id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:74:4: Unsupported: covergroup + 74 | covergroup cg_id_cp_id3; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:75:34: Unsupported: cover 'iff' + 75 | var [3:0] id: coverpoint a iff (b); + | ^~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:75:21: Unsupported: cover point + 75 | var [3:0] id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:77:4: Unsupported: covergroup + 77 | covergroup cg_id_cp_id4; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:78:30: Unsupported: cover 'iff' + 78 | [3:0] id: coverpoint a iff (b); + | ^~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:78:17: Unsupported: cover point + 78 | [3:0] id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:80:4: Unsupported: covergroup + 80 | covergroup cg_id_cp_id5; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:81:31: Unsupported: cover 'iff' + 81 | signed id: coverpoint a iff (b); + | ^~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:81:18: Unsupported: cover point + 81 | signed id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:84:4: Unsupported: covergroup + 84 | covergroup cg_cross; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:85:18: Unsupported: cover 'iff' + 85 | cross a, b iff (!rst); + | ^~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:85:7: Unsupported: cross + 85 | cross a, b iff (!rst); + | ^~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:87:4: Unsupported: covergroup + 87 | covergroup cg_cross2; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:88:18: Unsupported: cover 'iff' + 88 | cross a, b iff (!rst) {} + | ^~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:88:7: Unsupported: cross + 88 | cross a, b iff (!rst) {} + | ^~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:90:4: Unsupported: covergroup + 90 | covergroup cg_cross3; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:91:20: 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 + 91 | cross a, b { option.comment = "cross"; option.weight = 12; } + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:91:7: Unsupported: 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; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:94:34: Unsupported: coverage cross 'function' declaration + 94 | cross a, b { function void crossfunc; endfunction; } + | ^~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:94:7: Unsupported: cross + 94 | cross a, b { function void crossfunc; endfunction; } + | ^~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:96:4: Unsupported: covergroup + 96 | covergroup cg_cross_id; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:97:28: Unsupported: cover 'iff' + 97 | my_cg_id: cross a, b iff (!rst); + | ^~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:97:17: Unsupported: cross + 97 | my_cg_id: cross a, b iff (!rst); + | ^~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:100:4: Unsupported: covergroup + 100 | covergroup cg_binsoroptions_bk1; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:102:17: Unsupported: cover bin specification + 102 | { bins ba = {a}; } + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:103:24: Unsupported: cover 'iff' + 103 | { bins bar = {a} iff (!rst); } + | ^~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:103:18: Unsupported: cover bin specification + 103 | { bins bar = {a} iff (!rst); } + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:104:26: Unsupported: cover bin specification + 104 | { illegal_bins ila = {a}; } + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:105:25: Unsupported: cover bin specification + 105 | { ignore_bins iga = {a}; } + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:107:19: Unsupported: cover bin specification + 107 | { bins ba[] = {a}; } + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:108:20: Unsupported: cover bin specification + 108 | { bins ba[2] = {a}; } + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:110:23: 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 + 112 | { wildcard bins bwa = {a}; } + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:113:34: 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' + 115 | { bins def = default; } + | ^~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:116:29: Unsupported: cover bin 'default' 'sequence' + 116 | { bins defs = default sequence; } + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:118:18: 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 + 119 | { wildcard bins wbts = ( 1, 2 ); } + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:120:33: 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 + 120 | { bins bts2 = ( 2, 3 ), ( [5:6] ) ; } + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:122:27: 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 + 122 | { bins bts2 = ( 1,5 => 6,7 ) ; } + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:123:25: Unsupported: cover '[*' + 123 | { bins bts2 = ( 3 [*5] ) ; } + | ^~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:123:19: Unsupported: cover bin trans list + 123 | { bins bts2 = ( 3 [*5] ) ; } + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:124:25: Unsupported: cover '[*' + 124 | { bins bts2 = ( 3 [*5:6] ) ; } + | ^~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:124:19: Unsupported: cover bin trans list + 124 | { bins bts2 = ( 3 [*5:6] ) ; } + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:125:25: Unsupported: cover '[->' + 125 | { bins bts2 = ( 3 [->5] ) ; } + | ^~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:125:19: Unsupported: cover bin trans list + 125 | { bins bts2 = ( 3 [->5] ) ; } + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:126:25: Unsupported: cover '[->' + 126 | { bins bts2 = ( 3 [->5:6] ) ; } + | ^~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:126:19: Unsupported: cover bin trans list + 126 | { bins bts2 = ( 3 [->5:6] ) ; } + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:127:25: Unsupported: cover '[=' + 127 | { bins bts2 = ( 3 [=5] ) ; } + | ^~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:127:19: Unsupported: cover bin trans list + 127 | { bins bts2 = ( 3 [=5] ) ; } + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:128:25: Unsupported: cover '[=' + 128 | { bins bts2 = ( 3 [=5:6] ) ; } + | ^~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:128:19: Unsupported: cover bin trans list + 128 | { bins bts2 = ( 3 [=5:6] ) ; } + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:132:4: Unsupported: covergroup + 132 | covergroup cg_cross_bins; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:134:23: Unsupported: coverage select expression 'binsof' + 134 | bins bin_a = binsof(a); + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:134:10: Unsupported: coverage cross bin + 134 | bins bin_a = binsof(a); + | ^~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:135:24: 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' + 135 | bins bin_ai = binsof(a) iff (!rst); + | ^~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:135:10: 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' + 136 | bins bin_c = binsof(cp.x); + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:136:10: 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' + 137 | bins bin_na = ! binsof(a); + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:137:10: Unsupported: coverage cross bin + 137 | bins bin_na = ! binsof(a); + | ^~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:139:33: 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 + 139 | bins bin_d = binsof(a) intersect { b }; + | ^~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:140:34: 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 + 140 | bins bin_nd = ! binsof(a) intersect { b }; + | ^~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:142:23: Unsupported: coverage select expression with + 142 | bins bin_e = with (a); + | ^~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:142:10: Unsupported: coverage cross bin + 142 | bins bin_e = with (a); + | ^~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:143:23: Unsupported: coverage select expression with + 143 | bins bin_e = ! with (a); + | ^ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:143:10: Unsupported: coverage cross bin + 143 | bins bin_e = ! with (a); + | ^~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:145:26: Unsupported: coverage select expression 'binsof' + 145 | bins bin_par = (binsof(a)); + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:145:10: Unsupported: coverage cross bin + 145 | bins bin_par = (binsof(a)); + | ^~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:146:25: 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' + 146 | bins bin_and = binsof(a) && binsof(b); + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:146:35: 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 + 146 | bins bin_and = binsof(a) && binsof(b); + | ^~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:147:24: 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' + 147 | bins bin_or = binsof(a) || binsof(b); + | ^~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:147:34: 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 + 147 | bins bin_or = binsof(a) || binsof(b); + | ^~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:133:7: Unsupported: cross + 133 | cross a, b { + | ^~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_unsup.pl b/test_regress/t/t_covergroup_unsup.pl new file mode 100755 index 000000000..c9ca914d8 --- /dev/null +++ b/test_regress/t/t_covergroup_unsup.pl @@ -0,0 +1,20 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 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 + +scenarios(vlt => 1); + +compile( + expect_filename => $Self->{golden_filename}, + verilator_flags2 => ['--assert --error-limit 1000'], + fails => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_covergroup_unsup.v b/test_regress/t/t_covergroup_unsup.v new file mode 100644 index 000000000..6fed272bc --- /dev/null +++ b/test_regress/t/t_covergroup_unsup.v @@ -0,0 +1,157 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + int a; + int b; + logic c; + int cyc = 0; + + always @(posedge clk) begin + cyc <= cyc + 1; + end + + // NOTE this grammar hasn't been checked with other simulators, + // is here just to avoid uncovered code lines in the grammar. + + covergroup cg_empty; + endgroup + + covergroup cg_opt; + type_option.weight = 1; // cg, cp, cross + type_option.goal = 99; // cg, cp, cross + type_option.comment = "type_option_comment"; // cg, cp, cross + type_option.strobe = 0; // cg + type_option.merge_instances = 1; // cg + type_option.distribuge_first = 1; // cg + option.name = "the_name"; // cg + option.weight = 1; // cg, cp, cross + option.goal = 98; // cg, cp, cross + option.comment = "option_comment"; // cg, cp, cross + option.at_least = 20; // cg, cp, cross + option.auto_bin_max = 10; // cg, cp + option.cross_num_print_missing = 2; // cg, cross + option.detect_overlap = 1; // cg, cp + option.per_instance = 1; // cg + option.get_inst_coverage = 1; // cg + endgroup + + covergroup cg_clockingevent() @(posedge clk); + endgroup + covergroup cg_withfunction() with function sample (a); + endgroup + covergroup cg_atat() @@ (begin funca or end funcb); + endgroup + covergroup cg_bracket; + {} + endgroup + covergroup cg_bracket; + { option.name = "option"; } + endgroup + covergroup cg_cp; + coverpoint a; + endgroup + covergroup cg_cp_iff; + coverpoint a iff (b); + endgroup + covergroup cg_id_cp_iff; + id: coverpoint a iff (b); + endgroup + covergroup cg_id_cp_id1; + int id: coverpoint a iff (b); + endgroup + covergroup cg_id_cp_id2; + var int id: coverpoint a iff (b); + endgroup + covergroup cg_id_cp_id3; + var [3:0] id: coverpoint a iff (b); + endgroup + covergroup cg_id_cp_id4; + [3:0] id: coverpoint a iff (b); + endgroup + covergroup cg_id_cp_id5; + signed id: coverpoint a iff (b); + endgroup + + covergroup cg_cross; + cross a, b iff (!rst); + endgroup + covergroup cg_cross2; + cross a, b iff (!rst) {} + endgroup + covergroup cg_cross3; + cross a, b { option.comment = "cross"; option.weight = 12; } + endgroup + covergroup cg_cross3; + cross a, b { function void crossfunc; endfunction; } + endgroup + covergroup cg_cross_id; + my_cg_id: cross a, b iff (!rst); + endgroup + + covergroup cg_binsoroptions_bk1; + // bins_keyword id/*bin_identifier*/ bins_orBraE '=' '{' open_range_list '}' iffE + { bins ba = {a}; } + { bins bar = {a} iff (!rst); } + { illegal_bins ila = {a}; } + { ignore_bins iga = {a}; } + + { bins ba[] = {a}; } + { bins ba[2] = {a}; } + + { bins ba = {a} with { b }; } + + { wildcard bins bwa = {a}; } + { wildcard bins bwaw = {a} with { b }; } + + { bins def = default; } + { bins defs = default sequence; } + + { bins bts = ( 1, 2 ); } + { wildcard bins wbts = ( 1, 2 ); } + { bins bts2 = ( 2, 3 ), ( [5:6] ) ; } + + { bins bts2 = ( 1,5 => 6,7 ) ; } + { bins bts2 = ( 3 [*5] ) ; } + { bins bts2 = ( 3 [*5:6] ) ; } + { bins bts2 = ( 3 [->5] ) ; } + { bins bts2 = ( 3 [->5:6] ) ; } + { bins bts2 = ( 3 [=5] ) ; } + { bins bts2 = ( 3 [=5:6] ) ; } + + endgroup + + covergroup cg_cross_bins; + cross a, b { + bins bin_a = binsof(a); + bins bin_ai = binsof(a) iff (!rst); + bins bin_c = binsof(cp.x); + bins bin_na = ! binsof(a); + + bins bin_d = binsof(a) intersect { b }; + bins bin_nd = ! binsof(a) intersect { b }; + + bins bin_e = with (a); + bins bin_e = ! with (a); + + bins bin_par = (binsof(a)); + bins bin_and = binsof(a) && binsof(b); + bins bin_or = binsof(a) || binsof(b); + } + endgroup + + always @(posedge clk) begin + if (cyc == 10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index 82db5dfde..abfcc0c61 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -169,9 +169,11 @@ module Vt_debug_emitv_t; : $c('sh1e))); $c(;); $display("%d", $c(0)); - $fopen(72'h2f6465762f6e756c6c); + fd = $fopen(72'h2f6465762f6e756c6c); + ; $fclose(fd); - $fopen(72'h2f6465762f6e756c6c, 8'h72); + fd = $fopen(72'h2f6465762f6e756c6c, 8'h72); + ; $fgetc(fd); $fflush(fd); $fscanf(fd, "%d", sum); @@ -335,14 +337,12 @@ module Vt_debug_emitv_sub; function f; input signed int [31:0] v; begin : label0 - begin : label0 - if ((v == 'sh0)) begin - f = 'sh21; - disable label0; - end - f = ({32'h1{{31'h0, v[2]}}} + 32'h1); + if ((v == 'sh0)) begin + f = 'sh21; disable label0; end + f = ({32'h1{{31'h0, v[2]}}} + 32'h1); + disable label0; end endfunction signed real r; diff --git a/test_regress/t/t_delay_var.out b/test_regress/t/t_delay_var.out index 7c78068df..1ee2ef517 100644 --- a/test_regress/t/t_delay_var.out +++ b/test_regress/t/t_delay_var.out @@ -1,5 +1,6 @@ -%Error-ZERODLY: t/t_delay_var.v:20:7: Unsupported: #0 delays do not schedule process resumption in the Inactive region +%Warning-ZERODLY: t/t_delay_var.v:20:7: Unsupported: #0 delays do not schedule process resumption in the Inactive region 20 | #0 in = 1'b1; | ^ - ... For error description see https://verilator.org/warn/ZERODLY?v=latest + ... For warning description see https://verilator.org/warn/ZERODLY?v=latest + ... Use "/* verilator lint_off ZERODLY */" and lint_on around source to disable this message. %Error: Exiting due to diff --git a/test_regress/t/t_dfg_4104.pl b/test_regress/t/t_dfg_4104.pl new file mode 100755 index 000000000..32bdf873d --- /dev/null +++ b/test_regress/t/t_dfg_4104.pl @@ -0,0 +1,16 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2023 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt => 1); + +compile(); + +ok(1); +1; diff --git a/test_regress/t/t_dfg_4104.v b/test_regress/t/t_dfg_4104.v new file mode 100644 index 000000000..508f93a57 --- /dev/null +++ b/test_regress/t/t_dfg_4104.v @@ -0,0 +1,12 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Geza Lore. +// SPDX-License-Identifier: CC0-1.0 + +module v(input logic t); +endmodule + +module top(input logic [2:0] c); + v v1((int'(c) + int'($countones(c))) > 2); +endmodule diff --git a/test_regress/t/t_dist_attributes_bad.out b/test_regress/t/t_dist_attributes_bad.out index e7649a02f..fea14b7b0 100644 --- a/test_regress/t/t_dist_attributes_bad.out +++ b/test_regress/t/t_dist_attributes_bad.out @@ -65,7 +65,7 @@ t/t_dist_attributes_bad.h:204: [mt_unsafe_one] TestCla t/t_dist_attributes_bad.h:209: [mt_safe, mt_unsafe, pure] TestClass::cm_ea_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.h:209: [mt_safe, mt_unsafe_one, pure] TestClass::cm_ea_VL_MT_UNSAFE_ONE(VerilatedMutex &) -%Error: "TestClass::cm_test_caller_smethod_VL_MT_START(VerilatedMutex &)" is mtsafe but calls non-mtsafe function(s) +%Error: "TestClass::cm_test_caller_smethod_VL_MT_START(VerilatedMutex &)" is stable_tree but calls non-stable_tree or non-mtsafe t/t_dist_attributes_bad.cpp:155: [mt_start] TestClass::cm_test_caller_smethod_VL_MT_START(VerilatedMutex &) t/t_dist_attributes_bad.h:191: [mt_unsafe] TestClass::cm_au_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.h:191: [mt_unsafe_one] TestClass::cm_au_VL_MT_UNSAFE_ONE(VerilatedMutex &) @@ -143,7 +143,7 @@ t/t_dist_attributes_bad.h:204: [mt_unsafe_one] TestCla t/t_dist_attributes_bad.h:209: [mt_safe, mt_unsafe, pure] TestClass::cm_ea_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.h:209: [mt_safe, mt_unsafe_one, pure] TestClass::cm_ea_VL_MT_UNSAFE_ONE(VerilatedMutex &) -%Error: "TestClass::cm_test_caller_smethod_hdr_VL_MT_START(VerilatedMutex &)" is mtsafe but calls non-mtsafe function(s) +%Error: "TestClass::cm_test_caller_smethod_hdr_VL_MT_START(VerilatedMutex &)" is stable_tree but calls non-stable_tree or non-mtsafe t/t_dist_attributes_bad.h:212: [mt_start] TestClass::cm_test_caller_smethod_hdr_VL_MT_START(VerilatedMutex &) t/t_dist_attributes_bad.h:191: [mt_unsafe] TestClass::cm_au_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.h:191: [mt_unsafe_one] TestClass::cm_au_VL_MT_UNSAFE_ONE(VerilatedMutex &) @@ -273,7 +273,7 @@ t/t_dist_attributes_bad.cpp:175: [mt_safe] TestClass t/t_dist_attributes_bad.h:235: [mt_unsafe] TestClass::icm_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.h:235: [mt_unsafe_one] TestClass::icm_VL_MT_UNSAFE_ONE(VerilatedMutex &) -%Error: "TestClass::icm_test_caller_smethod_VL_MT_START(VerilatedMutex &)" is mtsafe but calls non-mtsafe function(s) +%Error: "TestClass::icm_test_caller_smethod_VL_MT_START(VerilatedMutex &)" is stable_tree but calls non-stable_tree or non-mtsafe t/t_dist_attributes_bad.cpp:175: [mt_start] TestClass::icm_test_caller_smethod_VL_MT_START(VerilatedMutex &) t/t_dist_attributes_bad.h:235: [mt_unsafe] TestClass::icm_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.h:235: [mt_unsafe_one] TestClass::icm_VL_MT_UNSAFE_ONE(VerilatedMutex &) @@ -299,7 +299,7 @@ t/t_dist_attributes_bad.h:238: [mt_safe] TestClass t/t_dist_attributes_bad.h:235: [mt_unsafe] TestClass::icm_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.h:235: [mt_unsafe_one] TestClass::icm_VL_MT_UNSAFE_ONE(VerilatedMutex &) -%Error: "TestClass::icm_test_caller_smethod_hdr_VL_MT_START(VerilatedMutex &)" is mtsafe but calls non-mtsafe function(s) +%Error: "TestClass::icm_test_caller_smethod_hdr_VL_MT_START(VerilatedMutex &)" is stable_tree but calls non-stable_tree or non-mtsafe t/t_dist_attributes_bad.h:238: [mt_start] TestClass::icm_test_caller_smethod_hdr_VL_MT_START(VerilatedMutex &) t/t_dist_attributes_bad.h:235: [mt_unsafe] TestClass::icm_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.h:235: [mt_unsafe_one] TestClass::icm_VL_MT_UNSAFE_ONE(VerilatedMutex &) @@ -327,7 +327,7 @@ t/t_dist_attributes_bad.h:170: [mt_start] TestCla t/t_dist_attributes_bad.h:170: [mt_unsafe] TestClass::iscm_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.h:170: [mt_unsafe_one] TestClass::iscm_VL_MT_UNSAFE_ONE(VerilatedMutex &) -%Error: "TestClass::iscm_test_caller_smethod_VL_MT_START(VerilatedMutex &)" is mtsafe but calls non-mtsafe function(s) +%Error: "TestClass::iscm_test_caller_smethod_VL_MT_START(VerilatedMutex &)" is stable_tree but calls non-stable_tree or non-mtsafe t/t_dist_attributes_bad.cpp:119: [mt_start] TestClass::iscm_test_caller_smethod_VL_MT_START(VerilatedMutex &) t/t_dist_attributes_bad.h:170: [] TestClass::iscm_NO_ANNOTATION(VerilatedMutex &) t/t_dist_attributes_bad.h:170: [mt_start] TestClass::iscm_VL_MT_START(VerilatedMutex &) @@ -357,7 +357,7 @@ t/t_dist_attributes_bad.h:170: [mt_start] TestCla t/t_dist_attributes_bad.h:170: [mt_unsafe] TestClass::iscm_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.h:170: [mt_unsafe_one] TestClass::iscm_VL_MT_UNSAFE_ONE(VerilatedMutex &) -%Error: "TestClass::iscm_test_caller_smethod_hdr_VL_MT_START(VerilatedMutex &)" is mtsafe but calls non-mtsafe function(s) +%Error: "TestClass::iscm_test_caller_smethod_hdr_VL_MT_START(VerilatedMutex &)" is stable_tree but calls non-stable_tree or non-mtsafe t/t_dist_attributes_bad.h:173: [mt_start] TestClass::iscm_test_caller_smethod_hdr_VL_MT_START(VerilatedMutex &) t/t_dist_attributes_bad.h:170: [] TestClass::iscm_NO_ANNOTATION(VerilatedMutex &) t/t_dist_attributes_bad.h:170: [mt_start] TestClass::iscm_VL_MT_START(VerilatedMutex &) @@ -467,7 +467,7 @@ t/t_dist_attributes_bad.h:133: [mt_unsafe_one] TestCla t/t_dist_attributes_bad.h:138: [mt_safe, mt_unsafe, pure] TestClass::scm_ea_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.h:138: [mt_safe, mt_unsafe_one, pure] TestClass::scm_ea_VL_MT_UNSAFE_ONE(VerilatedMutex &) -%Error: "TestClass::scm_test_caller_smethod_VL_MT_START(VerilatedMutex &)" is mtsafe but calls non-mtsafe function(s) +%Error: "TestClass::scm_test_caller_smethod_VL_MT_START(VerilatedMutex &)" is stable_tree but calls non-stable_tree or non-mtsafe t/t_dist_attributes_bad.cpp:93: [mt_start] TestClass::scm_test_caller_smethod_VL_MT_START(VerilatedMutex &) t/t_dist_attributes_bad.h:120: [] TestClass::scm_au_NO_ANNOTATION(VerilatedMutex &) t/t_dist_attributes_bad.h:120: [mt_start] TestClass::scm_au_VL_MT_START(VerilatedMutex &) @@ -585,7 +585,7 @@ t/t_dist_attributes_bad.h:133: [mt_unsafe_one] TestCla t/t_dist_attributes_bad.h:138: [mt_safe, mt_unsafe, pure] TestClass::scm_ea_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.h:138: [mt_safe, mt_unsafe_one, pure] TestClass::scm_ea_VL_MT_UNSAFE_ONE(VerilatedMutex &) -%Error: "TestClass::scm_test_caller_smethod_hdr_VL_MT_START(VerilatedMutex &)" is mtsafe but calls non-mtsafe function(s) +%Error: "TestClass::scm_test_caller_smethod_hdr_VL_MT_START(VerilatedMutex &)" is stable_tree but calls non-stable_tree or non-mtsafe t/t_dist_attributes_bad.h:141: [mt_start] TestClass::scm_test_caller_smethod_hdr_VL_MT_START(VerilatedMutex &) t/t_dist_attributes_bad.h:120: [] TestClass::scm_au_NO_ANNOTATION(VerilatedMutex &) t/t_dist_attributes_bad.h:120: [mt_start] TestClass::scm_au_VL_MT_START(VerilatedMutex &) @@ -759,7 +759,7 @@ t/t_dist_attributes_bad.h:94: [mt_start] ifh_VL_ t/t_dist_attributes_bad.h:94: [mt_unsafe] ifh_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.h:94: [mt_unsafe_one] ifh_VL_MT_UNSAFE_ONE(VerilatedMutex &) -%Error: "ifh_test_caller_func_VL_MT_START(VerilatedMutex &)" is mtsafe but calls non-mtsafe function(s) +%Error: "ifh_test_caller_func_VL_MT_START(VerilatedMutex &)" is stable_tree but calls non-stable_tree or non-mtsafe t/t_dist_attributes_bad.cpp:53: [mt_start] ifh_test_caller_func_VL_MT_START(VerilatedMutex &) t/t_dist_attributes_bad.h:94: [] ifh_NO_ANNOTATION(VerilatedMutex &) t/t_dist_attributes_bad.h:94: [mt_start] ifh_VL_MT_START(VerilatedMutex &) @@ -789,7 +789,7 @@ t/t_dist_attributes_bad.h:94: [mt_start] ifh_VL_ t/t_dist_attributes_bad.h:94: [mt_unsafe] ifh_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.h:94: [mt_unsafe_one] ifh_VL_MT_UNSAFE_ONE(VerilatedMutex &) -%Error: "ifh_test_caller_func_hdr_VL_MT_START(VerilatedMutex &)" is mtsafe but calls non-mtsafe function(s) +%Error: "ifh_test_caller_func_hdr_VL_MT_START(VerilatedMutex &)" is stable_tree but calls non-stable_tree or non-mtsafe t/t_dist_attributes_bad.h:97: [mt_start] ifh_test_caller_func_hdr_VL_MT_START(VerilatedMutex &) t/t_dist_attributes_bad.h:94: [] ifh_NO_ANNOTATION(VerilatedMutex &) t/t_dist_attributes_bad.h:94: [mt_start] ifh_VL_MT_START(VerilatedMutex &) @@ -899,7 +899,7 @@ t/t_dist_attributes_bad.h:75: [mt_unsafe_one] nsf_ae_ t/t_dist_attributes_bad.h:80: [mt_safe, mt_unsafe, pure] nsf_ea_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.h:80: [mt_safe, mt_unsafe_one, pure] nsf_ea_VL_MT_UNSAFE_ONE(VerilatedMutex &) -%Error: "nsf_test_caller_func_VL_MT_START(VerilatedMutex &)" is mtsafe but calls non-mtsafe function(s) +%Error: "nsf_test_caller_func_VL_MT_START(VerilatedMutex &)" is stable_tree but calls non-stable_tree or non-mtsafe t/t_dist_attributes_bad.cpp:42: [mt_start] nsf_test_caller_func_VL_MT_START(VerilatedMutex &) t/t_dist_attributes_bad.h:62: [] nsf_au_NO_ANNOTATION(VerilatedMutex &) t/t_dist_attributes_bad.h:62: [mt_start] nsf_au_VL_MT_START(VerilatedMutex &) @@ -1017,7 +1017,7 @@ t/t_dist_attributes_bad.h:75: [mt_unsafe_one] nsf_ae_ t/t_dist_attributes_bad.h:80: [mt_safe, mt_unsafe, pure] nsf_ea_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.h:80: [mt_safe, mt_unsafe_one, pure] nsf_ea_VL_MT_UNSAFE_ONE(VerilatedMutex &) -%Error: "nsf_test_caller_func_hdr_VL_MT_START(VerilatedMutex &)" is mtsafe but calls non-mtsafe function(s) +%Error: "nsf_test_caller_func_hdr_VL_MT_START(VerilatedMutex &)" is stable_tree but calls non-stable_tree or non-mtsafe t/t_dist_attributes_bad.h:83: [mt_start] nsf_test_caller_func_hdr_VL_MT_START(VerilatedMutex &) t/t_dist_attributes_bad.h:62: [] nsf_au_NO_ANNOTATION(VerilatedMutex &) t/t_dist_attributes_bad.h:62: [mt_start] nsf_au_VL_MT_START(VerilatedMutex &) @@ -1163,7 +1163,7 @@ t/t_dist_attributes_bad.cpp:60: [mt_start] sfc_VL_ t/t_dist_attributes_bad.cpp:60: [mt_unsafe] sfc_VL_MT_UNSAFE(VerilatedMutex &) t/t_dist_attributes_bad.cpp:60: [mt_unsafe_one] sfc_VL_MT_UNSAFE_ONE(VerilatedMutex &) -%Error: "sfc_test_caller_func_VL_MT_START(VerilatedMutex &)" is mtsafe but calls non-mtsafe function(s) +%Error: "sfc_test_caller_func_VL_MT_START(VerilatedMutex &)" is stable_tree but calls non-stable_tree or non-mtsafe t/t_dist_attributes_bad.cpp:63: [mt_start] sfc_test_caller_func_VL_MT_START(VerilatedMutex &) t/t_dist_attributes_bad.cpp:60: [] sfc_NO_ANNOTATION(VerilatedMutex &) t/t_dist_attributes_bad.cpp:60: [mt_start] sfc_VL_MT_START(VerilatedMutex &) diff --git a/test_regress/t/t_dist_cinclude.pl b/test_regress/t/t_dist_cinclude.pl index da88adfc7..f006eae01 100755 --- a/test_regress/t/t_dist_cinclude.pl +++ b/test_regress/t/t_dist_cinclude.pl @@ -26,6 +26,7 @@ if (!-r catfile($root, ".git")) { next if $file =~ m!include/vltstd/vpi_user.h!; # IEEE Standard file - can't change it next if $file =~ m!include/gtkwave/!; # Standard file - can't change it my $filename = catfile($root, $file); + next if !-r $filename; @lines = split /\n/, file_contents($filename); @include_lines = grep(/include/, @lines); foreach my $line (@include_lines) { diff --git a/test_regress/t/t_dist_copyright.pl b/test_regress/t/t_dist_copyright.pl index 809b9af7b..065f54ff5 100755 --- a/test_regress/t/t_dist_copyright.pl +++ b/test_regress/t/t_dist_copyright.pl @@ -11,6 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di use IO::File; use POSIX qw(strftime); use strict; +use File::Spec::Functions 'catfile'; scenarios(dist => 1); @@ -74,8 +75,10 @@ if (!-r "$root/.git") { $added{$1} = 1; } - foreach my $filename (sort keys %files) { - my $fh = IO::File->new("<$root/$filename") or error("$! $filename"); + foreach my $file (sort keys %files) { + my $filename = catfile($root, $file); + next if !-r $filename; + my $fh = IO::File->new("<$filename") or error("$! $filename"); next if !$fh; my $spdx; my $copyright; @@ -86,12 +89,12 @@ if (!-r "$root/.git") { } elsif ($line =~ /Copyright 20[0-9][0-9]/) { $copyright = $line; if ($line =~ /Wilson Snyder/) { - } elsif (!$added{$filename} && $line =~ /Antmicro|Geza Lore|Todd Strader/) { - } elsif ($filename =~ /$Exempt_Author_Re/) { + } elsif (!$added{$file} && $line =~ /Antmicro|Geza Lore|Todd Strader/) { + } elsif ($file =~ /$Exempt_Author_Re/) { } else { - my $yeardash = ($filename =~ m!test_regress/t!) ? $year : $year."-".$year; + my $yeardash = ($file =~ m!test_regress/t!) ? $year : $year."-".$year; warn " ".$copyright; - error("$filename: Please use standard 'Copyright $yeardash by Wilson Snyder'"); + error("$file: Please use standard 'Copyright $yeardash by Wilson Snyder'"); } } elsif ($line =~ m!Creative Commons Public Domain! || $line =~ m!freely copied and/or distributed! @@ -100,14 +103,14 @@ if (!-r "$root/.git") { } } my $release_note; - if ($release && $filename !~ /$Release_Ok_Re/) { + if ($release && $file !~ /$Release_Ok_Re/) { $release_note = " (has copyright release, but not part of $Release_Ok_Re)"; } if (!$copyright && (!$release || $release_note)) { - error("$filename: Please add standard 'Copyright $year ...', similar to in other files" . $release_note); + error("$file: Please add standard 'Copyright $year ...', similar to in other files" . $release_note); } if (!$spdx) { - error("$filename: Please add standard 'SPDX-License_Identifier: ...', similar to in other files"); + error("$file: Please add standard 'SPDX-License_Identifier: ...', similar to in other files"); } } } diff --git a/test_regress/t/t_dist_fixme.pl b/test_regress/t/t_dist_fixme.pl index 0f36f4486..20079a96c 100755 --- a/test_regress/t/t_dist_fixme.pl +++ b/test_regress/t/t_dist_fixme.pl @@ -9,6 +9,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 use IO::File; +use File::Spec::Functions 'catfile'; scenarios(dist => 1); @@ -28,7 +29,9 @@ if (!-r "$root/.git") { my $n = 0; my $re = qr/(FIX[M]E|BO[Z]O)/; foreach my $file (split /\s+/, $files) { - my $wholefile = file_contents($root . "/" . $file); + my $filename = catfile($root, $file); + next if !-r $filename; + my $wholefile = file_contents($filename); if ($wholefile =~ /$re/) { $names{$file} = 1; } diff --git a/test_regress/t/t_dist_warn_coverage.pl b/test_regress/t/t_dist_warn_coverage.pl index 7fb44afc5..234e2bce3 100755 --- a/test_regress/t/t_dist_warn_coverage.pl +++ b/test_regress/t/t_dist_warn_coverage.pl @@ -31,7 +31,6 @@ foreach my $s ( 'dynamic new() not expected in this context (expected under an assign)', # Instead get syntax error # Not yet analyzed ' is not an in/out/inout/param/interface: ', - 'Big endian instance range connecting to ', ' loading non-variable', '--pipe-filter protocol error, unexpected: ', '/*verilator sformat*/ can only be applied to last argument of ', @@ -43,6 +42,7 @@ foreach my $s ( 'Assignment pattern with no members', 'Assignment pattern with too many elements', 'Attempted parameter setting of non-parameter: Param ', + 'Can\'t find typedef: ', 'Can\'t find varpin scope of ', 'Can\'t resolve module reference: \'', 'Cannot mix DPI import, DPI export, class methods, and/or public ', @@ -53,6 +53,7 @@ foreach my $s ( 'Exceeded limit of ', 'Extern declaration\'s scope is not a defined class', 'Format to $display-like function must have constant format string', + 'Forward typedef used as class/package does not resolve to class/package: ', 'Illegal +: or -: select; type already selected, or bad dimension: ', 'Illegal bit or array select; type already selected, or bad dimension: ', 'Illegal range select; type already selected, or bad dimension: ', @@ -177,6 +178,7 @@ sub read_messages { line: while (my $origline = ($fh && $fh->getline)) { my $line = $origline; + next if $line =~ m!^\s*//!; ++$lineno; if ($line =~ /\b(v3error|v3warn)\b\($/g) { $read_next = 1 if $line !~ /LCOV_EXCL_LINE/; diff --git a/test_regress/t/t_dist_whitespace.pl b/test_regress/t/t_dist_whitespace.pl index 3a09d63e7..5fcf0c9c3 100755 --- a/test_regress/t/t_dist_whitespace.pl +++ b/test_regress/t/t_dist_whitespace.pl @@ -67,7 +67,8 @@ foreach my $file (sort keys %files) { if ($contents =~ /[^[:alnum:][:punct:] \t\r\n]/) { my $unicode_exempt = ($file =~ /Changes$/ || $file =~ /CONTRIBUTORS$/ - || $file =~ /contributors.rst$/); + || $file =~ /contributors.rst$/ + || $file =~ /spelling.txt$/); next if $unicode_exempt; $warns{$file} = "Warning: non-ASCII contents in $file\n"; } diff --git a/test_regress/t/t_do_while.pl b/test_regress/t/t_do_while.pl new file mode 100755 index 000000000..aabcde63e --- /dev/null +++ b/test_regress/t/t_do_while.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2020 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_do_while.v b/test_regress/t/t_do_while.v new file mode 100644 index 000000000..9423853f4 --- /dev/null +++ b/test_regress/t/t_do_while.v @@ -0,0 +1,58 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +function automatic int get_1; + int a = 0; + do begin + int x = 1; + a += x; + end while (a < 0); + return a; +endfunction + +module t (/*AUTOARG*/); + int a; + initial begin + if (get_1() != 1) $stop; + + a = 0; + do begin + int x = 1; + a += x; + if (a == 1) begin + a = 2; + end + end while (a < 0); + if (a != 2) $stop; + + a = 1; + do begin + if (a == 1) begin + a = 2; + end + if (a == 2) begin + a = 3; + end + end while (a < 0); + if (a != 3) $stop; + + a = 1; + do begin + if (a == 1) begin + do begin + a++; + end while (a < 5); + end + if (a == 2) begin + a = 3; + end + end while (a < 0); + if (a != 5) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_dpi_shortcircuit.out b/test_regress/t/t_dpi_shortcircuit.out index e1463672b..53f6dc0e8 100644 --- a/test_regress/t/t_dpi_shortcircuit.out +++ b/test_regress/t/t_dpi_shortcircuit.out @@ -1,18 +1,22 @@ -%Error: Line 60: Bad result, got=1 expect=0 -%Error: Line 64: Bad result, got=1 expect=0 -%Error: Line 75: Bad result, got=0 expect=1 -%Error: Line 98: Bad result, got=1 expect=0 -%Error: Line 102: Bad result, got=1 expect=0 -%Error: Line 112: Bad result, got=0 expect=1 -%Error: Line 132: Bad result, got=1 expect=0 -%Error: Line 136: Bad result, got=1 expect=0 -%Error: Line 150: Bad result, got=1 expect=0 -%Error: Line 154: Bad result, got=1 expect=0 -%Error: Line 163: Bad result, got=0 expect=1 -%Error: Line 203: Bad result, got=64 expect=32 -%Error: Line 204: Bad result, got=64 expect=16 -%Error: Line 205: Bad result, got=64 expect=16 -%Error: Line 206: Bad result, got=64 expect=36 -%Error: Line 207: Bad result, got=64 expect=46 -%Error: t/t_dpi_shortcircuit.v:209: Verilog $stop +%Error: Line 62: Bad result, got=1 expect=0 +%Error: Line 66: Bad result, got=1 expect=0 +%Error: Line 69: Bad result, got=0 expect=1 +%Error: Line 81: Bad result, got=0 expect=1 +%Error: Line 91: Bad result, got=0 expect=1 +%Error: Line 108: Bad result, got=1 expect=0 +%Error: Line 112: Bad result, got=1 expect=0 +%Error: Line 114: Bad result, got=0 expect=1 +%Error: Line 126: Bad result, got=0 expect=1 +%Error: Line 136: Bad result, got=0 expect=1 +%Error: Line 148: Bad result, got=1 expect=0 +%Error: Line 152: Bad result, got=1 expect=0 +%Error: Line 166: Bad result, got=1 expect=0 +%Error: Line 170: Bad result, got=1 expect=0 +%Error: Line 179: Bad result, got=0 expect=1 +%Error: Line 219: Bad result, got=64 expect=32 +%Error: Line 220: Bad result, got=64 expect=16 +%Error: Line 221: Bad result, got=64 expect=16 +%Error: Line 222: Bad result, got=64 expect=36 +%Error: Line 223: Bad result, got=64 expect=46 +%Error: t/t_dpi_shortcircuit.v:225: Verilog $stop Aborting... diff --git a/test_regress/t/t_dpi_shortcircuit.v b/test_regress/t/t_dpi_shortcircuit.v index b89cdffd4..e6be0315a 100644 --- a/test_regress/t/t_dpi_shortcircuit.v +++ b/test_regress/t/t_dpi_shortcircuit.v @@ -54,6 +54,8 @@ module t (/*AUTOARG*/); check1(`__LINE__, (dpii_inc1(4) && dpii_inc0(5)), 1'b0); check1(`__LINE__, (dpii_inc0(6) && dpii_inc1(7)), 1'b0); check1(`__LINE__, (!(dpii_inc1(8) && dpii_inc1(9))), 1'b0); + check1(`__LINE__, (dpii_inc0(10) && 1'b0), 1'b0); + check1(`__LINE__, (dpii_inc0(11) && 1'b1), 1'b0); check (`__LINE__, dpii_count(0), 0); check (`__LINE__, dpii_count(1), 1); check (`__LINE__, dpii_count(2), 1); @@ -64,6 +66,8 @@ module t (/*AUTOARG*/); check (`__LINE__, dpii_count(7), 0); check (`__LINE__, dpii_count(8), 1); check (`__LINE__, dpii_count(9), 1); + check (`__LINE__, dpii_count(10), 1); + check (`__LINE__, dpii_count(11), 1); // dpii_clear(); check1(`__LINE__, (1'b0 & dpii_inc0(0)), 1'b0); @@ -72,6 +76,8 @@ module t (/*AUTOARG*/); check1(`__LINE__, (dpii_inc1(4) & dpii_inc0(5)), 1'b0); check1(`__LINE__, (dpii_inc0(6) & dpii_inc1(7)), 1'b0); check1(`__LINE__, (!(dpii_inc1(8) & dpii_inc1(9))), 1'b0); + check1(`__LINE__, (dpii_inc0(10) & 1'b0), 1'b0); + check1(`__LINE__, (dpii_inc0(11) & 1'b1), 1'b0); check (`__LINE__, dpii_count(0), 1); check (`__LINE__, dpii_count(1), 1); check (`__LINE__, dpii_count(2), 1); @@ -82,6 +88,8 @@ module t (/*AUTOARG*/); check (`__LINE__, dpii_count(7), 1); check (`__LINE__, dpii_count(8), 1); check (`__LINE__, dpii_count(9), 1); + check (`__LINE__, dpii_count(10), 1); + check (`__LINE__, dpii_count(11), 1); // dpii_clear(); check1(`__LINE__, (1'b0 || dpii_inc0(0)), 1'b0); @@ -90,6 +98,8 @@ module t (/*AUTOARG*/); check1(`__LINE__, (dpii_inc1(4) || dpii_inc0(5)), 1'b1); check1(`__LINE__, (dpii_inc0(6) || dpii_inc1(7)), 1'b1); check1(`__LINE__, (!(dpii_inc1(8) || dpii_inc1(9))), 1'b0); + check1(`__LINE__, (dpii_inc0(10) || 1'b0), 1'b0); + check1(`__LINE__, (dpii_inc0(11) || 1'b1), 1'b1); check (`__LINE__, dpii_count(0), 1); check (`__LINE__, dpii_count(1), 0); check (`__LINE__, dpii_count(2), 1); @@ -100,6 +110,8 @@ module t (/*AUTOARG*/); check (`__LINE__, dpii_count(7), 1); check (`__LINE__, dpii_count(8), 1); check (`__LINE__, dpii_count(9), 0); + check (`__LINE__, dpii_count(10), 1); + check (`__LINE__, dpii_count(11), 1); // dpii_clear(); check1(`__LINE__, (1'b0 | dpii_inc0(0)), 1'b0); @@ -108,6 +120,8 @@ module t (/*AUTOARG*/); check1(`__LINE__, (dpii_inc1(4) | dpii_inc0(5)), 1'b1); check1(`__LINE__, (dpii_inc0(6) | dpii_inc1(7)), 1'b1); check1(`__LINE__, (!(dpii_inc1(8) | dpii_inc1(9))), 1'b0); + check1(`__LINE__, (dpii_inc0(10) | 1'b0), 1'b0); + check1(`__LINE__, (dpii_inc0(11) | 1'b1), 1'b1); check (`__LINE__, dpii_count(0), 1); check (`__LINE__, dpii_count(1), 1); check (`__LINE__, dpii_count(2), 1); @@ -118,6 +132,8 @@ module t (/*AUTOARG*/); check (`__LINE__, dpii_count(7), 1); check (`__LINE__, dpii_count(8), 1); check (`__LINE__, dpii_count(9), 1); + check (`__LINE__, dpii_count(10), 1); + check (`__LINE__, dpii_count(11), 1); // dpii_clear(); check1(`__LINE__, (1'b0 -> dpii_inc0(0)), 1'b1); @@ -192,12 +208,12 @@ module t (/*AUTOARG*/); // Something a lot more complicated dpii_clear(); for (i=0; i<64; i++) begin - b = ( ((dpii_incx(0,i[0]) - && (dpii_incx(1,i[1]) - || dpii_incx(2,i[2]) - | dpii_incx(3,i[3]))) // | not || - || dpii_incx(4,i[4])) - -> dpii_incx(5,i[5])); + b = ( ((dpii_incx(0,i[0]) + && (dpii_incx(1,i[1]) + || dpii_incx(2,i[2]) + | dpii_incx(3,i[3]))) // | not || + || dpii_incx(4,i[4])) + -> dpii_incx(5,i[5])); end check (`__LINE__, dpii_count(0), 64); check (`__LINE__, dpii_count(1), 32); diff --git a/test_regress/t/t_dpi_shortcircuit_c.cpp b/test_regress/t/t_dpi_shortcircuit_c.cpp index 12ff41438..9a352a6b6 100644 --- a/test_regress/t/t_dpi_shortcircuit_c.cpp +++ b/test_regress/t/t_dpi_shortcircuit_c.cpp @@ -54,7 +54,7 @@ void dpii_clear() { } int dpii_count(int idx) { return (idx >= 0 && idx < COUNTERS) ? global_count[idx] : -1; } unsigned char dpii_incx(int idx, unsigned char value) { - if (idx >= 0 && idx < COUNTERS) global_count[idx]++; + if (idx >= 0 && idx < COUNTERS) ++global_count[idx]; return value; } unsigned char dpii_inc0(int idx) { return dpii_incx(idx, 0); } diff --git a/test_regress/t/t_enum.v b/test_regress/t/t_enum.v index 2182ce9de..887866a37 100644 --- a/test_regress/t/t_enum.v +++ b/test_regress/t/t_enum.v @@ -31,10 +31,17 @@ module t (/*AUTOARG*/); z5 = e5 } ZN; + enum int unsigned { + FIVE_INT = 5 + } FI; + typedef enum three_t; // Forward typedef enum [2:0] { ONES=~0 } three_t; three_t three = ONES; + int array5[z5]; + int array5i[FIVE_INT]; + var logic [ONES:0] sized_based_on_enum; var enum logic [3:0] { QINVALID='1, QSEND={2'b0,2'h0}, QOP={2'b0,2'h1}, QCL={2'b0,2'h2}, @@ -76,6 +83,9 @@ module t (/*AUTOARG*/); if (QACK != 4) $stop; if (QRSP != 5) $stop; + if ($size(array5) != 5) $stop; + if ($size(array5i) != 5) $stop; + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_event_control_prev_name_collision.pl b/test_regress/t/t_event_control_prev_name_collision.pl new file mode 100755 index 000000000..0fa78eb28 --- /dev/null +++ b/test_regress/t/t_event_control_prev_name_collision.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(simulator => 1); + +compile( + verilator_flags2 => ["-fno-inline"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_event_control_prev_name_collision.v b/test_regress/t/t_event_control_prev_name_collision.v new file mode 100644 index 000000000..2b95b86c5 --- /dev/null +++ b/test_regress/t/t_event_control_prev_name_collision.v @@ -0,0 +1,98 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 +module S( + input reset, + io_i, + output io_o +); + reg s; + always @(posedge reset) begin + if (reset) begin + s <= 1'h0; + end + else begin + s <= io_i; + end + end + assign io_o = s; +endmodule + +module Q( + input reset_e, + input reset_d, + output ready_e +); + + wire reset_n; + wire io_v; + wire io_e; + S e ( + .io_i (), + .reset (reset_e | ~reset_n), + .io_o (io_e) + ); + S v ( + .io_i (io_e), + .reset (reset_e), + .io_o (io_v) + ); + assign reset_n = ~reset_d; + assign ready_e = io_v; +endmodule + +module Test( + input reset, + output valid +); + wire ready_e; + + Q q ( + .reset_e (reset), + .reset_d (reset), + .ready_e (ready_e) + ); + + assign valid = ready_e; +endmodule + +module Test2( + input reset, + input valid +); + always begin + if (~reset & valid) begin + $fatal; + end + end +endmodule + +module Dut( + input reset +); + wire valid_g; + + Test t ( + .reset (reset), + .valid (valid_g) + ); + Test2 t2 ( + .reset (reset), + .valid (valid_g) + ); +endmodule + +module t (/*AUTOARG*/ + ); + reg [$bits(dut.reset)-1:0] reset; + + Dut dut ( + .reset(reset) + ); + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_event_control_scope_var.pl b/test_regress/t/t_event_control_scope_var.pl new file mode 100755 index 000000000..d78b3ff03 --- /dev/null +++ b/test_regress/t/t_event_control_scope_var.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(simulator => 1); + +compile( + verilator_flags2 => ['-fno-inline', '-Wno-WIDTHTRUNC'], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_event_control_scope_var.v b/test_regress/t/t_event_control_scope_var.v new file mode 100644 index 000000000..e976b1c60 --- /dev/null +++ b/test_regress/t/t_event_control_scope_var.v @@ -0,0 +1,61 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module test_mod(input reg clk, input reg reset, output integer result); + always @(reset) begin + result <= 1; + end +endmodule + +module Dut(input clk); + integer num; + integer result1; + integer result2; + reg reset1; + reg reset2; + initial begin + reset1 = $random; + reset2 = $random; + end + always @(posedge clk) begin + num <= num + 1; + if (num == 5) begin + reset1 <= 1'b1; + end + if (num == 10) begin + // display to prevent optimalization + $display("result1: %d", result1); + $display("result2: %d", result2); + $write("*-* All Finished *-*\n"); + $finish; + end + end + always @(reset1) begin + reset2 <= t.reset; + end + + test_mod t ( + .clk(clk), + .reset(reset1), + .result(result1) + ); + test_mod t2 ( + .clk(clk), + .reset(reset2), + .result(result2)); + endmodule + +module Dut_wrapper(input clk); + + Dut d(.clk(clk)); + Dut d2(.clk(clk)); +endmodule + +module t (/*AUTOARG*/ + clk); + input clk; + Dut_wrapper d_w(.clk(clk)); +endmodule diff --git a/test_regress/t/t_expect.out b/test_regress/t/t_expect.out new file mode 100644 index 000000000..5df2a5f91 --- /dev/null +++ b/test_regress/t/t_expect.out @@ -0,0 +1,29 @@ +%Error-UNSUPPORTED: t/t_expect.v:19:32: Unsupported: ## () cycle delay range expression + 19 | expect (@(posedge clk) a ##1 b) a = 110; + | ^~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_expect.v:19:34: Unsupported: ## (in sequence expression) + 19 | expect (@(posedge clk) a ##1 b) a = 110; + | ^ +%Error-UNSUPPORTED: t/t_expect.v:19:7: Unsupported: expect + 19 | expect (@(posedge clk) a ##1 b) a = 110; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_expect.v:21:32: Unsupported: ## () cycle delay range expression + 21 | expect (@(posedge clk) a ##1 b) else a = 299; + | ^~ +%Error-UNSUPPORTED: t/t_expect.v:21:34: Unsupported: ## (in sequence expression) + 21 | expect (@(posedge clk) a ##1 b) else a = 299; + | ^ +%Error-UNSUPPORTED: t/t_expect.v:21:7: Unsupported: expect + 21 | expect (@(posedge clk) a ##1 b) else a = 299; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_expect.v:23:32: Unsupported: ## () cycle delay range expression + 23 | expect (@(posedge clk) a ##1 b) a = 300; else a = 399; + | ^~ +%Error-UNSUPPORTED: t/t_expect.v:23:34: Unsupported: ## (in sequence expression) + 23 | expect (@(posedge clk) a ##1 b) a = 300; else a = 399; + | ^ +%Error-UNSUPPORTED: t/t_expect.v:23:7: Unsupported: expect + 23 | expect (@(posedge clk) a ##1 b) a = 300; else a = 399; + | ^~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_expect.pl b/test_regress/t/t_expect.pl new file mode 100755 index 000000000..1e49d46fd --- /dev/null +++ b/test_regress/t/t_expect.pl @@ -0,0 +1,20 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 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 + +scenarios(vlt => 1); + +compile( + expect_filename => $Self->{golden_filename}, + verilator_flags2 => ['--assert --timing'], + fails => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_expect.v b/test_regress/t/t_expect.v new file mode 100644 index 000000000..e51b22eaf --- /dev/null +++ b/test_regress/t/t_expect.v @@ -0,0 +1,28 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg a; + reg b; + + initial begin + #10; + expect (@(posedge clk) a ##1 b) a = 110; + #10; + expect (@(posedge clk) a ##1 b) else a = 299; + #10; + expect (@(posedge clk) a ##1 b) a = 300; else a = 399; + end + + // TODO set a/b appropriately - this is just a parsing test currently + +endmodule diff --git a/test_regress/t/t_for_assign.pl b/test_regress/t/t_for_assign.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_for_assign.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_for_assign.v b/test_regress/t/t_for_assign.v new file mode 100644 index 000000000..61e0d6c85 --- /dev/null +++ b/test_regress/t/t_for_assign.v @@ -0,0 +1,42 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0); + +module t (/*AUTOARG*/); + + int a, b; + int sum; + // Complicated assignment cases + + initial begin + sum = 0; + for (integer a=0; a<3; ) begin + a = a + 1; + sum = sum + a; + end + `checkd(sum, 6); + + // foperator_assignment + sum = 0; + for (integer a=0; a<3; a=a+1, sum += a) ; + `checkd(sum, 6); + + // inc_or_dec_expression + sum = 0; + for (integer a=0; a<3; a++, ++sum) ; + `checkd(sum, 3); + + // task_subroutine_call + sum = 0; + for (integer a=0; a<3; a++, sum += $clog2(a)) ; + `checkd(sum, 3); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_for_comma.pl b/test_regress/t/t_for_comma.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_for_comma.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_for_comma.v b/test_regress/t/t_for_comma.v new file mode 100644 index 000000000..86ae62a87 --- /dev/null +++ b/test_regress/t/t_for_comma.v @@ -0,0 +1,61 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define checkc(expc) \ + do begin \ + if (c !== expc) begin \ + $write("%%Error: %s:%0d: a=%0d b=%0d c=%0d expc=%0d\n", `__FILE__,`__LINE__, a, b, c, (expc)); \ + $stop; \ + end \ + a=0; b=0; c=0; \ + end while(0); + +module t (/*AUTOARG*/); + + int a, b, c; + + initial begin + for (; ; ) begin c = c + 1 + a + b; break; end + `checkc(1); + for (; ; a = a + 1) begin c = c + 1 + a + b; break; end + `checkc(1); + for (; ; a = a + 1, b = b + 1) begin c = c + 1 + a + b; break; end + `checkc(1); + for (; a < 3; ) begin c = c + 1 + a + b; break; end + `checkc(1); + for (; a < 3; a = a + 1) begin c = c + 1 + a + b; break; end + `checkc(1); + for (; a < 3; a = a + 1, b = b + 1) begin c = c + 1 + a + b; break; end + `checkc(1); + for (a = 1; a < 3; ) begin c = c + 1 + a + b; a = a + 10; end + `checkc(2); + for (a = 1; a < 3; a = a + 1) begin c = c + 1 + a + b; end + `checkc(5); + for (a = 1; a < 3; a = a + 1, b = b + 1) begin c = c + 1 + a + b; end + `checkc(6); + for (int a = 1; a < 3; ) begin c = c + 1 + a + b; a = a + 10; end + `checkc(2); + for (int a = 1; a < 3; a = a + 1) begin c = c + 1 + a + b; end + `checkc(5); + for (int a = 1; a < 3; a = a + 1, b = b + 1) begin c = c + 1 + a + b; end + `checkc(6); + for (var int a = 1; a < 3; ) begin c = c + 1 + a + b; a = a + 10; end + `checkc(2); + for (var int a = 1; a < 3; a = a + 1) begin c = c + 1 + a + b; end + `checkc(5); + for (var int a = 1; a < 3; a = a + 1, b = b + 1) begin c = c + 1 + a + b; end + `checkc(6); + for (int a = 1, int b = 1; a < 3; ) begin c = c + 1 + a + b; a = a + 10; end + `checkc(3); + for (int a = 1, int b = 1; a < 3; a = a + 1) begin c = c + 1 + a + b; end + `checkc(7); + for (int a = 1, int b = 1; a < 3; a = a + 1, b = b + 1) begin c = c + 1 + a + b; end + `checkc(8); + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_for_comma_bad.out b/test_regress/t/t_for_comma_bad.out deleted file mode 100644 index d430f98c9..000000000 --- a/test_regress/t/t_for_comma_bad.out +++ /dev/null @@ -1,11 +0,0 @@ -%Error-UNSUPPORTED: t/t_for_comma_bad.v:27:23: Unsupported: for loop initialization after the first comma - 27 | for (integer a=0, integer b=0; a<1; ) ; - | ^ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_for_comma_bad.v:28:23: Unsupported: for loop initialization after the first comma - 28 | for (integer a=0, integer b=0; a<1; a=a+1) ; - | ^ -%Error-UNSUPPORTED: t/t_for_comma_bad.v:29:23: Unsupported: for loop initialization after the first comma - 29 | for (integer a=0, integer b=0; a<1; a=a+1, b=b+1) ; - | ^ -%Error: Exiting due to diff --git a/test_regress/t/t_for_comma_bad.v b/test_regress/t/t_for_comma_bad.v deleted file mode 100644 index 384b9c517..000000000 --- a/test_regress/t/t_for_comma_bad.v +++ /dev/null @@ -1,34 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2003 by Wilson Snyder. -// SPDX-License-Identifier: CC0-1.0 - -module t (/*AUTOARG*/); - - integer a, b; - - initial begin - for (; ; ) ; - for (; ; a=a+1) ; - for (; ; a=a+1, b=b+1) ; - for (; a<1; ) ; - for (; a<1; a=a+1) ; - for (; a<1; a=a+1, b=b+1) ; - for (a=0; a<1; ) ; - for (a=0; a<1; a=a+1) ; - for (a=0; a<1; a=a+1, b=b+1) ; - for (integer a=0; a<1; ) ; - for (integer a=0; a<1; a=a+1) ; - for (integer a=0; a<1; a=a+1, b=b+1) ; - for (var integer a=0; a<1; ) ; - for (var integer a=0; a<1; a=a+1) ; - for (var integer a=0; a<1; a=a+1, b=b+1) ; - for (integer a=0, integer b=0; a<1; ) ; - for (integer a=0, integer b=0; a<1; a=a+1) ; - for (integer a=0, integer b=0; a<1; a=a+1, b=b+1) ; - $write("*-* All Finished *-*\n"); - $finish; - end - -endmodule diff --git a/test_regress/t/t_for_disable_dot.out b/test_regress/t/t_for_disable_dot.out new file mode 100644 index 000000000..fe6edd7f7 --- /dev/null +++ b/test_regress/t/t_for_disable_dot.out @@ -0,0 +1,5 @@ +%Error-UNSUPPORTED: t/t_for_disable_dot.v:14:35: Unsupported: disable with '.' + 14 | if (i == 5) disable t.named; + | ^~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_for_disable_dot.pl b/test_regress/t/t_for_disable_dot.pl new file mode 100755 index 000000000..59ba0d6c6 --- /dev/null +++ b/test_regress/t/t_for_disable_dot.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(linter => 1); + +lint( + fails => $Self->{vlt_all}, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_for_disable_dot.v b/test_regress/t/t_for_disable_dot.v new file mode 100644 index 000000000..5e062b077 --- /dev/null +++ b/test_regress/t/t_for_disable_dot.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, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + + int i; + + initial begin + begin : named + for (i = 0; i < 10; ++i) begin : loop + if (i == 5) disable t.named; + end + end + if (i != 5) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_foreach.v b/test_regress/t/t_foreach.v index e7f75f0a5..4cdf96180 100644 --- a/test_regress/t/t_foreach.v +++ b/test_regress/t/t_foreach.v @@ -8,7 +8,7 @@ module t (/*AUTOARG*/); - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE // verilator lint_off WIDTH reg [63:0] sum; // Checked not in objects diff --git a/test_regress/t/t_func_many_return.pl b/test_regress/t/t_func_many_return.pl new file mode 100755 index 000000000..b51fe33a6 --- /dev/null +++ b/test_regress/t/t_func_many_return.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(simulator => 1); + +compile( + verilator_flags2 => ["--comp-limit-blocks 100"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_func_many_return.v b/test_regress/t/t_func_many_return.v new file mode 100644 index 000000000..c8142f24b --- /dev/null +++ b/test_regress/t/t_func_many_return.v @@ -0,0 +1,251 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2013 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + + function automatic string get_csr_name(input logic [11:0] csr_addr); + // verilator no_inline_task + unique case (csr_addr) + 12'd0000: return "xx0000xxxxx"; + 12'd0001: return "xx0001xxxx"; + 12'd1952: return "xx1952xxxxx"; + 12'd1953: return "xx1953xxx1"; + 12'd1954: return "xx1954xxx2"; + 12'd1955: return "xx1955xxx3"; + 12'd1968: return "xx1968xx"; + 12'd1969: return "xx1969x"; + 12'd1970: return "xx1970xxxxxx"; + 12'd0256: return "xx0256xxxxx"; + 12'd0258: return "xx0258xxxxx"; + 12'd0259: return "xx0259xxxxx"; + 12'd0260: return "xx0260x"; + 12'd0261: return "xx0261xxx"; + 12'd0262: return "xx0262xxxxxxxx"; + 12'd2816: return "xx2816xxxx"; + 12'd2818: return "xx2818xxxxxx"; + 12'd2819: return "xx2819xxxxxxxxx3"; + 12'd2820: return "xx2820xxxxxxxxx4"; + 12'd2821: return "xx2821xxxxxxxxx5"; + 12'd2822: return "xx2822xxxxxxxxx6"; + 12'd2823: return "xx2823xxxxxxxxx7"; + 12'd2824: return "xx2824xxxxxxxxx8"; + 12'd2825: return "xx2825xxxxxxxxx9"; + 12'd2826: return "xx2826xxxxxxxxx10"; + 12'd2827: return "xx2827xxxxxxxxx11"; + 12'd2828: return "xx2828xxxxxxxxx12"; + 12'd2829: return "xx2829xxxxxxxxx13"; + 12'd2830: return "xx2830xxxxxxxxx14"; + 12'd2831: return "xx2831xxxxxxxxx15"; + 12'd2832: return "xx2832xxxxxxxxx16"; + 12'd2833: return "xx2833xxxxxxxxx17"; + 12'd2834: return "xx2834xxxxxxxxx18"; + 12'd2835: return "xx2835xxxxxxxxx19"; + 12'd2836: return "xx2836xxxxxxxxx20"; + 12'd2837: return "xx2837xxxxxxxxx21"; + 12'd2838: return "xx2838xxxxxxxxx22"; + 12'd2839: return "xx2839xxxxxxxxx23"; + 12'd2840: return "xx2840xxxxxxxxx24"; + 12'd2841: return "xx2841xxxxxxxxx25"; + 12'd2842: return "xx2842xxxxxxxxx26"; + 12'd2843: return "xx2843xxxxxxxxx27"; + 12'd2844: return "xx2844xxxxxxxxx28"; + 12'd2845: return "xx2845xxxxxxxxx29"; + 12'd2846: return "xx2846xxxxxxxxx30"; + 12'd2847: return "xx2847xxxxxxxxx31"; + 12'd2944: return "xx2944xxxxx"; + 12'd2946: return "xx2946xxxxxxx"; + 12'd2947: return "xx2947xxxxxxxxx3x"; + 12'd2948: return "xx2948xxxxxxxxx4x"; + 12'd2949: return "xx2949xxxxxxxxx5x"; + 12'd2950: return "xx2950xxxxxxxxx6x"; + 12'd2951: return "xx2951xxxxxxxxx7x"; + 12'd2952: return "xx2952xxxxxxxxx8x"; + 12'd2953: return "xx2953xxxxxxxxx9x"; + 12'd2954: return "xx2954xxxxxxxxx10x"; + 12'd2955: return "xx2955xxxxxxxxx11x"; + 12'd2956: return "xx2956xxxxxxxxx12x"; + 12'd2957: return "xx2957xxxxxxxxx13x"; + 12'd2958: return "xx2958xxxxxxxxx14x"; + 12'd2959: return "xx2959xxxxxxxxx15x"; + 12'd2960: return "xx2960xxxxxxxxx16x"; + 12'd2961: return "xx2961xxxxxxxxx17x"; + 12'd2962: return "xx2962xxxxxxxxx18x"; + 12'd2963: return "xx2963xxxxxxxxx19x"; + 12'd2964: return "xx2964xxxxxxxxx20x"; + 12'd2965: return "xx2965xxxxxxxxx21x"; + 12'd2966: return "xx2966xxxxxxxxx22x"; + 12'd2967: return "xx2967xxxxxxxxx23x"; + 12'd2968: return "xx2968xxxxxxxxx24x"; + 12'd2969: return "xx2969xxxxxxxxx25x"; + 12'd2970: return "xx2970xxxxxxxxx26x"; + 12'd2971: return "xx2971xxxxxxxxx27x"; + 12'd2972: return "xx2972xxxxxxxxx28x"; + 12'd2973: return "xx2973xxxxxxxxx29x"; + 12'd2974: return "xx2974xxxxxxxxx30x"; + 12'd2975: return "xx2975xxxxxxxxx31x"; + 12'd0002: return "xx0002x"; + 12'd3072: return "xx3072xxx"; + 12'd3073: return "xx3073xx"; + 12'd3074: return "xx3074xxxxx"; + 12'd3075: return "xx3075xxxxxxxx3"; + 12'd3076: return "xx3076xxxxxxxx4"; + 12'd3077: return "xx3077xxxxxxxx5"; + 12'd3078: return "xx3078xxxxxxxx6"; + 12'd3079: return "xx3079xxxxxxxx7"; + 12'd3080: return "xx3080xxxxxxxx8"; + 12'd3081: return "xx3081xxxxxxxx9"; + 12'd3082: return "xx3082xxxxxxxx10"; + 12'd3083: return "xx3083xxxxxxxx11"; + 12'd3084: return "xx3084xxxxxxxx12"; + 12'd3085: return "xx3085xxxxxxxx13"; + 12'd3086: return "xx3086xxxxxxxx14"; + 12'd3087: return "xx3087xxxxxxxx15"; + 12'd3088: return "xx3088xxxxxxxx16"; + 12'd3089: return "xx3089xxxxxxxx17"; + 12'd3090: return "xx3090xxxxxxxx18"; + 12'd3091: return "xx3091xxxxxxxx19"; + 12'd3092: return "xx3092xxxxxxxx20"; + 12'd3093: return "xx3093xxxxxxxx21"; + 12'd3094: return "xx3094xxxxxxxx22"; + 12'd3095: return "xx3095xxxxxxxx23"; + 12'd3096: return "xx3096xxxxxxxx24"; + 12'd3097: return "xx3097xxxxxxxx25"; + 12'd3098: return "xx3098xxxxxxxx26"; + 12'd3099: return "xx3099xxxxxxxx27"; + 12'd3100: return "xx3100xxxxxxxx28"; + 12'd3101: return "xx3101xxxxxxxx29"; + 12'd3102: return "xx3102xxxxxxxx30"; + 12'd3103: return "xx3103xxxxxxxx31"; + 12'd3200: return "xx3200xxxx"; + 12'd3201: return "xx3201xxx"; + 12'd3202: return "xx3202xxxxxx"; + 12'd3203: return "xx3203xxxxxxxx3x"; + 12'd3204: return "xx3204xxxxxxxx4x"; + 12'd3205: return "xx3205xxxxxxxx5x"; + 12'd3206: return "xx3206xxxxxxxx6x"; + 12'd3207: return "xx3207xxxxxxxx7x"; + 12'd3208: return "xx3208xxxxxxxx8x"; + 12'd3209: return "xx3209xxxxxxxx9x"; + 12'd0320: return "xx0320xxxxxx"; + 12'd3210: return "xx3210xxxxxxxx10x"; + 12'd3211: return "xx3211xxxxxxxx11x"; + 12'd3212: return "xx3212xxxxxxxx12x"; + 12'd3213: return "xx3213xxxxxxxx13x"; + 12'd3214: return "xx3214xxxxxxxx14x"; + 12'd3215: return "xx3215xxxxxxxx15x"; + 12'd3216: return "xx3216xxxxxxxx16x"; + 12'd3217: return "xx3217xxxxxxxx17x"; + 12'd3218: return "xx3218xxxxxxxx18x"; + 12'd3219: return "xx3219xxxxxxxx19x"; + 12'd3220: return "xx3220xxxxxxxx20x"; + 12'd3221: return "xx3221xxxxxxxx21x"; + 12'd3222: return "xx3222xxxxxxxx22x"; + 12'd3223: return "xx3223xxxxxxxx23x"; + 12'd3224: return "xx3224xxxxxxxx24x"; + 12'd3225: return "xx3225xxxxxxxx25x"; + 12'd3226: return "xx3226xxxxxxxx26x"; + 12'd3227: return "xx3227xxxxxxxx27x"; + 12'd3228: return "xx3228xxxxxxxx28x"; + 12'd3229: return "xx3229xxxxxxxx29x"; + 12'd3230: return "xx3230xxxxxxxx30x"; + 12'd3231: return "xx3231xxxxxxxx31x"; + 12'd3857: return "xx3857xxxxxxx"; + 12'd3858: return "xx3858xxxxx"; + 12'd3859: return "xx3859xxxx"; + 12'd3860: return "xx3860xxxxx"; + 12'd0512: return "xx0512xxxxx"; + 12'd0514: return "xx0514xxxxx"; + 12'd0515: return "xx0515xxxxx"; + 12'd0516: return "xx0516x"; + 12'd0517: return "xx0517xxx"; + 12'd0576: return "xx0576xxxxxx"; + 12'd0577: return "xx0577xx"; + 12'd0578: return "xx0578xxxx"; + 12'd0579: return "xx0579xxxxxx"; + 12'd0580: return "xx0580x"; + 12'd0768: return "xx0768xxxxx"; + 12'd0769: return "xx0769xx"; + 12'd0770: return "xx0770xxxxx"; + 12'd0771: return "xx0771xxxxx"; + 12'd0772: return "xx0772x"; + 12'd0773: return "xx0773xxx"; + 12'd0774: return "xx0774xxxxxxxx"; + 12'd0800: return "xx0800xxxxxxxxxxx"; + 12'd0803: return "xx0803xxxxxxx3"; + 12'd0804: return "xx0804xxxxxxx4"; + 12'd0805: return "xx0805xxxxxxx5"; + 12'd0806: return "xx0806xxxxxxx6"; + 12'd0807: return "xx0807xxxxxxx7"; + 12'd0808: return "xx0808xxxxxxx8"; + 12'd0809: return "xx0809xxxxxxx9"; + 12'd0810: return "xx0810xxxxxxx10"; + 12'd0811: return "xx0811xxxxxxx11"; + 12'd0812: return "xx0812xxxxxxx12"; + 12'd0813: return "xx0813xxxxxxx13"; + 12'd0814: return "xx0814xxxxxxx14"; + 12'd0815: return "xx0815xxxxxxx15"; + 12'd0816: return "xx0816xxxxxxx16"; + 12'd0817: return "xx0817xxxxxxx17"; + 12'd0818: return "xx0818xxxxxxx18"; + 12'd0819: return "xx0819xxxxxxx19"; + 12'd0820: return "xx0820xxxxxxx20"; + 12'd0821: return "xx0821xxxxxxx21"; + 12'd0822: return "xx0822xxxxxxx22"; + 12'd0823: return "xx0823xxxxxxx23"; + 12'd0824: return "xx0824xxxxxxx24"; + 12'd0825: return "xx0825xxxxxxx25"; + 12'd0826: return "xx0826xxxxxxx26"; + 12'd0827: return "xx0827xxxxxxx27"; + 12'd0828: return "xx0828xxxxxxx28"; + 12'd0829: return "xx0829xxxxxxx29"; + 12'd0830: return "xx0830xxxxxxx30"; + 12'd0831: return "xx0831xxxxxxx31"; + 12'd0832: return "xx0832xxxxxx"; + 12'd0833: return "xx0833xx"; + 12'd0834: return "xx0834xxxx"; + 12'd0835: return "xx0835xxx"; + 12'd0836: return "xx0836x"; + 12'd0896: return "xx0896xxx"; + 12'd0897: return "xx0897xxxx"; + 12'd0898: return "xx0898xxxx"; + 12'd0899: return "xx0899xxxxx"; + 12'd0900: return "xx0900xxxx"; + 12'd0901: return "xx0901xxxxx"; + 12'd0928: return "xx0928xxxx0"; + 12'd0929: return "xx0929xxxx1"; + 12'd0930: return "xx0930xxxx2"; + 12'd0931: return "xx0931xxxx3"; + 12'd0944: return "xx0944xxxxx0"; + 12'd0945: return "xx0945xxxxx1"; + 12'd0946: return "xx0946xxxxx2"; + 12'd0947: return "xx0947xxxxx3"; + 12'd0948: return "xx0948xxxxx4"; + 12'd0949: return "xx0949xxxxx5"; + 12'd0950: return "xx0950xxxxx6"; + 12'd0951: return "xx0951xxxxx7"; + 12'd0952: return "xx0952xxxxx8"; + 12'd0953: return "xx0953xxxxx9"; + 12'd0954: return "xx0954xxxxx10"; + 12'd0955: return "xx0955xxxxx11"; + 12'd0956: return "xx0956xxxxx12"; + 12'd0957: return "xx0957xxxxx13"; + 12'd0958: return "xx0958xxxxx14"; + 12'd0959: return "xx0959xxxxx15"; + default: return $sformatf("0x%x", csr_addr); + endcase + endfunction + + int i; + + initial begin + if (get_csr_name(12'd0957) != "xx0957xxxxx13") $stop; + if (get_csr_name(12'd2830) != "xx2830xxxxxxxxx14") $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_gen_defparam_multi.out b/test_regress/t/t_gen_defparam_multi.out new file mode 100644 index 000000000..8b751017c --- /dev/null +++ b/test_regress/t/t_gen_defparam_multi.out @@ -0,0 +1,17 @@ +%Error-UNSUPPORTED: t/t_gen_defparam_multi.v:14:17: Unsupported: defparam with no dot + 14 | defparam PAR = 5; + | ^ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_gen_defparam_multi.v:39:24: Unsupported: defparam with more than one dot + 39 | defparam m2.m3.PAR3 = 80; + | ^ +%Error: t/t_gen_defparam_multi.v:39:25: syntax error, unexpected IDENTIFIER, expecting ',' or ';' + 39 | defparam m2.m3.PAR3 = 80; + | ^~~~ +%Error-UNSUPPORTED: t/t_gen_defparam_multi.v:44:24: Unsupported: defparam with more than one dot + 44 | defparam m2.m3.PAR3 = 40; + | ^ +%Error: t/t_gen_defparam_multi.v:44:25: syntax error, unexpected IDENTIFIER, expecting ',' or ';' + 44 | defparam m2.m3.PAR3 = 40; + | ^~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_for_comma_bad.pl b/test_regress/t/t_gen_defparam_multi.pl similarity index 95% rename from test_regress/t/t_for_comma_bad.pl rename to test_regress/t/t_gen_defparam_multi.pl index a60503a1f..a5846c699 100755 --- a/test_regress/t/t_for_comma_bad.pl +++ b/test_regress/t/t_gen_defparam_multi.pl @@ -8,7 +8,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # Version 2.0. # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -scenarios(linter => 1); +scenarios(vlt => 1); lint( fails => 1, diff --git a/test_regress/t/t_gen_defparam_multi.v b/test_regress/t/t_gen_defparam_multi.v new file mode 100644 index 000000000..8f4642bc7 --- /dev/null +++ b/test_regress/t/t_gen_defparam_multi.v @@ -0,0 +1,59 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2012 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + parameter PAR = 3; + + defparam PAR = 5; + + wire [31:0] o2a, o2b, o3a, o3b; + + m1 #(0) m1a(.o2(o2a), .o3(o3a)); + m1 #(1) m1b(.o2(o2b), .o3(o3b)); + + always @ (posedge clk) begin + if (PAR != 5) $stop; + if (o2a != 8) $stop; + if (o2b != 4) $stop; + if (o3a != 80) $stop; + if (o3b != 40) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule + +module m1 (output wire [31:0] o2, + output wire [31:0] o3); + parameter W = 0; + generate + if (W == 0) begin + m2 m2 (.*); + defparam m2.PAR2 = 8; + defparam m2.m3.PAR3 = 80; + end + else begin + m2 m2 (.*); + defparam m2.PAR2 = 4; + defparam m2.m3.PAR3 = 40; + end + endgenerate +endmodule + +module m2 (output wire [31:0] o2, + output wire [31:0] o3); + parameter PAR2 = 20; + assign o2 = PAR2; + m3 m3 (.*); +endmodule + +module m3 (output wire [31:0] o3); + parameter PAR3 = 40; + assign o3 = PAR3; +endmodule diff --git a/test_regress/t/t_gen_defparam_unsup_bad.out b/test_regress/t/t_gen_defparam_unsup_bad.out deleted file mode 100644 index 97a834b03..000000000 --- a/test_regress/t/t_gen_defparam_unsup_bad.out +++ /dev/null @@ -1,8 +0,0 @@ -%Error-UNSUPPORTED: t/t_gen_defparam_unsup_bad.v:9:16: Unsupported: defparam with more than one dot - 9 | defparam a.b.W = 3; - | ^ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: t/t_gen_defparam_unsup_bad.v:9:17: syntax error, unexpected IDENTIFIER, expecting ',' or ';' - 9 | defparam a.b.W = 3; - | ^ -%Error: Exiting due to diff --git a/test_regress/t/t_iff.out b/test_regress/t/t_iff.out index 6680a685b..24dc99876 100644 --- a/test_regress/t/t_iff.out +++ b/test_regress/t/t_iff.out @@ -1,7 +1,17 @@ -%Error: t/t_iff.v:64:15: syntax error, unexpected iff, expecting ')' or ',' or or - 64 | always @(d iff enable == 1) begin +%Error-UNSUPPORTED: t/t_iff.v:66:15: Unsupported: event expression 'iff' + 66 | always @(d iff enable) begin | ^~~ -%Error: t/t_iff.v:69:35: syntax error, unexpected iff, expecting ')' - 69 | assert property (@(posedge clk iff enable) + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_iff.v:71:23: Unsupported: event expression 'iff' + 71 | always @(posedge d iff enable) begin + | ^~~ +%Error-UNSUPPORTED: t/t_iff.v:76:23: Unsupported: event expression 'iff' + 76 | always @(negedge d iff enable) begin + | ^~~ +%Error-UNSUPPORTED: t/t_iff.v:81:20: Unsupported: event expression 'iff' + 81 | always @(edge d iff enable) begin + | ^~~ +%Error-UNSUPPORTED: t/t_iff.v:86:35: Unsupported: event expression 'iff' + 86 | assert property (@(posedge clk iff enable) | ^~~ %Error: Exiting due to diff --git a/test_regress/t/t_iff.pl b/test_regress/t/t_iff.pl index 734924e17..2966ed9a2 100755 --- a/test_regress/t/t_iff.pl +++ b/test_regress/t/t_iff.pl @@ -11,6 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( + verilator_flags2 => ['--timing'], fails => $Self->{vlt_all}, # Verilator unsupported, bug1482, iff not supported expect_filename => $Self->{golden_filename}, ); diff --git a/test_regress/t/t_iff.v b/test_regress/t/t_iff.v index e41a2f746..833bf411f 100644 --- a/test_regress/t/t_iff.v +++ b/test_regress/t/t_iff.v @@ -42,7 +42,7 @@ module t (/*AUTOARG*/ $write("[%0t] cyc==%0d crc=%x sum=%x\n", $time, cyc, crc, sum); if (crc !== 64'hc77bb9b3784ea091) $stop; // What checksum will we end up with (above print should match) -`define EXPECTED_SUM 64'he58508de5310b541 +`define EXPECTED_SUM 64'h390aa8652d33a691 if (sum !== `EXPECTED_SUM) $stop; $write("*-* All Finished *-*\n"); $finish; @@ -59,10 +59,27 @@ module Test output wire [31:0] result); wire enable = crc[32]; - wire [31:0] d = crc[31:0]; - logic [31:0] y; - always @(d iff enable == 1) begin - y <= d; + wire [7:0] d = crc[7:0]; + + + logic [7:0] d0_r; + always @(d iff enable) begin + d0_r <= d; + end + + logic [7:0] d1_r; + always @(posedge d iff enable) begin + d1_r <= d; + end + + logic [7:0] d2_r; + always @(negedge d iff enable) begin + d2_r <= d; + end + + logic [7:0] d3_r; + always @(edge d iff enable) begin + d3_r <= d; end wire reset = (cyc < 10); @@ -71,6 +88,6 @@ module Test (crc != '0)); // Aggregate outputs into a single result vector - assign result = {32'h0, y}; + assign result = {32'h0, d3_r, d2_r, d1_r, d0_r}; endmodule diff --git a/test_regress/t/t_increment_bad.out b/test_regress/t/t_increment_bad.out index 026148082..08749d938 100644 --- a/test_regress/t/t_increment_bad.out +++ b/test_regress/t/t_increment_bad.out @@ -17,9 +17,6 @@ %Error-UNSUPPORTED: t/t_increment_bad.v:27:29: Unsupported: Incrementation in this context. 27 | pos = (a > 0) ? a++ : --b; | ^~ -%Error-UNSUPPORTED: t/t_increment_bad.v:29:24: Unsupported: Incrementation in this context. - 29 | pos = array[0][0]++; - | ^~ %Error-UNSUPPORTED: t/t_increment_bad.v:32:37: Unsupported: Incrementation in this context. 32 | assert property (@(posedge clk) a++ >= 0); | ^~ diff --git a/test_regress/t/t_inst_array_connect.v b/test_regress/t/t_inst_array_connect.v index 48d5af727..af08f7c30 100644 --- a/test_regress/t/t_inst_array_connect.v +++ b/test_regress/t/t_inst_array_connect.v @@ -20,7 +20,7 @@ module t (/*AUTOARG*/ assign inc = 4'b0001; - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE COUNTER UCOUNTER1[N-1:0] ( diff --git a/test_regress/t/t_interface_array_nocolon.v b/test_regress/t/t_interface_array_nocolon.v index 7536b49ce..370ca35fd 100644 --- a/test_regress/t/t_interface_array_nocolon.v +++ b/test_regress/t/t_interface_array_nocolon.v @@ -22,13 +22,13 @@ module t (); wire [2:0] X = 3'b110; - // Should not cause LITENDIAN warning, as no harm in array selections. - // verilator lint_on LITENDIAN + // Should not cause ASCRANGE warning, as no harm in array selections. + // verilator lint_on ASCRANGE foo_intf foo1 [N] (.x(1'b1)); foo_subm sub1 [N] (.x(1'b1)); - // Will cause LITENDIAN warning? - // verilator lint_off LITENDIAN + // Will cause ASCRANGE warning? + // verilator lint_off ASCRANGE foo_intf foos [N] (.x(X)); foo_intf fool [1:3] (.x(X)); foo_intf foom [3:1] (.x(X)); diff --git a/test_regress/t/t_interface_array_nocolon_bad.out b/test_regress/t/t_interface_array_nocolon_bad.out index b189edec7..744b81d20 100644 --- a/test_regress/t/t_interface_array_nocolon_bad.out +++ b/test_regress/t/t_interface_array_nocolon_bad.out @@ -1,19 +1,19 @@ -%Warning-LITENDIAN: t/t_interface_array_nocolon_bad.v:26:26: Big endian instance range connecting to vector: left < right of instance range: [0:2] - : ... In instance t +%Warning-ASCRANGE: t/t_interface_array_nocolon_bad.v:26:26: Ascending instance range connecting to vector: left < right of instance range: [0:2] + : ... In instance t 26 | foo_intf foos [N] (.x(X)); | ^ - ... For warning description see https://verilator.org/warn/LITENDIAN?v=latest - ... Use "/* verilator lint_off LITENDIAN */" and lint_on around source to disable this message. -%Warning-LITENDIAN: t/t_interface_array_nocolon_bad.v:27:28: Big endian instance range connecting to vector: left < right of instance range: [1:3] - : ... In instance t + ... For warning description see https://verilator.org/warn/ASCRANGE?v=latest + ... Use "/* verilator lint_off ASCRANGE */" and lint_on around source to disable this message. +%Warning-ASCRANGE: t/t_interface_array_nocolon_bad.v:27:28: Ascending instance range connecting to vector: left < right of instance range: [1:3] + : ... In instance t 27 | foo_intf fool [1:3] (.x(X)); | ^ -%Warning-LITENDIAN: t/t_interface_array_nocolon_bad.v:30:26: Big endian instance range connecting to vector: left < right of instance range: [0:2] - : ... In instance t +%Warning-ASCRANGE: t/t_interface_array_nocolon_bad.v:30:26: Ascending instance range connecting to vector: left < right of instance range: [0:2] + : ... In instance t 30 | foo_subm subs [N] (.x(X)); | ^ -%Warning-LITENDIAN: t/t_interface_array_nocolon_bad.v:31:28: Big endian instance range connecting to vector: left < right of instance range: [1:3] - : ... In instance t +%Warning-ASCRANGE: t/t_interface_array_nocolon_bad.v:31:28: Ascending instance range connecting to vector: left < right of instance range: [1:3] + : ... In instance t 31 | foo_subm subl [1:3] (.x(X)); | ^ %Error: Exiting due to diff --git a/test_regress/t/t_interface_array_nocolon_bad.v b/test_regress/t/t_interface_array_nocolon_bad.v index 94431d640..088f38ef1 100644 --- a/test_regress/t/t_interface_array_nocolon_bad.v +++ b/test_regress/t/t_interface_array_nocolon_bad.v @@ -22,7 +22,7 @@ module t (); wire [2:0] X = 3'b110; - // Will cause LITENDIAN warning? + // Will cause ASCRANGE warning? foo_intf foos [N] (.x(X)); // bad foo_intf fool [1:3] (.x(X)); // bad foo_intf foom [3:1] (.x(X)); // ok diff --git a/test_regress/t/t_interface_gen13.pl b/test_regress/t/t_interface_gen13.pl new file mode 100755 index 000000000..f60fd309f --- /dev/null +++ b/test_regress/t/t_interface_gen13.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2009 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_interface_gen13.v b/test_regress/t/t_interface_gen13.v new file mode 100644 index 000000000..0435dda0e --- /dev/null +++ b/test_regress/t/t_interface_gen13.v @@ -0,0 +1,59 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty. +// SPDX-License-Identifier: CC0-1.0 + +// bug998 + +interface intf + #(parameter PARAM = 0) + (); + + int p1; + generate + initial p1 = 1; + endgenerate + + int p2; + generate begin + initial p2 = 1; + end + endgenerate + + int p3; + int p3_no; + if (PARAM == 1) initial p3 = 1; else initial p3_no = 1; + + int p4; + int p4_no; + case (PARAM) + 1: initial p4 = 1; + default: initial p4_no = 1; + endcase + + int p5; + for (genvar g=0; g<=PARAM; ++g) initial p5 = 1; + +endinterface + +module t(/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + intf #(.PARAM(1)) my_intf (); + + always @ (posedge clk) begin + if (my_intf.p1 != 1) $stop; + if (my_intf.p2 != 1) $stop; + if (my_intf.p3 != 1) $stop; + if (my_intf.p3_no != 0) $stop; + if (my_intf.p4 != 1) $stop; + if (my_intf.p4_no != 0) $stop; + if (my_intf.p5 != 1) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_typedef.v b/test_regress/t/t_interface_typedef.v index a8faecb9b..c3491894d 100644 --- a/test_regress/t/t_interface_typedef.v +++ b/test_regress/t/t_interface_typedef.v @@ -50,6 +50,7 @@ module sub #( initial begin struct_t substruct; substruct.data = '1; + `checkh($bits(struct_t), EXP_WIDTH); `checkh(substruct.data, expval); end diff --git a/test_regress/t/t_let.out b/test_regress/t/t_let.out new file mode 100644 index 000000000..aca0f5993 --- /dev/null +++ b/test_regress/t/t_let.out @@ -0,0 +1,41 @@ +%Error-UNSUPPORTED: t/t_let.v:8:4: Unsupported: let + 8 | let P = 11; + | ^~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_let.v:13:4: Unsupported: let + 13 | let A = 10; + | ^~~ +%Error-UNSUPPORTED: t/t_let.v:14:4: Unsupported: let + 14 | let B() = 20; + | ^~~ +%Error-UNSUPPORTED: t/t_let.v:13:14: Unsupported: let ports + 13 | let A = 10; + | ^ +%Error-UNSUPPORTED: t/t_let.v:15:4: Unsupported: let + 15 | let C(a) = 30 + a; + | ^~~ +%Error-UNSUPPORTED: t/t_let.v:15:13: Unsupported: let ports + 15 | let C(a) = 30 + a; + | ^ +%Error-UNSUPPORTED: t/t_let.v:16:4: Unsupported: let + 16 | let D(a, b) = 30 + a + b; + | ^~~ +%Error-UNSUPPORTED: t/t_let.v:16:16: Unsupported: let ports + 16 | let D(a, b) = 30 + a + b; + | ^ +%Error-UNSUPPORTED: t/t_let.v:17:4: Unsupported: let + 17 | let E(a, b=7) = 30 + a + b; + | ^~~ +%Error-UNSUPPORTED: t/t_let.v:18:10: Unsupported: let untyped ports + 18 | let F(untyped a) = 30 + a; + | ^~~~~~~ +%Error-UNSUPPORTED: t/t_let.v:18:4: Unsupported: let + 18 | let F(untyped a) = 30 + a; + | ^~~ +%Error-UNSUPPORTED: t/t_let.v:19:10: Unsupported: let ports + 19 | let G(int a) = 30 + a; + | ^~~ +%Error-UNSUPPORTED: t/t_let.v:19:4: Unsupported: let + 19 | let G(int a) = 30 + a; + | ^~~ +%Error: Exiting due to diff --git a/test_regress/t/t_let.pl b/test_regress/t/t_let.pl new file mode 100755 index 000000000..c18ffc8f0 --- /dev/null +++ b/test_regress/t/t_let.pl @@ -0,0 +1,23 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2023 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt => 1); + +compile( + expect_filename => $Self->{golden_filename}, + fails => 1, + ); + +#execute( +# check_finished => 1, +# ); + +ok(1); +1; diff --git a/test_regress/t/t_let.v b/test_regress/t/t_let.v new file mode 100644 index 000000000..b8d010fb3 --- /dev/null +++ b/test_regress/t/t_let.v @@ -0,0 +1,36 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +package Pkg; + let P = 11; +endpackage + +module t(/*AUTOARG*/); + + let A = 10; + let B() = 20; + let C(a) = 30 + a; + let D(a, b) = 30 + a + b; + let E(a, b=7) = 30 + a + b; + let F(untyped a) = 30 + a; + let G(int a) = 30 + a; + + initial begin + if (A != 10) $stop; + if (A() != 10) $stop; + if (B != 20) $stop; + if (B() != 20) $stop; + if (C(1) != (30 + 1)) $stop; + if (D(1, 2) != (30 + 1 + 2)) $stop; + if (E(1) != (30 + 1 + 7)) $stop; + if (F(1) != (30 + 1)) $stop; + if (G(1) != (30 + 1)) $stop; + if (Pkg::P != 11) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_let_bad.out b/test_regress/t/t_let_bad.out new file mode 100644 index 000000000..ce76ac770 --- /dev/null +++ b/test_regress/t/t_let_bad.out @@ -0,0 +1,11 @@ +%Error-UNSUPPORTED: t/t_let_bad.v:9:4: Unsupported: let + 9 | let NO_ARG = 10; + | ^~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_let_bad.v:9:19: Unsupported: let ports + 9 | let NO_ARG = 10; + | ^ +%Error-UNSUPPORTED: t/t_let_bad.v:10:4: Unsupported: let + 10 | let ONE_ARG(a) = 10; + | ^~~ +%Error: Exiting due to diff --git a/test_regress/t/t_let_bad.pl b/test_regress/t/t_let_bad.pl new file mode 100755 index 000000000..882bc2418 --- /dev/null +++ b/test_regress/t/t_let_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2023 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(linter => 1); + +compile( + expect_filename => $Self->{golden_filename}, + fails => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_let_bad.v b/test_regress/t/t_let_bad.v new file mode 100644 index 000000000..816b46651 --- /dev/null +++ b/test_regress/t/t_let_bad.v @@ -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, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t(/*AUTOARG*/); + + let NO_ARG = 10; + let ONE_ARG(a) = 10; + + initial begin + if (NO_ARG(10) != 10) $stop; // BAD + if (ONE_ARG() != 10) $stop; // BAD + if (ONE_ARG(10, 20) != 10) $stop; // BAD + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_lib_prot.v b/test_regress/t/t_lib_prot.v index 7fa639835..464f91426 100644 --- a/test_regress/t/t_lib_prot.v +++ b/test_regress/t/t_lib_prot.v @@ -57,10 +57,10 @@ module t #(parameter GATED_CLK = 0) (/*AUTOARG*/ logic [128:0] s129_out; logic [3:0] [31:0] s4x32_in; logic [3:0] [31:0] s4x32_out; - /*verilator lint_off LITENDIAN*/ + /*verilator lint_off ASCRANGE*/ logic [0:15] s6x16up_in[0:1][2:0]; logic [0:15] s6x16up_out[0:1][2:0]; - /*verilator lint_on LITENDIAN*/ + /*verilator lint_on ASCRANGE*/ logic [15:0] s8x16up_in[1:0][0:3]; logic [15:0] s8x16up_out[1:0][0:3]; logic [15:0] s8x16up_3d_in[1:0][0:1][0:1]; diff --git a/test_regress/t/t_lib_prot_secret.v b/test_regress/t/t_lib_prot_secret.v index 1ed4dae83..44e055cfe 100644 --- a/test_regress/t/t_lib_prot_secret.v +++ b/test_regress/t/t_lib_prot_secret.v @@ -27,10 +27,10 @@ module secret #(parameter GATED_CLK = 0) output logic [128:0] s129_out, input [3:0] [31:0] s4x32_in, output logic [3:0] [31:0] s4x32_out, - /*verilator lint_off LITENDIAN*/ + /*verilator lint_off ASCRANGE*/ input [0:15] s6x16up_in[0:1][2:0], output logic [0:15] s6x16up_out[0:1][2:0], - /*verilator lint_on LITENDIAN*/ + /*verilator lint_on ASCRANGE*/ input [15:0] s8x16up_in[1:0][0:3], output logic [15:0] s8x16up_out[1:0][0:3], input [15:0] s8x16up_3d_in[1:0][0:1][0:1], diff --git a/test_regress/t/t_lint_historical.v b/test_regress/t/t_lint_historical.v index 8647a070c..b1c1caf24 100644 --- a/test_regress/t/t_lint_historical.v +++ b/test_regress/t/t_lint_historical.v @@ -45,7 +45,7 @@ module t; // verilator lint_off INITIALDLY // verilator lint_off INSECURE // verilator lint_off LATCH - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE // verilator lint_off MODDUP // verilator lint_off MULTIDRIVEN // verilator lint_off MULTITOP diff --git a/test_regress/t/t_math_real.v b/test_regress/t/t_math_real.v index 8798f1cdb..661837d61 100644 --- a/test_regress/t/t_math_real.v +++ b/test_regress/t/t_math_real.v @@ -138,6 +138,16 @@ module t (/*AUTOARG*/ r = real'(96'shf0000000_00000000_00000000); if (r != -4951760157141521099596496896.0) $stop; + r = 1.5; + if (r++ != 1.5) $stop; + if (r != 2.5) $stop; + if (r-- != 2.5) $stop; + if (r != 1.5) $stop; + if (++r != 2.5) $stop; + if (r != 2.5) $stop; + if (--r != 1.5) $stop; + if (r != 1.5) $stop; + r = 1.23456; s = $sformatf("%g", r); `checks(s, "1.23456"); diff --git a/test_regress/t/t_math_wide_inc.pl b/test_regress/t/t_math_wide_inc.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_math_wide_inc.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_wide_inc.v b/test_regress/t/t_math_wide_inc.v new file mode 100644 index 000000000..1d62ca03f --- /dev/null +++ b/test_regress/t/t_math_wide_inc.v @@ -0,0 +1,89 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2023 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + integer i; + reg [6:0] w7; + reg [14:0] w15; + reg [30:0] w31; + reg [62:0] w63; + reg [94:0] w95; + + integer cyc = 0; + + // Test loop + always @ (posedge clk) begin +`ifdef TEST_VERBOSE + $write("[%0t] cyc==%0d\n", $time, cyc); +`endif + cyc <= cyc + 1; + if (cyc==0) begin + // Setup + w7 = {7{1'b1}}; + w15 = {15{1'b1}}; + w31 = {31{1'b1}}; + w63 = {63{1'b1}}; + w95 = {95{1'b1}}; + end + else if (cyc == 1) begin + if (w7++ != {7{1'b1}}) $stop; + if (w7 != {7{1'b0}}) $stop; + if (w7-- != {7{1'b0}}) $stop; + if (w7 != {7{1'b1}}) $stop; + if (++w7 != {7{1'b0}}) $stop; + if (w7 != {7{1'b0}}) $stop; + if (--w7 != {7{1'b1}}) $stop; + if (w7 != {7{1'b1}}) $stop; + + if (w15++ != {15{1'b1}}) $stop; + if (w15 != {15{1'b0}}) $stop; + if (w15-- != {15{1'b0}}) $stop; + if (w15 != {15{1'b1}}) $stop; + if (++w15 != {15{1'b0}}) $stop; + if (w15 != {15{1'b0}}) $stop; + if (--w15 != {15{1'b1}}) $stop; + if (w15 != {15{1'b1}}) $stop; + + if (w31++ != {31{1'b1}}) $stop; + if (w31 != {31{1'b0}}) $stop; + if (w31-- != {31{1'b0}}) $stop; + if (w31 != {31{1'b1}}) $stop; + if (++w31 != {31{1'b0}}) $stop; + if (w31 != {31{1'b0}}) $stop; + if (--w31 != {31{1'b1}}) $stop; + if (w31 != {31{1'b1}}) $stop; + + if (w63++ != {63{1'b1}}) $stop; + if (w63 != {63{1'b0}}) $stop; + if (w63-- != {63{1'b0}}) $stop; + if (w63 != {63{1'b1}}) $stop; + if (++w63 != {63{1'b0}}) $stop; + if (w63 != {63{1'b0}}) $stop; + if (--w63 != {63{1'b1}}) $stop; + if (w63 != {63{1'b1}}) $stop; + + if (w95++ != {95{1'b1}}) $stop; + if (w95 != {95{1'b0}}) $stop; + if (w95-- != {95{1'b0}}) $stop; + if (w95 != {95{1'b1}}) $stop; + if (++w95 != {95{1'b0}}) $stop; + if (w95 != {95{1'b0}}) $stop; + if (--w95 != {95{1'b1}}) $stop; + if (w95 != {95{1'b1}}) $stop; + end + else if (cyc==99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_mem_fifo.v b/test_regress/t/t_mem_fifo.v index e76ec783a..ca1e13940 100644 --- a/test_regress/t/t_mem_fifo.v +++ b/test_regress/t/t_mem_fifo.v @@ -89,9 +89,9 @@ module fifo (/*AUTOARG*/ reg [65:0] outData; // verilator lint_off VARHIDDEN - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE reg [65:0] fifo[0:fifoDepth-1]; - // verilator lint_on LITENDIAN + // verilator lint_on ASCRANGE // verilator lint_on VARHIDDEN //reg [65:0] temp; diff --git a/test_regress/t/t_mem_first.v b/test_regress/t/t_mem_first.v index 372032a77..dfda7d9cb 100644 --- a/test_regress/t/t_mem_first.v +++ b/test_regress/t/t_mem_first.v @@ -12,13 +12,13 @@ module t (/*AUTOARG*/ input clk; integer _mode; initial _mode = 0; - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE reg [7:0] mem_narrow [0:31]; //surefire lint_off_line RD_WRT WRTWRT NBAJAM reg [77:0] mem_wide [1024:0]; //surefire lint_off_line RD_WRT WRTWRT NBAJAM reg [7:0] mem_dly_narrow [0:1]; //surefire lint_off_line RD_WRT WRTWRT NBAJAM reg [77:0] mem_dly_wide [1:0]; //surefire lint_off_line RD_WRT WRTWRT NBAJAM reg [34:0] vec_wide; - // verilator lint_on LITENDIAN + // verilator lint_on ASCRANGE reg [31:0] wrd0 [15:0]; wire [3:0] sel = 4'h3; diff --git a/test_regress/t/t_mem_multidim.v b/test_regress/t/t_mem_multidim.v index 9f68b4d5d..a81d5319d 100644 --- a/test_regress/t/t_mem_multidim.v +++ b/test_regress/t/t_mem_multidim.v @@ -11,7 +11,7 @@ module t (/*AUTOARG*/ input clk; - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE // verilator lint_off BLKANDNBLK // 3 3 4 reg [71:0] memw [2:0][1:3][5:2]; @@ -29,7 +29,7 @@ module t (/*AUTOARG*/ integer imem[2:0][1:3]; reg [2:0] cstyle[2]; - // verilator lint_on LITENDIAN + // verilator lint_on ASCRANGE initial begin for (i0=0; i0<3; i0=i0+1) begin diff --git a/test_regress/t/t_mem_multiwire.v b/test_regress/t/t_mem_multiwire.v index 24a53f502..15569e2f7 100644 --- a/test_regress/t/t_mem_multiwire.v +++ b/test_regress/t/t_mem_multiwire.v @@ -11,10 +11,10 @@ module t (/*AUTOARG*/ input clk; - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE wire [7:0] array [2:0][1:3]; wire [7:0] arrayNoColon [2][3]; - // verilator lint_on LITENDIAN + // verilator lint_on ASCRANGE integer cyc; initial cyc = 0; integer i0,i1,i2; diff --git a/test_regress/t/t_metacmt_onoff.out b/test_regress/t/t_metacmt_onoff.out index 4c6ca3bf2..7b912780c 100644 --- a/test_regress/t/t_metacmt_onoff.out +++ b/test_regress/t/t_metacmt_onoff.out @@ -1,11 +1,11 @@ -%Warning-LITENDIAN: t/t_metacmt_onoff.v:8:8: Big bit endian vector: left < right of bit range: [0:1] - : ... In instance t - 8 | reg [0:1] show1; /*verilator lint_off LITENDIAN*/ reg [0:2] ign2; /*verilator lint_on LITENDIAN*/ reg [0:3] show3; +%Warning-ASCRANGE: t/t_metacmt_onoff.v:8:8: Ascending bit range vector: left < right of bit range: [0:1] + : ... In instance t + 8 | reg [0:1] show1; /*verilator lint_off ASCRANGE*/ reg [0:2] ign2; /*verilator lint_on ASCRANGE*/ reg [0:3] show3; | ^ - ... For warning description see https://verilator.org/warn/LITENDIAN?v=latest - ... Use "/* verilator lint_off LITENDIAN */" and lint_on around source to disable this message. -%Warning-LITENDIAN: t/t_metacmt_onoff.v:8:109: Big bit endian vector: left < right of bit range: [0:3] - : ... In instance t - 8 | reg [0:1] show1; /*verilator lint_off LITENDIAN*/ reg [0:2] ign2; /*verilator lint_on LITENDIAN*/ reg [0:3] show3; - | ^ + ... For warning description see https://verilator.org/warn/ASCRANGE?v=latest + ... Use "/* verilator lint_off ASCRANGE */" and lint_on around source to disable this message. +%Warning-ASCRANGE: t/t_metacmt_onoff.v:8:107: Ascending bit range vector: left < right of bit range: [0:3] + : ... In instance t + 8 | reg [0:1] show1; /*verilator lint_off ASCRANGE*/ reg [0:2] ign2; /*verilator lint_on ASCRANGE*/ reg [0:3] show3; + | ^ %Error: Exiting due to diff --git a/test_regress/t/t_metacmt_onoff.v b/test_regress/t/t_metacmt_onoff.v index 7b7c5735e..91dc5eeae 100644 --- a/test_regress/t/t_metacmt_onoff.v +++ b/test_regress/t/t_metacmt_onoff.v @@ -5,7 +5,7 @@ module t; // Test turning on and off a message on the same line; only middle reg shouldn't warn - reg [0:1] show1; /*verilator lint_off LITENDIAN*/ reg [0:2] ign2; /*verilator lint_on LITENDIAN*/ reg [0:3] show3; + reg [0:1] show1; /*verilator lint_off ASCRANGE*/ reg [0:2] ign2; /*verilator lint_on ASCRANGE*/ reg [0:3] show3; initial begin $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_mod_interface_array1.v b/test_regress/t/t_mod_interface_array1.v index bd497af90..f16a65b41 100644 --- a/test_regress/t/t_mod_interface_array1.v +++ b/test_regress/t/t_mod_interface_array1.v @@ -44,9 +44,9 @@ module t logic [N-1:0] a_in; logic [N-1:0] a_out; logic [N-1:0] ack_out; - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE a_if #(.PARAM(1)) tl_intf [N] (); - // verilator lint_on LITENDIAN + // verilator lint_on ASCRANGE intf_source source(a_in, tl_intf); intf_sink sink(a_out, tl_intf); diff --git a/test_regress/t/t_mod_interface_array2.v b/test_regress/t/t_mod_interface_array2.v index b7f7647f8..b2f15dc04 100644 --- a/test_regress/t/t_mod_interface_array2.v +++ b/test_regress/t/t_mod_interface_array2.v @@ -5,7 +5,7 @@ // SPDX-License-Identifier: CC0-1.0 parameter N = 4; -// verilator lint_off LITENDIAN +// verilator lint_off ASCRANGE interface a_if #(parameter PARAM = 0) (); logic long_name; diff --git a/test_regress/t/t_nettype.out b/test_regress/t/t_nettype.out index 6811cc566..dc401b4c9 100644 --- a/test_regress/t/t_nettype.out +++ b/test_regress/t/t_nettype.out @@ -1,11 +1,14 @@ -%Error-UNSUPPORTED: t/t_nettype.v:24:4: Unsupported: nettype - 24 | nettype real real1_n with Pkg::resolver; +%Error-UNSUPPORTED: t/t_nettype.v:25:4: Unsupported: nettype + 25 | nettype real real1_n with Pkg::resolver; | ^~~~~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_nettype.v:28:4: Unsupported: nettype - 28 | nettype real real2_n with local_resolver; +%Error-UNSUPPORTED: t/t_nettype.v:29:4: Unsupported: nettype + 29 | nettype real real2_n with local_resolver; | ^~~~~~~ -%Error-UNSUPPORTED: t/t_nettype.v:33:4: Unsupported: nettype - 33 | nettype real2_n real3_n; +%Error-UNSUPPORTED: t/t_nettype.v:34:4: Unsupported: nettype + 34 | nettype real2_n real3_n; + | ^~~~~~~ +%Error-UNSUPPORTED: t/t_nettype.v:38:4: Unsupported: nettype + 38 | nettype Pkg::real_t real4_n with Pkg::resolver; | ^~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_nettype.v b/test_regress/t/t_nettype.v index 312a5960f..c81792601 100644 --- a/test_regress/t/t_nettype.v +++ b/test_regress/t/t_nettype.v @@ -5,6 +5,7 @@ // SPDX-License-Identifier: CC0-1.0 package Pkg; + typedef real real_t; real last_resolve; function automatic real resolver(input real drivers[]); @@ -34,6 +35,10 @@ module t(/*AUTOARG*/); real3_n real3; assign real3 = 1.23; + nettype Pkg::real_t real4_n with Pkg::resolver; + real4_n real4; + assign real4 = 1.23; + // TODO when implement net types need to check multiple driver cases, across // submodules diff --git a/test_regress/t/t_param_mintypmax.v b/test_regress/t/t_param_mintypmax.v index dd9acdb5a..dfc793551 100644 --- a/test_regress/t/t_param_mintypmax.v +++ b/test_regress/t/t_param_mintypmax.v @@ -8,10 +8,23 @@ module t (/*AUTOARG*/); parameter MTM = (1:2:3); + sub sub (); + //UNSUP sub #(.MTM(10:20:30)) sub20name (); + //UNSUP sub #(.MTM(100:200)) sub200name (); + //UNSUP sub #(10:20:30) sub20pos (); + //UNSUP sub #(100:200) sub200pos (); + initial begin if (MTM != 2) $stop; + //UNSUP if (sub20pos.MTM != 20) $stop; + //UNSUP if (sub200pos.MTM != 200) $stop; + //UNSUP if (sub20name.MTM != 20) $stop; + //UNSUP if (sub200name.MTM != 200) $stop; $write("*-* All Finished *-*\n"); $finish; end endmodule + +module sub #(parameter MTM = (1:2:3)) (); +endmodule diff --git a/test_regress/t/t_param_module.v b/test_regress/t/t_param_module.v index 18369db2c..c83a8e805 100644 --- a/test_regress/t/t_param_module.v +++ b/test_regress/t/t_param_module.v @@ -7,7 +7,7 @@ // used in the test module to set the value of MSB. A number of warnings and // errors follow, starting with: // -// %Warning-LITENDIAN: t/t_param_module.v:42: Big bit endian vector: MSB +// %Warning-ASCRANGE: t/t_param_module.v:42: Ascending bit range vector: MSB // < LSB of bit range: -17:0 // // This file ONLY is placed into the Public Domain, for any use, without diff --git a/test_regress/t/t_param_repl.v b/test_regress/t/t_param_repl.v index 0cc2c107a..cc7b04d40 100644 --- a/test_regress/t/t_param_repl.v +++ b/test_regress/t/t_param_repl.v @@ -21,9 +21,9 @@ module t (/*AUTOARG*/ parameter DWORDS_LOG2 = 7; parameter DWORDS = (1< 1); compile( - fails => $Self->{vlt_all}, - expect_filename => $Self->{golden_filename}, ); execute( - check_finished => 1, - ) if !$Self->{vlt_all}; + fails => $Self->{vlt_all}, + expect_filename => $Self->{golden_filename}, + check_finished => !$Self->{vlt_all}, + ); ok(1); 1; diff --git a/test_regress/t/t_process_bad.out b/test_regress/t/t_process_bad.out index 3e6f927d1..0c696d1d4 100644 --- a/test_regress/t/t_process_bad.out +++ b/test_regress/t/t_process_bad.out @@ -1,9 +1,9 @@ -%Error: t/t_process_bad.v:12:11: Forward typedef used as class/package does not resolve to class/package: 'process' - 12 | p = process::self(); - | ^~~~~~~ -%Error: t/t_process_bad.v:8:4: Can't find typedef: 'process' - 8 | process p; - | ^~~~~~~ -%Error: Internal Error: t/t_process_bad.v:12:11: ../V3LinkDot.cpp:#: Bad package link - 12 | p = process::self(); - | ^~~~~~~ +%Error: t/t_process_bad.v:13:13: Class method 'bad_method' not found in class 'process' + : ... In instance t + 13 | if (p.bad_method() != 0) $stop; + | ^~~~~~~~~~ +%Error: t/t_process_bad.v:15:9: Class method 'bad_method_2' not found in class 'process' + : ... In instance t + 15 | p.bad_method_2(); + | ^~~~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_process_std.out b/test_regress/t/t_process_std.out index 74d069569..ab04bc4e8 100644 --- a/test_regress/t/t_process_std.out +++ b/test_regress/t/t_process_std.out @@ -1,24 +1,3 @@ -%Error: t/t_process.v:26:11: Forward typedef used as class/package does not resolve to class/package: 'process' - 26 | p = process::self(); - | ^~~~~~~ -%Error: t/t_process.v:27:25: Forward typedef used as class/package does not resolve to class/package: 'process' - 27 | if (p.status() != process::RUNNING) $stop; - | ^~~~~~~ -%Error: t/t_process.v:28:25: Forward typedef used as class/package does not resolve to class/package: 'process' - 28 | if (p.status() == process::WAITING) $stop; - | ^~~~~~~ -%Error: t/t_process.v:29:25: Forward typedef used as class/package does not resolve to class/package: 'process' - 29 | if (p.status() == process::SUSPENDED) $stop; - | ^~~~~~~ -%Error: t/t_process.v:30:25: Forward typedef used as class/package does not resolve to class/package: 'process' - 30 | if (p.status() == process::KILLED) $stop; - | ^~~~~~~ -%Error: t/t_process.v:31:25: Forward typedef used as class/package does not resolve to class/package: 'process' - 31 | if (p.status() == process::FINISHED) $stop; - | ^~~~~~~ -%Error: t/t_process.v:22:4: Can't find typedef: 'process' - 22 | process p; - | ^~~~~~~ -%Error: Internal Error: t/t_process.v:26:11: ../V3LinkDot.cpp:#: Bad package link - 26 | p = process::self(); - | ^~~~~~~ +[0] %Error: verilated_std.sv:154: Assertion failed in top.std.process.set_randstate: std::process::set_randstate() not supported +%Error: verilated_std.sv:154: Verilog $stop +Aborting... diff --git a/test_regress/t/t_process_std.pl b/test_regress/t/t_process_std.pl index 0c23347b2..64cabbccb 100755 --- a/test_regress/t/t_process_std.pl +++ b/test_regress/t/t_process_std.pl @@ -14,13 +14,13 @@ top_filename("t/t_process.v"); compile( v_flags2 => ["+define+T_PROCESS+std::process"], + ); + +execute( + check_finished => !$Self->{vlt_all}, fails => $Self->{vlt_all}, expect_filename => $Self->{golden_filename}, ); -execute( - check_finished => 1, - ) if !$Self->{vlt_all}; - ok(1); 1; diff --git a/test_regress/t/t_protect_ids_key.out b/test_regress/t/t_protect_ids_key.out index 5f362ac43..117c4a728 100644 --- a/test_regress/t/t_protect_ids_key.out +++ b/test_regress/t/t_protect_ids_key.out @@ -28,7 +28,7 @@ - + diff --git a/test_regress/t/t_queue_back.pl b/test_regress/t/t_queue_back.pl new file mode 100755 index 000000000..1aa73f80a --- /dev/null +++ b/test_regress/t/t_queue_back.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_queue_back.v b/test_regress/t/t_queue_back.v new file mode 100644 index 000000000..1d8d2bb90 --- /dev/null +++ b/test_regress/t/t_queue_back.v @@ -0,0 +1,36 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t(/*AUTOARG*/); + + int q[$]; + int r; + + initial begin + q = { 20, 30, 40 }; + + r = q[$]; + if (r != 40) $stop; + + r = q[$-1]; + if (r != 30) $stop; + + q = q[0:$-1]; // void'(q.pop_back()) or q.delete(q.size-1) + if (q.size != 2) $stop; + if (q[0] != 20) $stop; + if (q[1] != 30) $stop; + + q = { 20, 30, 40 }; + q = q[$-1:$]; + if (q.size != 2) $stop; + if (q[0] != 30) $stop; + if (q[1] != 40) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_queue_bounded.v b/test_regress/t/t_queue_bounded.v index e6020465b..05653887c 100644 --- a/test_regress/t/t_queue_bounded.v +++ b/test_regress/t/t_queue_bounded.v @@ -6,7 +6,9 @@ module t (/*AUTOARG*/); - int q[$ : 2]; // Shall not go higher than [2], i.e. size 3 + localparam TWO = 2; + + int q[$ : TWO]; // Shall not go higher than [2], i.e. size 3 initial begin q.push_front(3); diff --git a/test_regress/t/t_queue_struct.pl b/test_regress/t/t_queue_struct.pl new file mode 100755 index 000000000..9a15dd2cc --- /dev/null +++ b/test_regress/t/t_queue_struct.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2019 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_queue_struct.v b/test_regress/t/t_queue_struct.v new file mode 100644 index 000000000..e2f6b6b36 --- /dev/null +++ b/test_regress/t/t_queue_struct.v @@ -0,0 +1,35 @@ +// 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 + +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0); + +module t (/*AUTOARG*/); + + typedef struct { + int b[$]; + } st_t; + + function automatic st_t bar(); + // verilator no_inline_task + for (int i = 0; i < 4; ++i) begin + bar.b.push_back(i); + end + endfunction // bar + + st_t res; + + initial begin + res = bar(); + `checkd(res.b[0], 0); + `checkd(res.b[1], 1); + `checkd(res.b[2], 2); + `checkd(res.b[3], 3); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_randomize.out b/test_regress/t/t_randomize.out index 334f02e8a..82e50c3f1 100644 --- a/test_regress/t/t_randomize.out +++ b/test_regress/t/t_randomize.out @@ -2,12 +2,31 @@ 11 | extern constraint ex; | ^~~~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Warning-CONSTRAINTIGN: t/t_randomize.v:13:4: Constraint ignored (unsupported) + 13 | constraint a { header > 0 && header < 1000; } + | ^~~~~~~~~~ + ... Use "/* verilator lint_off CONSTRAINTIGN */" and lint_on around source to disable this message. +%Warning-CONSTRAINTIGN: t/t_randomize.v:14:4: Constraint ignored (unsupported) + 14 | constraint b { + | ^~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_randomize.v:19:4: Constraint ignored (unsupported) + 19 | constraint c { + | ^~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_randomize.v:23:4: Constraint ignored (unsupported) + 23 | constraint d { + | ^~~~~~~~~~ %Error-UNSUPPORTED: t/t_randomize.v:29:29: Unsupported: solve before 29 | constraint order { solve length before header; } | ^~~~~~ +%Warning-CONSTRAINTIGN: t/t_randomize.v:29:4: Constraint ignored (unsupported) + 29 | constraint order { solve length before header; } + | ^~~~~~~~~~ %Error-UNSUPPORTED: t/t_randomize.v:32:9: Unsupported: dist 32 | x dist { [100:102] :/ 1, 200 := 2, 300 := 5, 400}; | ^~~~ +%Warning-CONSTRAINTIGN: t/t_randomize.v:30:4: Constraint ignored (unsupported) + 30 | constraint dis { + | ^~~~~~~~~~ %Error-UNSUPPORTED: t/t_randomize.v:37:1: Unsupported: extern constraint 37 | constraint Packet::ex { header > 0 }; | ^~~~~~~~~~ diff --git a/test_regress/t/t_randomize_method.v b/test_regress/t/t_randomize_method.v index 126f0785e..dc3fa9561 100644 --- a/test_regress/t/t_randomize_method.v +++ b/test_regress/t/t_randomize_method.v @@ -4,6 +4,20 @@ // any use, without warranty, 2020 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 +`define check_rand(cl, field) \ +begin \ + longint prev_result; \ + int ok = 0; \ + for (int i = 0; i < 10; i++) begin \ + longint result; \ + void'(cl.randomize()); \ + result = longint'(field); \ + if (i > 0 && result != prev_result) ok = 1; \ + prev_result = result; \ + end \ + if (ok != 1) $stop; \ +end + typedef enum bit[15:0] { ONE = 3, TWO = 5, @@ -78,59 +92,82 @@ class OtherCls; endclass -module t (/*AUTOARG*/); - bit ok = 0; - longint checksum; +class ContainsNull; + rand BaseCls b; +endclass - task checksum_next(longint x); - checksum = x ^ {checksum[62:0],checksum[63]^checksum[2]^checksum[0]}; - endtask; +class ClsWithInt; + rand int a; + int b; +endclass + +class DeriveClsWithInt extends ClsWithInt; +endclass + +class DeriveAndContainClsWithInt extends ClsWithInt; + rand ClsWithInt cls1; + ClsWithInt cls2; + function new; + cls1 = new; + cls2 = new; + endfunction +endclass + +module t (/*AUTOARG*/); DerivedCls derived; OtherCls other; BaseCls base; + ContainsNull cont; + DeriveClsWithInt der_int; + DeriveAndContainClsWithInt der_contain; initial begin int rand_result; - longint prev_checksum; + derived = new; + other = new; + cont = new; + der_int = new; + der_contain = new; + base = derived; for (int i = 0; i < 10; i++) begin - derived = new; - other = new; - base = derived; rand_result = base.randomize(); rand_result = other.randomize(); + rand_result = cont.randomize(); + rand_result = der_int.randomize(); + rand_result = der_contain.randomize(); if (!(derived.l inside {ONE, TWO, THREE, FOUR})) $stop; if (!(other.str.s.c inside {ONE, TWO, THREE, FOUR})) $stop; if (!(other.str.y inside {ONE, TWO, THREE, FOUR})) $stop; if (derived.i.e != 0) $stop; if (derived.k != 0) $stop; if (other.v != 0) $stop; - checksum = 0; - checksum_next(longint'(derived.i.a)); - checksum_next(longint'(derived.i.b)); - checksum_next(longint'(derived.i.c)); - checksum_next(longint'(derived.j)); - checksum_next(longint'(derived.l)); - checksum_next(longint'(other.w)); - checksum_next(longint'(other.x)); - checksum_next(longint'(other.y)); - checksum_next(longint'(other.z)); - checksum_next(longint'(other.str.x)); - checksum_next(longint'(other.str.y)); - checksum_next(longint'(other.str.z)); - checksum_next(longint'(other.str.s.a)); - checksum_next(longint'(other.str.s.b)); - checksum_next(longint'(other.str.s.c)); - $write("checksum: %d\n", checksum); - if (i > 0 && checksum != prev_checksum) begin - ok = 1; - end - prev_checksum = checksum; + if (cont.b != null) $stop; + if (der_int.b != 0) $stop; + if (der_contain.cls2.a != 0) $stop; + if (der_contain.cls1.b != 0) $stop; + if (der_contain.b != 0) $stop; end - if (ok) begin - $write("*-* All Finished *-*\n"); - $finish; - end - else $stop; + `check_rand(derived, derived.i.a); + `check_rand(derived, derived.i.b); + `check_rand(derived, derived.i.c); + `check_rand(derived, derived.j); + `check_rand(derived, derived.l); + `check_rand(other, other.w); + `check_rand(other, other.x); + `check_rand(other, other.y); + `check_rand(other, other.z); + `check_rand(other, other.str.x); + `check_rand(other, other.str.y); + `check_rand(other, other.str.z); + `check_rand(other, other.str.s.a); + `check_rand(other, other.str.s.b); + `check_rand(other, other.str.s.c); + `check_rand(der_int, der_int.a); + `check_rand(der_contain, der_contain.cls1.a); + `check_rand(der_contain, der_contain.a); + + $write("*-* All Finished *-*\n"); + $finish; end endmodule diff --git a/test_regress/t/t_randomize_method_bad.out b/test_regress/t/t_randomize_method_bad.out index 80f06eb60..421ba63cf 100644 --- a/test_regress/t/t_randomize_method_bad.out +++ b/test_regress/t/t_randomize_method_bad.out @@ -4,4 +4,7 @@ %Error: t/t_randomize_method_bad.v:14:18: 'randomize' is a predefined class method; redefinition not allowed (IEEE 1800-2017 18.6.3) 14 | function void randomize(int x); | ^~~~~~~~~ +%Error: t/t_randomize_method_bad.v:16:18: 'srandom' is a predefined class method; redefinition not allowed (IEEE 1800-2017 18.6.3) + 16 | function void srandom(int seed); + | ^~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_randomize_method_bad.v b/test_regress/t/t_randomize_method_bad.v index 72da52529..cae21a9b9 100644 --- a/test_regress/t/t_randomize_method_bad.v +++ b/test_regress/t/t_randomize_method_bad.v @@ -13,6 +13,8 @@ endclass class Cls2; function void randomize(int x); endfunction + function void srandom(int seed); + endfunction endclass module t (/*AUTOARG*/); diff --git a/test_regress/t/t_randomize_method_nclass_bad.out b/test_regress/t/t_randomize_method_nclass_bad.out new file mode 100644 index 000000000..6c48315e6 --- /dev/null +++ b/test_regress/t/t_randomize_method_nclass_bad.out @@ -0,0 +1,9 @@ +%Error: t/t_randomize_method_nclass_bad.v:9:7: Calling implicit class method 'randomize' without being under class + : ... In instance t + 9 | randomize(1); + | ^~~~~~~~~ +%Error: t/t_randomize_method_nclass_bad.v:10:7: Calling implicit class method 'srandom' without being under class + : ... In instance t + 10 | srandom(1); + | ^~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_randomize_method_nclass_bad.pl b/test_regress/t/t_randomize_method_nclass_bad.pl new file mode 100755 index 000000000..66fa61649 --- /dev/null +++ b/test_regress/t/t_randomize_method_nclass_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2020 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 + +scenarios(linter => 1); + +lint( + fails => $Self->{vlt_all}, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_gen_defparam_unsup_bad.v b/test_regress/t/t_randomize_method_nclass_bad.v similarity index 55% rename from test_regress/t/t_gen_defparam_unsup_bad.v rename to test_regress/t/t_randomize_method_nclass_bad.v index 2d2842552..6b58cf27f 100644 --- a/test_regress/t/t_gen_defparam_unsup_bad.v +++ b/test_regress/t/t_randomize_method_nclass_bad.v @@ -1,18 +1,12 @@ // DESCRIPTION: Verilator: Verilog Test module // // This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2012 by Wilson Snyder. +// any use, without warranty, 2023 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 module t (/*AUTOARG*/); - a a (); - defparam a.b.W = 3; -endmodule - -module a; - b b(); -endmodule - -module b; - parameter W = 0; + initial begin + randomize(1); + srandom(1); + end endmodule diff --git a/test_regress/t/t_randomize_method_types_unsup.out b/test_regress/t/t_randomize_method_types_unsup.out index 175d8a134..331acdcd8 100644 --- a/test_regress/t/t_randomize_method_types_unsup.out +++ b/test_regress/t/t_randomize_method_types_unsup.out @@ -1,18 +1,22 @@ -%Error-UNSUPPORTED: t/t_randomize_method_types_unsup.v:12:13: Unsupported: random member variables with type 'int[string]' +%Error-UNSUPPORTED: t/t_randomize_method_types_unsup.v:12:13: Unsupported: random member variable with type 'int[string]' : ... In instance t 12 | rand int assocarr[string]; | ^~~~~~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_randomize_method_types_unsup.v:13:13: Unsupported: random member variables with type 'int[]' +%Error-UNSUPPORTED: t/t_randomize_method_types_unsup.v:13:13: Unsupported: random member variable with type 'int[]' : ... In instance t 13 | rand int dynarr[]; | ^~~~~~ -%Error-UNSUPPORTED: t/t_randomize_method_types_unsup.v:14:13: Unsupported: random member variables with type 'int$[0:4]' +%Error-UNSUPPORTED: t/t_randomize_method_types_unsup.v:14:13: Unsupported: random member variable with type 'int$[0:4]' : ... In instance t 14 | rand int unpackarr[5]; | ^~~~~~~~~ -%Error-UNSUPPORTED: t/t_randomize_method_types_unsup.v:15:15: Unsupported: random member variables with type '$unit::Union' +%Error-UNSUPPORTED: t/t_randomize_method_types_unsup.v:15:15: Unsupported: random member variable with type '$unit::Union' : ... In instance t 15 | rand Union uni; | ^~~ +%Error-UNSUPPORTED: t/t_randomize_method_types_unsup.v:16:13: Unsupported: random member variable with type of a current class + : ... In instance t + 16 | rand Cls cls; + | ^~~ %Error: Exiting due to diff --git a/test_regress/t/t_randomize_method_types_unsup.v b/test_regress/t/t_randomize_method_types_unsup.v index 1595a6c07..fb3fd938d 100644 --- a/test_regress/t/t_randomize_method_types_unsup.v +++ b/test_regress/t/t_randomize_method_types_unsup.v @@ -13,6 +13,7 @@ class Cls; rand int dynarr[]; rand int unpackarr[5]; rand Union uni; + rand Cls cls; endclass module t (/*AUTOARG*/); diff --git a/test_regress/t/t_randomize_srandom.pl b/test_regress/t/t_randomize_srandom.pl new file mode 100755 index 000000000..aabcde63e --- /dev/null +++ b/test_regress/t/t_randomize_srandom.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2020 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_randomize_srandom.v b/test_regress/t/t_randomize_srandom.v new file mode 100644 index 000000000..218565138 --- /dev/null +++ b/test_regress/t/t_randomize_srandom.v @@ -0,0 +1,154 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define stop $stop +`define checkeq(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) +`define checkne(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; + bit [63:0] m_sum; + rand int m_r; + function void hash_init(); + m_sum = 64'h5aef0c8d_d70a4497; + endfunction + function void hash(int res); + $display(" res %x", res); + m_sum = {32'h0, res} ^ {m_sum[62:0], m_sum[63] ^ m_sum[2] ^ m_sum[0]}; + endfunction + + function bit [63:0] test1(); + Cls o; + // Affected by srandom + $display(" init for randomize"); + hash_init; + // TODO: Support this.randomize() + o = this; + void'(o.randomize()); + hash(m_r); + void'(o.randomize()); + hash(m_r); + return m_sum; + endfunction + + function bit [63:0] test2(int seed); + $display(" init for seeded randomize"); + hash_init; + this.srandom(seed); + void'(this.randomize()); + hash(m_r); + return m_sum; + endfunction + + function bit [63:0] test3(int seed); + $display(" init for seeded randomize"); + hash_init; + srandom(seed); + void'(randomize()); + hash(m_r); + return m_sum; + endfunction +endclass + +class Foo; +endclass + +class Bar extends Foo; + bit [63:0] m_sum; + rand int m_r; + function void hash_init(); + m_sum = 64'h5aef0c8d_d70a4497; + endfunction + function void hash(int res); + $display(" res %x", res); + m_sum = {32'h0, res} ^ {m_sum[62:0], m_sum[63] ^ m_sum[2] ^ m_sum[0]}; + endfunction + + function void this_srandom(int seed); + this.srandom(seed); + endfunction + + function bit [63:0] test2; + $display(" init for seeded randomize"); + hash_init; + $display("%d", m_r); + hash(m_r); + return m_sum; + endfunction +endclass + +module t(/*AUTOARG*/); + + Cls ca; + Cls cb; + Bar b1; + Bar b2; + + bit [63:0] sa; + bit [63:0] sb; + + initial begin + // Each class gets different seed from same thread, + // so the randomization should be different + $display("New"); + ca = new; + cb = new; + b1 = new; + b2 = new; + + sa = ca.test1(); + sb = cb.test1(); + `checkne(sa, sb); // Could false-fail 2^-32 + + // Seed the classes to be synced + $display("Seed"); + ca.srandom(123); + cb.srandom(123); + + sa = ca.test1(); + sb = cb.test1(); + `checkeq(sa, sb); + + // Check using this + $display("this.srandom"); + sa = ca.test2(1); + sb = cb.test2(2); + `checkne(sa, sb); + + sa = ca.test2(3); + sb = cb.test2(3); + `checkeq(sa, sb); + + $display("this.srandom - Bar class"); + b1.this_srandom(1); + b2.this_srandom(2); + void'(b1.randomize()); + void'(b2.randomize()); + sa = b1.test2; + sb = b2.test2; + `checkne(sa, sb); + + b1.this_srandom(3); + b2.this_srandom(3); + void'(b1.randomize()); + void'(b2.randomize()); + sa = b1.test2; + sb = b2.test2; + `checkeq(sa, sb); + + // Check using direct call + $display("srandom"); + sa = ca.test3(1); + sb = cb.test3(2); + `checkne(sa, sb); + + sa = ca.test3(3); + sb = cb.test3(3); + `checkeq(sa, sb); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randsequence_bad.out b/test_regress/t/t_randsequence_bad.out new file mode 100644 index 000000000..9cc011c04 --- /dev/null +++ b/test_regress/t/t_randsequence_bad.out @@ -0,0 +1,20 @@ +%Error-UNSUPPORTED: t/t_randsequence_bad.v:13:25: Unsupported: randsequence production + 13 | such_production: { }; + | ^ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_randsequence_bad.v:12:7: Unsupported: randsequence + 12 | randsequence(no_such_production) + | ^~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_randsequence_bad.v:17:16: Unsupported: randsequence production id + 17 | main: production_bad; + | ^~~~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_randsequence_bad.v:17:14: Unsupported: randsequence production + 17 | main: production_bad; + | ^ +%Error-UNSUPPORTED: t/t_randsequence_bad.v:18:24: Unsupported: randsequence production + 18 | production_baa: {}; + | ^ +%Error-UNSUPPORTED: t/t_randsequence_bad.v:16:7: Unsupported: randsequence + 16 | randsequence(main) + | ^~~~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_randsequence_bad.pl b/test_regress/t/t_randsequence_bad.pl new file mode 100755 index 000000000..a083f46f5 --- /dev/null +++ b/test_regress/t/t_randsequence_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 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 + +scenarios(vlt => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_randsequence_bad.v b/test_regress/t/t_randsequence_bad.v new file mode 100644 index 000000000..5e335f237 --- /dev/null +++ b/test_regress/t/t_randsequence_bad.v @@ -0,0 +1,25 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2023 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +module t(/*AUTOARG*/); + + initial begin; + randsequence(no_such_production) // Bad + such_production: { }; + endsequence + + randsequence(main) + main: production_bad; // Bad + production_baa: {}; + endsequence + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_select_ascending.pl b/test_regress/t/t_select_ascending.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_select_ascending.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_select_ascending.v b/test_regress/t/t_select_ascending.v new file mode 100644 index 000000000..0f37470d9 --- /dev/null +++ b/test_regress/t/t_select_ascending.v @@ -0,0 +1,75 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2009 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + integer cyc = 0; + reg [63:0] crc; + reg [63:0] sum; + + // verilator lint_off ASCRANGE + wire [10:41] sel2 = crc[31:0]; + wire [10:100] sel3 = {crc[26:0],crc}; + + wire out20 = sel2[{1'b0,crc[3:0]} + 11]; + wire [3:0] out21 = sel2[13 : 16]; + wire [3:0] out22 = sel2[{1'b0,crc[3:0]} + 20 +: 4]; + wire [3:0] out23 = sel2[{1'b0,crc[3:0]} + 20 -: 4]; + + wire out30 = sel3[{2'b0,crc[3:0]} + 11]; + wire [3:0] out31 = sel3[13 : 16]; + wire [3:0] out32 = sel3[crc[5:0] + 20 +: 4]; + wire [3:0] out33 = sel3[crc[5:0] + 20 -: 4]; + + // Aggregate outputs into a single result vector + wire [63:0] result = {38'h0, out20, out21, out22, out23, out30, out31, out32, out33}; + + reg [19:50] sel1; + initial begin + // Path clearing + // 122333445 + // 826048260 + sel1 = 32'h12345678; + if (sel1 != 32'h12345678) $stop; + if (sel1[47 : 50] != 4'h8) $stop; + if (sel1[31 : 34] != 4'h4) $stop; + if (sel1[27 +: 4] != 4'h3) $stop; //==[27:30], in memory as [23:20] + if (sel1[26 -: 4] != 4'h2) $stop; //==[23:26], in memory as [27:24] + end + + // Test loop + always @ (posedge clk) begin +`ifdef TEST_VERBOSE + $write("[%0t] sels=%x,%x,%x,%x %x,%x,%x,%x\n", $time, out20,out21,out22,out23, out30,out31,out32,out33); + $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 (cyc==0) begin + // Setup + crc <= 64'h5aef0c8d_d70a4497; + end + else if (cyc<10) begin + sum <= 64'h0; + end + else if (cyc<90) begin + 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'h28bf65439eb12c00 + if (sum !== `EXPECTED_SUM) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_select_bad_msb.out b/test_regress/t/t_select_bad_msb.out index 1ec1c350e..84e30c325 100644 --- a/test_regress/t/t_select_bad_msb.out +++ b/test_regress/t/t_select_bad_msb.out @@ -1,10 +1,10 @@ -%Warning-LITENDIAN: t/t_select_bad_msb.v:12:8: Big bit endian vector: left < right of bit range: [0:22] - : ... In instance t +%Warning-ASCRANGE: t/t_select_bad_msb.v:12:8: Ascending bit range vector: left < right of bit range: [0:22] + : ... In instance t 12 | reg [0:22] backwd; | ^ - ... For warning description see https://verilator.org/warn/LITENDIAN?v=latest - ... Use "/* verilator lint_off LITENDIAN */" and lint_on around source to disable this message. -%Warning-SELRANGE: t/t_select_bad_msb.v:16:16: [1:4] Range extract has backward bit ordering, perhaps you wanted [4:1] + ... For warning description see https://verilator.org/warn/ASCRANGE?v=latest + ... Use "/* verilator lint_off ASCRANGE */" and lint_on around source to disable this message. +%Warning-SELRANGE: t/t_select_bad_msb.v:16:16: [1:4] Slice range has ascending bit ordering, perhaps you wanted [4:1] : ... In instance t 16 | sel2 = mi[1:4]; | ^ diff --git a/test_regress/t/t_select_bad_range4.out b/test_regress/t/t_select_bad_range4.out index 302b44d6e..cff72fd9a 100644 --- a/test_regress/t/t_select_bad_range4.out +++ b/test_regress/t/t_select_bad_range4.out @@ -36,7 +36,7 @@ : ... In instance t 22 | sel2 = mi[44 +: nonconst]; | ^~~~~~~~ -%Error: t/t_select_bad_range4.v:22:23: Width of :+ or :- bit extract isn't a constant +%Error: t/t_select_bad_range4.v:22:23: Width of :+ or :- bit slice range isn't a constant : ... In instance t 22 | sel2 = mi[44 +: nonconst]; | ^~~~~~~~ diff --git a/test_regress/t/t_select_little_pack.v b/test_regress/t/t_select_little_pack.v index e265f3b1f..3f7492a84 100644 --- a/test_regress/t/t_select_little_pack.v +++ b/test_regress/t/t_select_little_pack.v @@ -10,7 +10,7 @@ module t (/*AUTOARG*/ ); input clk; - // No endian warning here + // No ascending range warning here reg [7:0] pack [3:0]; initial begin diff --git a/test_regress/t/t_sequence_sexpr_unsup.out b/test_regress/t/t_sequence_sexpr_unsup.out new file mode 100644 index 000000000..f72f26834 --- /dev/null +++ b/test_regress/t/t_sequence_sexpr_unsup.out @@ -0,0 +1,242 @@ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:27:4: Unsupported: sequence + 27 | sequence s_a; + | ^~~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:30:4: Unsupported: sequence + 30 | sequence s_var; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:35:4: Unsupported: sequence + 35 | sequence s_within; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:36:9: Unsupported: within (in sequence expression) + 36 | a within(b); + | ^~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:39:4: Unsupported: sequence + 39 | sequence s_and; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:40:9: Unsupported: and (in sequence expression) + 40 | a and b; + | ^~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:43:4: Unsupported: sequence + 43 | sequence s_or; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:44:9: Unsupported: or (in sequence expression) + 44 | a or b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:47:4: Unsupported: sequence + 47 | sequence s_throughout; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:48:9: Unsupported: throughout (in sequence expression) + 48 | a throughout b; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:51:4: Unsupported: sequence + 51 | sequence s_intersect; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:52:9: Unsupported: intersect (in sequence expression) + 52 | a intersect b; + | ^~~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:55:4: Unsupported: sequence + 55 | sequence s_uni_cycdelay_int; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:56:7: Unsupported: ## () cycle delay range expression + 56 | ## 1 b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:56:12: Unsupported: ## (in sequence expression) + 56 | ## 1 b; + | ^ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:58:4: Unsupported: sequence + 58 | sequence s_uni_cycdelay_id; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:59:7: Unsupported: ## id cycle delay range expression + 59 | ## DELAY b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:59:16: Unsupported: ## (in sequence expression) + 59 | ## DELAY b; + | ^ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:61:4: Unsupported: sequence + 61 | sequence s_uni_cycdelay_pid; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:62:7: Unsupported: ## () cycle delay range expression + 62 | ## ( DELAY ) b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:62:20: Unsupported: ## (in sequence expression) + 62 | ## ( DELAY ) b; + | ^ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:64:4: Unsupported: sequence + 64 | sequence s_uni_cycdelay_range; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:65:7: Unsupported: ## range cycle delay range expression + 65 | ## [1:2] b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:65:16: Unsupported: ## (in sequence expression) + 65 | ## [1:2] b; + | ^ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:67:4: Unsupported: sequence + 67 | sequence s_uni_cycdelay_star; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:68:7: Unsupported: ## [*] cycle delay range expression + 68 | ## [*] b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:68:14: Unsupported: ## (in sequence expression) + 68 | ## [*] b; + | ^ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:70:4: Unsupported: sequence + 70 | sequence s_uni_cycdelay_plus; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:71:7: Unsupported: ## [+] cycle delay range expression + 71 | ## [+] b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:71:14: Unsupported: ## (in sequence expression) + 71 | ## [+] b; + | ^ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:74:4: Unsupported: sequence + 74 | sequence s_cycdelay_int; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:75:9: Unsupported: ## () cycle delay range expression + 75 | a ## 1 b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:75:12: Unsupported: ## (in sequence expression) + 75 | a ## 1 b; + | ^ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:77:4: Unsupported: sequence + 77 | sequence s_cycdelay_id; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:78:9: Unsupported: ## id cycle delay range expression + 78 | a ## DELAY b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:78:9: Unsupported: ## (in sequence expression) + 78 | a ## DELAY b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:80:4: Unsupported: sequence + 80 | sequence s_cycdelay_pid; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:81:9: Unsupported: ## () cycle delay range expression + 81 | a ## ( DELAY ) b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:81:14: Unsupported: ## (in sequence expression) + 81 | a ## ( DELAY ) b; + | ^~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:83:4: Unsupported: sequence + 83 | sequence s_cycdelay_range; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:84:9: Unsupported: ## range cycle delay range expression + 84 | a ## [1:2] b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:84:9: Unsupported: ## (in sequence expression) + 84 | a ## [1:2] b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:86:4: Unsupported: sequence + 86 | sequence s_cycdelay_star; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:87:9: Unsupported: ## [*] cycle delay range expression + 87 | a ## [*] b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:87:9: Unsupported: ## (in sequence expression) + 87 | a ## [*] b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:89:4: Unsupported: sequence + 89 | sequence s_cycdelay_plus; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:90:9: Unsupported: ## [+] cycle delay range expression + 90 | a ## [+] b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:90:9: Unsupported: ## (in sequence expression) + 90 | a ## [+] b; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:93:4: Unsupported: sequence + 93 | sequence s_booleanabbrev_brastar_int; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:94:9: Unsupported: [*] boolean abbrev expression + 94 | a [* 1 ]; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:94:12: Unsupported: boolean abbrev (in sequence expression) + 94 | a [* 1 ]; + | ^ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:96:4: Unsupported: sequence + 96 | sequence s_booleanabbrev_brastar; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:97:9: Unsupported: [*] boolean abbrev expression + 97 | a [*]; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:97:9: Unsupported: boolean abbrev (in sequence expression) + 97 | a [*]; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:99:4: Unsupported: sequence + 99 | sequence s_booleanabbrev_plus; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:100:9: Unsupported: [+] boolean abbrev expression + 100 | a [+]; + | ^~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:100:9: Unsupported: boolean abbrev (in sequence expression) + 100 | a [+]; + | ^~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:102:4: Unsupported: sequence + 102 | sequence s_booleanabbrev_eq; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:103:9: Unsupported: [= boolean abbrev expression + 103 | a [= 1]; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:103:12: Unsupported: boolean abbrev (in sequence expression) + 103 | a [= 1]; + | ^ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:105:4: Unsupported: sequence + 105 | sequence s_booleanabbrev_eq_range; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:106:9: Unsupported: [= boolean abbrev expression + 106 | a [= 1:2]; + | ^~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:106:12: Unsupported: boolean abbrev (in sequence expression) + 106 | a [= 1:2]; + | ^ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:108:4: Unsupported: sequence + 108 | sequence s_booleanabbrev_minusgt; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:109:9: Unsupported: [-> boolean abbrev expression + 109 | a [-> 1]; + | ^~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:109:13: Unsupported: boolean abbrev (in sequence expression) + 109 | a [-> 1]; + | ^ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:111:4: Unsupported: sequence + 111 | sequence s_booleanabbrev_minusgt_range; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:112:9: Unsupported: [-> boolean abbrev expression + 112 | a [-> 1:2]; + | ^~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:112:13: Unsupported: boolean abbrev (in sequence expression) + 112 | a [-> 1:2]; + | ^ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:115:4: Unsupported: sequence + 115 | sequence p_arg_seqence(sequence inseq); + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:115:27: Unsupported: sequence argument data type + 115 | sequence p_arg_seqence(sequence inseq); + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:119:4: Unsupported: sequence + 119 | sequence s_firstmatch_a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:120:7: Unsupported: first_match (in sequence expression) + 120 | first_match (a); + | ^~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:122:4: Unsupported: sequence + 122 | sequence s_firstmatch_ab; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:123:7: Unsupported: first_match (in sequence expression) + 123 | first_match (a, res0 = 1); + | ^~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:125:4: Unsupported: sequence + 125 | sequence s_firstmatch_abc; + | ^~~~~~~~ +%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 + 129 | cover sequence (s_a) $display(""); + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:130:10: 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 + 131 | cover sequence (disable iff (b) s_a) $display(""); + | ^~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_sequence_sexpr_unsup.pl b/test_regress/t/t_sequence_sexpr_unsup.pl new file mode 100755 index 000000000..c9ca914d8 --- /dev/null +++ b/test_regress/t/t_sequence_sexpr_unsup.pl @@ -0,0 +1,20 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 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 + +scenarios(vlt => 1); + +compile( + expect_filename => $Self->{golden_filename}, + verilator_flags2 => ['--assert --error-limit 1000'], + fails => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_sequence_sexpr_unsup.v b/test_regress/t/t_sequence_sexpr_unsup.v new file mode 100644 index 000000000..da3b99ec2 --- /dev/null +++ b/test_regress/t/t_sequence_sexpr_unsup.v @@ -0,0 +1,139 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + int a; + int b; + int cyc = 0; + int res0, res1; + + localparam DELAY = 1; + + always @(posedge clk) begin + cyc <= cyc + 1; + end + + // NOTE this grammar hasn't been checked with other simulators, + // is here just to avoid uncovered code lines in the grammar. + // NOTE using 'property weak' here as sequence/endsequence not supported + sequence s_a; + a; + endsequence : s_a + sequence s_var; + logic l1, l2; + a; + endsequence + + sequence s_within; + a within(b); + endsequence + + sequence s_and; + a and b; + endsequence + + sequence s_or; + a or b; + endsequence + + sequence s_throughout; + a throughout b; + endsequence + + sequence s_intersect; + a intersect b; + endsequence + + sequence s_uni_cycdelay_int; + ## 1 b; + endsequence + sequence s_uni_cycdelay_id; + ## DELAY b; + endsequence + sequence s_uni_cycdelay_pid; + ## ( DELAY ) b; + endsequence + sequence s_uni_cycdelay_range; + ## [1:2] b; + endsequence + sequence s_uni_cycdelay_star; + ## [*] b; + endsequence + sequence s_uni_cycdelay_plus; + ## [+] b; + endsequence + + sequence s_cycdelay_int; + a ## 1 b; + endsequence + sequence s_cycdelay_id; + a ## DELAY b; + endsequence + sequence s_cycdelay_pid; + a ## ( DELAY ) b; + endsequence + sequence s_cycdelay_range; + a ## [1:2] b; + endsequence + sequence s_cycdelay_star; + a ## [*] b; + endsequence + sequence s_cycdelay_plus; + a ## [+] b; + endsequence + + sequence s_booleanabbrev_brastar_int; + a [* 1 ]; + endsequence + sequence s_booleanabbrev_brastar; + a [*]; + endsequence + sequence s_booleanabbrev_plus; + a [+]; + endsequence + sequence s_booleanabbrev_eq; + a [= 1]; + endsequence + sequence s_booleanabbrev_eq_range; + a [= 1:2]; + endsequence + sequence s_booleanabbrev_minusgt; + a [-> 1]; + endsequence + sequence s_booleanabbrev_minusgt_range; + a [-> 1:2]; + endsequence + + sequence p_arg_seqence(sequence inseq); + inseq; + endsequence + + sequence s_firstmatch_a; + first_match (a); + endsequence + sequence s_firstmatch_ab; + first_match (a, res0 = 1); + endsequence + sequence s_firstmatch_abc; + first_match (a, res0 = 1, res1 = 2); + endsequence + + cover sequence (s_a) $display(""); + cover sequence (@(posedge a) disable iff (b) s_a) $display(""); + cover sequence (disable iff (b) s_a) $display(""); + + always @(posedge clk) begin + if (cyc == 10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_split_var_0.v b/test_regress/t/t_split_var_0.v index ee3db8562..6b316d6f3 100644 --- a/test_regress/t/t_split_var_0.v +++ b/test_regress/t/t_split_var_0.v @@ -175,9 +175,9 @@ module barshift_2d_packed_array #(parameter DEPTH = 2, localparam WIDTH = 2**DEP (input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out); localparam OFFSET = -2; - /*verilator lint_off LITENDIAN*/ + /*verilator lint_off ASCRANGE*/ reg [OFFSET:DEPTH+OFFSET][WIDTH-1:0] tmp /*verilator split_var*/; - /*verilator lint_on LITENDIAN*/ + /*verilator lint_on ASCRANGE*/ generate for(genvar i = 0; i < DEPTH; ++i) begin @@ -201,9 +201,9 @@ module barshift_2d_packed_array_le #(parameter DEPTH = 2, localparam WIDTH = 2** (input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out); localparam OFFSET = -2; - /*verilator lint_off LITENDIAN*/ + /*verilator lint_off ASCRANGE*/ reg [OFFSET:DEPTH+OFFSET][OFFSET:WIDTH-1+OFFSET] tmp /*verilator split_var*/; - /*verilator lint_on LITENDIAN*/ + /*verilator lint_on ASCRANGE*/ generate for(genvar i = 0; i < DEPTH; ++i) begin @@ -245,9 +245,9 @@ endmodule module barshift_bitslice #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH) (input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out); - /*verilator lint_off LITENDIAN*/ + /*verilator lint_off ASCRANGE*/ wire [0:WIDTH*(DEPTH+1) - 1] tmp /*verilator split_var*/; - /*verilator lint_on LITENDIAN*/ + /*verilator lint_on ASCRANGE*/ generate for(genvar i = 0; i < DEPTH; ++i) begin @@ -267,10 +267,10 @@ endmodule module var_decl_with_init(); - /*verilator lint_off LITENDIAN*/ + /*verilator lint_off ASCRANGE*/ logic [-1:30] var0 /* verilator split_var */ = {4'd0, 4'd1, 4'd2, 4'd3, 4'd4, 4'd5, 4'd6, 4'd7}; logic [-1:30] var2 /* verilator split_var */; - /*verilator lint_on LITENDIAN*/ + /*verilator lint_on ASCRANGE*/ logic [30:-1] var1 /* verilator split_var */ = {4'd0, 4'd1, 4'd2, 4'd3, 4'd4, 4'd5, 4'd6, 4'd7}; logic [30:-1] var3 /* verilator split_var */; @@ -290,9 +290,9 @@ module t_array_rev(clk); // from t_array_rev.v input clk; integer cyc = 0; - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE logic arrd [0:1] /*verilator split_var*/ = '{ 1'b1, 1'b0 }; - // verilator lint_on LITENDIAN + // verilator lint_on ASCRANGE logic y0, y1; logic localbkw [1:0]/*verilator split_var*/ ; @@ -442,11 +442,11 @@ module t(/*AUTOARG*/ clk); t_array_rev i_t_array_rev(clk); assign in = 8'b10001110; - /*verilator lint_off LITENDIAN*/ + /*verilator lint_off ASCRANGE*/ logic [7:0] [7:0] expc = {8'b10001110, 8'b01000111, 8'b10100011, 8'b11010001, 8'b11101000, 8'b01110100, 8'b00111010, 8'b00011101}; - /*verilator lint_on LITENDIAN*/ + /*verilator lint_on ASCRANGE*/ always @(posedge clk) begin : always_block automatic bit failed = 0; $display("in:%b shift:%d expc:%b", in, shift, expc[7-shift]); diff --git a/test_regress/t/t_static_function_in_class.pl b/test_regress/t/t_static_function_in_class.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_static_function_in_class.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_static_function_in_class.v b/test_regress/t/t_static_function_in_class.v new file mode 100644 index 000000000..ab9a80288 --- /dev/null +++ b/test_regress/t/t_static_function_in_class.v @@ -0,0 +1,51 @@ +// DESCRIPTION: Verilator: Simple static elaboration case +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2015 by Todd Strader. +// SPDX-License-Identifier: CC0-1.0 + +class string_utils; + typedef string array_of_string[]; + + static function array_of_string split_by_dash(string s); + string parts[$]; + int last_char_position = -1; + for (int i = 0; i < s.len(); i++) begin + if (i == s.len()-1) begin + parts.push_back(s.substr(last_char_position+1, i)); + end + // Can't remove this, because then the code will work + if (string'(s[i]) == "-") begin + parts.push_back(s.substr(last_char_position+1, i-1)); + last_char_position = i; + end + end // for (int i = 0; i < s.len(); i++) + return parts; + endfunction // split_by_dash +endclass // string_utils + +class filter; + local static filter single_instance; + + static function filter get(); + if (single_instance == null) + single_instance = new(); + return single_instance; + endfunction // get + + local function new(); + string parts[] = string_utils::split_by_dash("*"); + if (parts.size() != 1) + $fatal(0, "Expected single element"); + if (parts[0] != "*") + $fatal(0, "Expected element to be *"); + endfunction // new +endclass // filter + +module t (/*AUTOARG*/); + const filter _filter = filter::get(); + initial begin + $write("*-* All Finished *-*\n"); + $finish(); + end +endmodule diff --git a/test_regress/t/t_static_in_loop_unsup.out b/test_regress/t/t_static_in_loop_unsup.out new file mode 100644 index 000000000..0070d8d3f --- /dev/null +++ b/test_regress/t/t_static_in_loop_unsup.out @@ -0,0 +1,6 @@ +%Warning-STATICVAR: t/t_static_in_loop_unsup.v:14:24: Static variable with assignment declaration declared in a loop converted to automatic + 14 | static int a = 0; + | ^ + ... For warning description see https://verilator.org/warn/STATICVAR?v=latest + ... Use "/* verilator lint_off STATICVAR */" and lint_on around source to disable this message. +%Error: Exiting due to diff --git a/test_regress/t/t_static_in_loop_unsup.pl b/test_regress/t/t_static_in_loop_unsup.pl new file mode 100755 index 000000000..a5846c699 --- /dev/null +++ b/test_regress/t/t_static_in_loop_unsup.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_static_in_loop_unsup.v b/test_regress/t/t_static_in_loop_unsup.v new file mode 100644 index 000000000..87c674688 --- /dev/null +++ b/test_regress/t/t_static_in_loop_unsup.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + + +module t; + initial begin + int x = 0; + while (x < 10) begin : outer_loop + int y = 0; + while (y < x) begin : inner_loop + static int a = 0; + a++; + y++; + end + x++; + end + if (outer_loop.inner_loop.a != 45) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_struct_clk.pl b/test_regress/t/t_struct_clk.pl new file mode 100755 index 000000000..859050d63 --- /dev/null +++ b/test_regress/t/t_struct_clk.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2023 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_struct_clk.v b/test_regress/t/t_struct_clk.v new file mode 100644 index 000000000..e09b4f3de --- /dev/null +++ b/test_regress/t/t_struct_clk.v @@ -0,0 +1,52 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0); + +typedef struct { + logic clk1; + logic clk2; + logic rst; +} clks_t; + +module t(/*AUTOARG*/ + // Inputs + clk, fastclk + ); + input clk; + input fastclk; + + int cyc = 0; + + clks_t clks; + always_comb begin + clks.clk1 = clk; + clks.clk2 = fastclk; + end + + // verilator lint_off MULTIDRIVEN + int cyc1 = 0; + int cyc2 = 0; + + always @ (negedge clks.clk1) cyc1 <= cyc1 + 1; + always @ (negedge clks.clk2) cyc2 <= cyc2 + 1; + + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc < 10) begin + cyc1 <= '0; + cyc2 <= '0; + end + else if (cyc == 99) begin + `checkd(cyc1, 90); + `checkd(cyc2, 90*5); + + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_struct_packed_sysfunct.v b/test_regress/t/t_struct_packed_sysfunct.v index acbe00291..540f27dcb 100644 --- a/test_regress/t/t_struct_packed_sysfunct.v +++ b/test_regress/t/t_struct_packed_sysfunct.v @@ -17,15 +17,15 @@ module t (/*AUTOARG*/ logic [1:0] e1; logic [3:0] e2; logic [7:0] e3; - } struct_bg; // big endian structure - /* verilator lint_off LITENDIAN */ + } struct_dsc; // descendng range structure + /* verilator lint_off ASCRANGE */ struct packed { logic e0; logic [0:1] e1; logic [0:3] e2; logic [0:7] e3; - } struct_lt; // little endian structure - /* verilator lint_on LITENDIAN */ + } struct_asc; // ascending range structure + /* verilator lint_on ASCRANGE */ integer cnt = 0; @@ -44,20 +44,20 @@ module t (/*AUTOARG*/ always @ (posedge clk) if (cnt==1) begin - // big endian - if ($bits (struct_bg ) != 15) $stop; - if ($bits (struct_bg.e0) != 1) $stop; - if ($bits (struct_bg.e1) != 2) $stop; - if ($bits (struct_bg.e2) != 4) $stop; - if ($bits (struct_bg.e3) != 8) $stop; - if ($increment (struct_bg, 1) != 1) $stop; - // little endian - if ($bits (struct_lt ) != 15) $stop; - if ($bits (struct_lt.e0) != 1) $stop; - if ($bits (struct_lt.e1) != 2) $stop; - if ($bits (struct_lt.e2) != 4) $stop; - if ($bits (struct_lt.e3) != 8) $stop; - if ($increment (struct_lt, 1) != 1) $stop; // Structure itself always big numbered + // descending range + if ($bits (struct_dsc ) != 15) $stop; + if ($bits (struct_dsc.e0) != 1) $stop; + if ($bits (struct_dsc.e1) != 2) $stop; + if ($bits (struct_dsc.e2) != 4) $stop; + if ($bits (struct_dsc.e3) != 8) $stop; + if ($increment (struct_dsc, 1) != 1) $stop; + // ascending range + if ($bits (struct_asc ) != 15) $stop; + if ($bits (struct_asc.e0) != 1) $stop; + if ($bits (struct_asc.e1) != 2) $stop; + if ($bits (struct_asc.e2) != 4) $stop; + if ($bits (struct_asc.e3) != 8) $stop; + if ($increment (struct_asc, 1) != 1) $stop; // Structure itself always big numbered end endmodule diff --git a/test_regress/t/t_struct_packed_value_list.v b/test_regress/t/t_struct_packed_value_list.v index 41c84d3b9..3321d3d9e 100644 --- a/test_regress/t/t_struct_packed_value_list.v +++ b/test_regress/t/t_struct_packed_value_list.v @@ -19,17 +19,17 @@ module t (/*AUTOARG*/ logic [1:0] e1; logic [3:0] e2; logic [7:0] e3; - } struct_bg; // big endian structure - /* verilator lint_off LITENDIAN */ + } struct_dsc; // descending range structure + /* verilator lint_off ASCRANGE */ struct packed { logic e0; logic [0:1] e1; logic [0:3] e2; logic [0:7] e3; - } struct_lt; // little endian structure - /* verilator lint_on LITENDIAN */ + } struct_asc; // ascending range structure + /* verilator lint_on ASCRANGE */ - localparam WS = 15; // $bits(struct_bg) + localparam WS = 15; // $bits(struct_dsc) integer cnt = 0; @@ -46,70 +46,70 @@ module t (/*AUTOARG*/ $finish; end - // big endian + // descending range always @ (posedge clk) if (cnt[1:0]==2'd0) begin // initialize to defaults (all bits 1'b0) - if (cnt[30:2]==0) struct_bg <= '0; - else if (cnt[30:2]==1) struct_bg <= '0; - else if (cnt[30:2]==2) struct_bg <= '0; - else if (cnt[30:2]==3) struct_bg <= '0; - else if (cnt[30:2]==4) struct_bg <= '0; - else if (cnt[30:2]==5) struct_bg <= '0; - else if (cnt[30:2]==6) struct_bg <= '0; + if (cnt[30:2]==0) struct_dsc <= '0; + else if (cnt[30:2]==1) struct_dsc <= '0; + else if (cnt[30:2]==2) struct_dsc <= '0; + else if (cnt[30:2]==3) struct_dsc <= '0; + else if (cnt[30:2]==4) struct_dsc <= '0; + else if (cnt[30:2]==5) struct_dsc <= '0; + else if (cnt[30:2]==6) struct_dsc <= '0; end else if (cnt[1:0]==2'd1) begin // write data into whole or part of the array using literals if (cnt[30:2]==0) begin end - else if (cnt[30:2]==1) struct_bg <= '{0 ,1 , 2, 3}; - else if (cnt[30:2]==2) struct_bg <= '{e0:1, e1:2, e2:3, e3:4}; - else if (cnt[30:2]==3) struct_bg <= '{e3:6, e2:4, e1:2, e0:0}; + else if (cnt[30:2]==1) struct_dsc <= '{0 ,1 , 2, 3}; + else if (cnt[30:2]==2) struct_dsc <= '{e0:1, e1:2, e2:3, e3:4}; + else if (cnt[30:2]==3) struct_dsc <= '{e3:6, e2:4, e1:2, e0:0}; // verilator lint_off WIDTH - else if (cnt[30:2]==4) struct_bg <= '{default:13}; - else if (cnt[30:2]==5) struct_bg <= '{e2:8'haa, default:1}; - else if (cnt[30:2]==6) struct_bg <= '{cnt+0 ,cnt+1 , cnt+2, cnt+3}; + else if (cnt[30:2]==4) struct_dsc <= '{default:13}; + else if (cnt[30:2]==5) struct_dsc <= '{e2:8'haa, default:1}; + else if (cnt[30:2]==6) struct_dsc <= '{cnt+0 ,cnt+1 , cnt+2, cnt+3}; // verilator lint_on WIDTH end else if (cnt[1:0]==2'd2) begin // chack array agains expected value - if (cnt[30:2]==0) begin if (struct_bg !== 15'b0_00_0000_00000000) begin $display("%b", struct_bg); $stop(); end end - else if (cnt[30:2]==1) begin if (struct_bg !== 15'b0_01_0010_00000011) begin $display("%b", struct_bg); $stop(); end end - else if (cnt[30:2]==2) begin if (struct_bg !== 15'b1_10_0011_00000100) begin $display("%b", struct_bg); $stop(); end end - else if (cnt[30:2]==3) begin if (struct_bg !== 15'b0_10_0100_00000110) begin $display("%b", struct_bg); $stop(); end end - else if (cnt[30:2]==4) begin if (struct_bg !== 15'b1_01_1101_00001101) begin $display("%b", struct_bg); $stop(); end end - else if (cnt[30:2]==5) begin if (struct_bg !== 15'b1_01_1010_00000001) begin $display("%b", struct_bg); $stop(); end end - else if (cnt[30:2]==6) begin if (struct_bg !== 15'b1_10_1011_00011100) begin $display("%b", struct_bg); $stop(); end end + if (cnt[30:2]==0) begin if (struct_dsc !== 15'b0_00_0000_00000000) begin $display("%b", struct_dsc); $stop(); end end + else if (cnt[30:2]==1) begin if (struct_dsc !== 15'b0_01_0010_00000011) begin $display("%b", struct_dsc); $stop(); end end + else if (cnt[30:2]==2) begin if (struct_dsc !== 15'b1_10_0011_00000100) begin $display("%b", struct_dsc); $stop(); end end + else if (cnt[30:2]==3) begin if (struct_dsc !== 15'b0_10_0100_00000110) begin $display("%b", struct_dsc); $stop(); end end + else if (cnt[30:2]==4) begin if (struct_dsc !== 15'b1_01_1101_00001101) begin $display("%b", struct_dsc); $stop(); end end + else if (cnt[30:2]==5) begin if (struct_dsc !== 15'b1_01_1010_00000001) begin $display("%b", struct_dsc); $stop(); end end + else if (cnt[30:2]==6) begin if (struct_dsc !== 15'b1_10_1011_00011100) begin $display("%b", struct_dsc); $stop(); end end end - // little endian + // ascending range always @ (posedge clk) if (cnt[1:0]==2'd0) begin // initialize to defaults (all bits 1'b0) - if (cnt[30:2]==0) struct_lt <= '0; - else if (cnt[30:2]==1) struct_lt <= '0; - else if (cnt[30:2]==2) struct_lt <= '0; - else if (cnt[30:2]==3) struct_lt <= '0; - else if (cnt[30:2]==4) struct_lt <= '0; - else if (cnt[30:2]==5) struct_lt <= '0; - else if (cnt[30:2]==6) struct_lt <= '0; + if (cnt[30:2]==0) struct_asc <= '0; + else if (cnt[30:2]==1) struct_asc <= '0; + else if (cnt[30:2]==2) struct_asc <= '0; + else if (cnt[30:2]==3) struct_asc <= '0; + else if (cnt[30:2]==4) struct_asc <= '0; + else if (cnt[30:2]==5) struct_asc <= '0; + else if (cnt[30:2]==6) struct_asc <= '0; end else if (cnt[1:0]==2'd1) begin // write data into whole or part of the array using literals if (cnt[30:2]==0) begin end - else if (cnt[30:2]==1) struct_lt <= '{0 ,1 , 2, 3}; - else if (cnt[30:2]==2) struct_lt <= '{e0:1, e1:2, e2:3, e3:4}; - else if (cnt[30:2]==3) struct_lt <= '{e3:6, e2:4, e1:2, e0:0}; + else if (cnt[30:2]==1) struct_asc <= '{0 ,1 , 2, 3}; + else if (cnt[30:2]==2) struct_asc <= '{e0:1, e1:2, e2:3, e3:4}; + else if (cnt[30:2]==3) struct_asc <= '{e3:6, e2:4, e1:2, e0:0}; // verilator lint_off WIDTH - else if (cnt[30:2]==4) struct_lt <= '{default:13}; - else if (cnt[30:2]==5) struct_lt <= '{e2:8'haa, default:1}; - else if (cnt[30:2]==6) struct_lt <= '{cnt+0 ,cnt+1 , cnt+2, cnt+3}; + else if (cnt[30:2]==4) struct_asc <= '{default:13}; + else if (cnt[30:2]==5) struct_asc <= '{e2:8'haa, default:1}; + else if (cnt[30:2]==6) struct_asc <= '{cnt+0 ,cnt+1 , cnt+2, cnt+3}; // verilator lint_on WIDTH end else if (cnt[1:0]==2'd2) begin // chack array agains expected value - if (cnt[30:2]==0) begin if (struct_lt !== 15'b0_00_0000_00000000) begin $display("%b", struct_lt); $stop(); end end - else if (cnt[30:2]==1) begin if (struct_lt !== 15'b0_01_0010_00000011) begin $display("%b", struct_lt); $stop(); end end - else if (cnt[30:2]==2) begin if (struct_lt !== 15'b1_10_0011_00000100) begin $display("%b", struct_lt); $stop(); end end - else if (cnt[30:2]==3) begin if (struct_lt !== 15'b0_10_0100_00000110) begin $display("%b", struct_lt); $stop(); end end - else if (cnt[30:2]==4) begin if (struct_lt !== 15'b1_01_1101_00001101) begin $display("%b", struct_lt); $stop(); end end - else if (cnt[30:2]==5) begin if (struct_lt !== 15'b1_01_1010_00000001) begin $display("%b", struct_lt); $stop(); end end - else if (cnt[30:2]==6) begin if (struct_lt !== 15'b1_10_1011_00011100) begin $display("%b", struct_lt); $stop(); end end + if (cnt[30:2]==0) begin if (struct_asc !== 15'b0_00_0000_00000000) begin $display("%b", struct_asc); $stop(); end end + else if (cnt[30:2]==1) begin if (struct_asc !== 15'b0_01_0010_00000011) begin $display("%b", struct_asc); $stop(); end end + else if (cnt[30:2]==2) begin if (struct_asc !== 15'b1_10_0011_00000100) begin $display("%b", struct_asc); $stop(); end end + else if (cnt[30:2]==3) begin if (struct_asc !== 15'b0_10_0100_00000110) begin $display("%b", struct_asc); $stop(); end end + else if (cnt[30:2]==4) begin if (struct_asc !== 15'b1_01_1101_00001101) begin $display("%b", struct_asc); $stop(); end end + else if (cnt[30:2]==5) begin if (struct_asc !== 15'b1_01_1010_00000001) begin $display("%b", struct_asc); $stop(); end end + else if (cnt[30:2]==6) begin if (struct_asc !== 15'b1_10_1011_00011100) begin $display("%b", struct_asc); $stop(); end end end endmodule diff --git a/test_regress/t/t_struct_packed_write_read.v b/test_regress/t/t_struct_packed_write_read.v index 7616b1593..fa7761d7d 100644 --- a/test_regress/t/t_struct_packed_write_read.v +++ b/test_regress/t/t_struct_packed_write_read.v @@ -19,17 +19,17 @@ module t (/*AUTOARG*/ logic [1:0] e1; logic [3:0] e2; logic [7:0] e3; - } struct_bg; // big endian structure - /* verilator lint_off LITENDIAN */ + } struct_dsc; // descending range structure + /* verilator lint_off ASCRANGE */ struct packed { logic e0; logic [0:1] e1; logic [0:3] e2; logic [0:7] e3; - } struct_lt; // little endian structure - /* verilator lint_on LITENDIAN */ + } struct_asc; // ascending range structure + /* verilator lint_on ASCRANGE */ - localparam WS = 15; // $bits(struct_bg) + localparam WS = 15; // $bits(struct_dsc) integer cnt = 0; @@ -46,76 +46,76 @@ module t (/*AUTOARG*/ $finish; end - // big endian + // descending range always @ (posedge clk) if (cnt[1:0]==2'd0) begin // initialize to defaaults (all bits to 0) - if (cnt[30:2]==0) struct_bg <= '0; - else if (cnt[30:2]==1) struct_bg <= '0; - else if (cnt[30:2]==2) struct_bg <= '0; - else if (cnt[30:2]==3) struct_bg <= '0; - else if (cnt[30:2]==4) struct_bg <= '0; - else if (cnt[30:2]==5) struct_bg <= '0; + if (cnt[30:2]==0) struct_dsc <= '0; + else if (cnt[30:2]==1) struct_dsc <= '0; + else if (cnt[30:2]==2) struct_dsc <= '0; + else if (cnt[30:2]==3) struct_dsc <= '0; + else if (cnt[30:2]==4) struct_dsc <= '0; + else if (cnt[30:2]==5) struct_dsc <= '0; end else if (cnt[1:0]==2'd1) begin // write value to structure if (cnt[30:2]==0) begin end - else if (cnt[30:2]==1) struct_bg <= '1; - else if (cnt[30:2]==2) struct_bg.e0 <= '1; - else if (cnt[30:2]==3) struct_bg.e1 <= '1; - else if (cnt[30:2]==4) struct_bg.e2 <= '1; - else if (cnt[30:2]==5) struct_bg.e3 <= '1; + else if (cnt[30:2]==1) struct_dsc <= '1; + else if (cnt[30:2]==2) struct_dsc.e0 <= '1; + else if (cnt[30:2]==3) struct_dsc.e1 <= '1; + else if (cnt[30:2]==4) struct_dsc.e2 <= '1; + else if (cnt[30:2]==5) struct_dsc.e3 <= '1; end else if (cnt[1:0]==2'd2) begin // check structure value - if (cnt[30:2]==0) begin if (struct_bg !== 15'b000000000000000) begin $display("%b", struct_bg); $stop(); end end - else if (cnt[30:2]==1) begin if (struct_bg !== 15'b111111111111111) begin $display("%b", struct_bg); $stop(); end end - else if (cnt[30:2]==2) begin if (struct_bg !== 15'b100000000000000) begin $display("%b", struct_bg); $stop(); end end - else if (cnt[30:2]==3) begin if (struct_bg !== 15'b011000000000000) begin $display("%b", struct_bg); $stop(); end end - else if (cnt[30:2]==4) begin if (struct_bg !== 15'b000111100000000) begin $display("%b", struct_bg); $stop(); end end - else if (cnt[30:2]==5) begin if (struct_bg !== 15'b000000011111111) begin $display("%b", struct_bg); $stop(); end end + if (cnt[30:2]==0) begin if (struct_dsc !== 15'b000000000000000) begin $display("%b", struct_dsc); $stop(); end end + else if (cnt[30:2]==1) begin if (struct_dsc !== 15'b111111111111111) begin $display("%b", struct_dsc); $stop(); end end + else if (cnt[30:2]==2) begin if (struct_dsc !== 15'b100000000000000) begin $display("%b", struct_dsc); $stop(); end end + else if (cnt[30:2]==3) begin if (struct_dsc !== 15'b011000000000000) begin $display("%b", struct_dsc); $stop(); end end + else if (cnt[30:2]==4) begin if (struct_dsc !== 15'b000111100000000) begin $display("%b", struct_dsc); $stop(); end end + else if (cnt[30:2]==5) begin if (struct_dsc !== 15'b000000011111111) begin $display("%b", struct_dsc); $stop(); end end end else if (cnt[1:0]==2'd3) begin // read value from structure (not a very good test for now) - if (cnt[30:2]==0) begin if (struct_bg !== {WS{1'b0}}) $stop(); end - else if (cnt[30:2]==1) begin if (struct_bg !== {WS{1'b1}}) $stop(); end - else if (cnt[30:2]==2) begin if (struct_bg.e0 !== { 1{1'b1}}) $stop(); end - else if (cnt[30:2]==3) begin if (struct_bg.e1 !== { 2{1'b1}}) $stop(); end - else if (cnt[30:2]==4) begin if (struct_bg.e2 !== { 4{1'b1}}) $stop(); end - else if (cnt[30:2]==5) begin if (struct_bg.e3 !== { 8{1'b1}}) $stop(); end + if (cnt[30:2]==0) begin if (struct_dsc !== {WS{1'b0}}) $stop(); end + else if (cnt[30:2]==1) begin if (struct_dsc !== {WS{1'b1}}) $stop(); end + else if (cnt[30:2]==2) begin if (struct_dsc.e0 !== { 1{1'b1}}) $stop(); end + else if (cnt[30:2]==3) begin if (struct_dsc.e1 !== { 2{1'b1}}) $stop(); end + else if (cnt[30:2]==4) begin if (struct_dsc.e2 !== { 4{1'b1}}) $stop(); end + else if (cnt[30:2]==5) begin if (struct_dsc.e3 !== { 8{1'b1}}) $stop(); end end - // little endian + // ascending range always @ (posedge clk) if (cnt[1:0]==2'd0) begin // initialize to defaaults (all bits to 0) - if (cnt[30:2]==0) struct_lt <= '0; - else if (cnt[30:2]==1) struct_lt <= '0; - else if (cnt[30:2]==2) struct_lt <= '0; - else if (cnt[30:2]==3) struct_lt <= '0; - else if (cnt[30:2]==4) struct_lt <= '0; - else if (cnt[30:2]==5) struct_lt <= '0; + if (cnt[30:2]==0) struct_asc <= '0; + else if (cnt[30:2]==1) struct_asc <= '0; + else if (cnt[30:2]==2) struct_asc <= '0; + else if (cnt[30:2]==3) struct_asc <= '0; + else if (cnt[30:2]==4) struct_asc <= '0; + else if (cnt[30:2]==5) struct_asc <= '0; end else if (cnt[1:0]==2'd1) begin // write value to structure if (cnt[30:2]==0) begin end - else if (cnt[30:2]==1) struct_lt <= '1; - else if (cnt[30:2]==2) struct_lt.e0 <= '1; - else if (cnt[30:2]==3) struct_lt.e1 <= '1; - else if (cnt[30:2]==4) struct_lt.e2 <= '1; - else if (cnt[30:2]==5) struct_lt.e3 <= '1; + else if (cnt[30:2]==1) struct_asc <= '1; + else if (cnt[30:2]==2) struct_asc.e0 <= '1; + else if (cnt[30:2]==3) struct_asc.e1 <= '1; + else if (cnt[30:2]==4) struct_asc.e2 <= '1; + else if (cnt[30:2]==5) struct_asc.e3 <= '1; end else if (cnt[1:0]==2'd2) begin // check structure value - if (cnt[30:2]==0) begin if (struct_lt !== 15'b000000000000000) begin $display("%b", struct_lt); $stop(); end end - else if (cnt[30:2]==1) begin if (struct_lt !== 15'b111111111111111) begin $display("%b", struct_lt); $stop(); end end - else if (cnt[30:2]==2) begin if (struct_lt !== 15'b100000000000000) begin $display("%b", struct_lt); $stop(); end end - else if (cnt[30:2]==3) begin if (struct_lt !== 15'b011000000000000) begin $display("%b", struct_lt); $stop(); end end - else if (cnt[30:2]==4) begin if (struct_lt !== 15'b000111100000000) begin $display("%b", struct_lt); $stop(); end end - else if (cnt[30:2]==5) begin if (struct_lt !== 15'b000000011111111) begin $display("%b", struct_lt); $stop(); end end + if (cnt[30:2]==0) begin if (struct_asc !== 15'b000000000000000) begin $display("%b", struct_asc); $stop(); end end + else if (cnt[30:2]==1) begin if (struct_asc !== 15'b111111111111111) begin $display("%b", struct_asc); $stop(); end end + else if (cnt[30:2]==2) begin if (struct_asc !== 15'b100000000000000) begin $display("%b", struct_asc); $stop(); end end + else if (cnt[30:2]==3) begin if (struct_asc !== 15'b011000000000000) begin $display("%b", struct_asc); $stop(); end end + else if (cnt[30:2]==4) begin if (struct_asc !== 15'b000111100000000) begin $display("%b", struct_asc); $stop(); end end + else if (cnt[30:2]==5) begin if (struct_asc !== 15'b000000011111111) begin $display("%b", struct_asc); $stop(); end end end else if (cnt[1:0]==2'd3) begin // read value from structure (not a very good test for now) - if (cnt[30:2]==0) begin if (struct_lt !== {WS{1'b0}}) $stop(); end - else if (cnt[30:2]==1) begin if (struct_lt !== {WS{1'b1}}) $stop(); end - else if (cnt[30:2]==2) begin if (struct_lt.e0 !== { 1{1'b1}}) $stop(); end - else if (cnt[30:2]==3) begin if (struct_lt.e1 !== { 2{1'b1}}) $stop(); end - else if (cnt[30:2]==4) begin if (struct_lt.e2 !== { 4{1'b1}}) $stop(); end - else if (cnt[30:2]==5) begin if (struct_lt.e3 !== { 8{1'b1}}) $stop(); end + if (cnt[30:2]==0) begin if (struct_asc !== {WS{1'b0}}) $stop(); end + else if (cnt[30:2]==1) begin if (struct_asc !== {WS{1'b1}}) $stop(); end + else if (cnt[30:2]==2) begin if (struct_asc.e0 !== { 1{1'b1}}) $stop(); end + else if (cnt[30:2]==3) begin if (struct_asc.e1 !== { 2{1'b1}}) $stop(); end + else if (cnt[30:2]==4) begin if (struct_asc.e2 !== { 4{1'b1}}) $stop(); end + else if (cnt[30:2]==5) begin if (struct_asc.e3 !== { 8{1'b1}}) $stop(); end end endmodule diff --git a/test_regress/t/t_struct_unpacked.v b/test_regress/t/t_struct_unpacked.v index 14190f693..ef0ad6b2a 100644 --- a/test_regress/t/t_struct_unpacked.v +++ b/test_regress/t/t_struct_unpacked.v @@ -7,6 +7,19 @@ `define stop $stop `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); +class Cls; + typedef struct { + string m_strg; + } underclass_t; + + underclass_t m_cstr; + function underclass_t get_cstr(); + m_cstr.m_strg = "foo"; + return m_cstr; + endfunction +endclass + + module x; typedef struct { int a, b; @@ -27,6 +40,7 @@ module x; embedded_t t [1:0]; istr_t istr; string s; + Cls c; initial begin t[1].a = 2; @@ -43,6 +57,10 @@ module x; s = $sformatf("%p", istr); `checks(s, "'{m_i:'hffff, m_s:\"str2\"}"); + c = new; + s = c.get_cstr().m_strg; + `checks(s, "foo"); + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_sys_file_basic.v b/test_regress/t/t_sys_file_basic.v index 35a0d54a8..7a0f0bda6 100644 --- a/test_regress/t/t_sys_file_basic.v +++ b/test_regress/t/t_sys_file_basic.v @@ -74,10 +74,7 @@ module t; $fflush; $fclose(file); -`ifdef verilator - if (file != 0) $stop(1); // Also test arguments to stop $fwrite(file, "Never printed, file closed\n"); -`endif begin // Check for opening errors diff --git a/test_regress/t/t_sys_file_zero.pl b/test_regress/t/t_sys_file_zero.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_sys_file_zero.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_sys_file_zero.v b/test_regress/t/t_sys_file_zero.v new file mode 100644 index 000000000..252bf268b --- /dev/null +++ b/test_regress/t/t_sys_file_zero.v @@ -0,0 +1,42 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2003 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0); + +module t; + int i; + int v; + string s; + reg [100*8:1] letterl; + + initial begin + // Display formatting + $fwrite(0, "Never printed, file closed\n"); + i = $feof(0); + if (i == 0) $stop; + $fflush(0); + $fclose(0); + i = $ferror(0, letterl); + i = $fgetc(0); + `checkd(i, -1); + i = $ungetc(0, 0); + `checkd(i, -1); + i = $fgets(letterl, 0); + `checkd(i, 0); + i = $fscanf(0, "%x", v); + `checkd(i, -1); + i = $ftell(0); + `checkd(i, -1); + i = $rewind(0); + `checkd(i, -1); + i = $fseek(0, 10, 0); + `checkd(i, -1); + + $write("*-* All Finished *-*\n"); + $finish(0); // Test arguments to finish + end + +endmodule diff --git a/test_regress/t/t_sys_readmem.v b/test_regress/t/t_sys_readmem.v index ca79d8b0c..98a025ee8 100644 --- a/test_regress/t/t_sys_readmem.v +++ b/test_regress/t/t_sys_readmem.v @@ -14,7 +14,7 @@ module t; - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE reg [5:0] binary_string [2:15]; reg [5:0] binary_nostart [2:15]; reg [5:0] binary_start [0:15]; @@ -30,7 +30,7 @@ module t; reg [(32*6)-1:0] hex_align_tmp [0:15]; string fns_tmp; `endif - // verilator lint_on LITENDIAN + // verilator lint_on ASCRANGE integer i; diff --git a/test_regress/t/t_tagged.out b/test_regress/t/t_tagged.out index b0279d98d..889926ef0 100644 --- a/test_regress/t/t_tagged.out +++ b/test_regress/t/t_tagged.out @@ -8,61 +8,49 @@ %Error-UNSUPPORTED: t/t_tagged.v:18:11: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' 18 | u = tagged m_invalid; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:21:16: Unsupported: SystemVerilog 2005 reserved word not implemented: 'matches' - 21 | case (u) matches - | ^~~~~~~ %Error-UNSUPPORTED: t/t_tagged.v:22:9: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' 22 | tagged m_invalid: ; | ^~~~~~ %Error-UNSUPPORTED: t/t_tagged.v:23:9: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' 23 | tagged m_int: $stop; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:26:13: Unsupported: SystemVerilog 2005 reserved word not implemented: 'matches' - 26 | if (u matches tagged m_invalid) ; - | ^~~~~~~ +%Error-UNSUPPORTED: t/t_tagged.v:21:16: Unsupported: matches (for tagged union) + 21 | case (u) matches + | ^~~~~~~ %Error-UNSUPPORTED: t/t_tagged.v:26:21: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' 26 | if (u matches tagged m_invalid) ; | ^~~~~~ -%Error: t/t_tagged.v:26:28: syntax error, unexpected IDENTIFIER +%Error-UNSUPPORTED: t/t_tagged.v:26:13: Unsupported: matches operator 26 | if (u matches tagged m_invalid) ; - | ^~~~~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:27:13: Unsupported: SystemVerilog 2005 reserved word not implemented: 'matches' - 27 | if (u matches tagged m_int .n) $stop; | ^~~~~~~ %Error-UNSUPPORTED: t/t_tagged.v:27:21: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' 27 | if (u matches tagged m_int .n) $stop; | ^~~~~~ -%Error: t/t_tagged.v:27:28: syntax error, unexpected IDENTIFIER +%Error-UNSUPPORTED: t/t_tagged.v:27:13: Unsupported: matches operator 27 | if (u matches tagged m_int .n) $stop; - | ^~~~~ + | ^~~~~~~ %Error-UNSUPPORTED: t/t_tagged.v:29:11: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' 29 | u = tagged m_int (123); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:32:16: Unsupported: SystemVerilog 2005 reserved word not implemented: 'matches' - 32 | case (u) matches - | ^~~~~~~ %Error-UNSUPPORTED: t/t_tagged.v:33:9: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' 33 | tagged m_invalid: $stop; | ^~~~~~ %Error-UNSUPPORTED: t/t_tagged.v:34:9: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' 34 | tagged m_int .n: if (n !== 123) $stop; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:37:13: Unsupported: SystemVerilog 2005 reserved word not implemented: 'matches' - 37 | if (u matches tagged m_invalid) $stop; - | ^~~~~~~ +%Error-UNSUPPORTED: t/t_tagged.v:32:16: Unsupported: matches (for tagged union) + 32 | case (u) matches + | ^~~~~~~ %Error-UNSUPPORTED: t/t_tagged.v:37:21: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' 37 | if (u matches tagged m_invalid) $stop; | ^~~~~~ -%Error: t/t_tagged.v:37:28: syntax error, unexpected IDENTIFIER +%Error-UNSUPPORTED: t/t_tagged.v:37:13: Unsupported: matches operator 37 | if (u matches tagged m_invalid) $stop; - | ^~~~~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:38:13: Unsupported: SystemVerilog 2005 reserved word not implemented: 'matches' - 38 | if (u matches tagged m_int .n) if (n != 123) $stop; | ^~~~~~~ %Error-UNSUPPORTED: t/t_tagged.v:38:21: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' 38 | if (u matches tagged m_int .n) if (n != 123) $stop; | ^~~~~~ -%Error: t/t_tagged.v:38:28: syntax error, unexpected IDENTIFIER +%Error-UNSUPPORTED: t/t_tagged.v:38:13: Unsupported: matches operator 38 | if (u matches tagged m_int .n) if (n != 123) $stop; - | ^~~~~ + | ^~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_timing_debug2.out b/test_regress/t/t_timing_debug2.out index bbb0b3fe2..3a4ac0ecb 100644 --- a/test_regress/t/t_timing_debug2.out +++ b/test_regress/t/t_timing_debug2.out @@ -526,7 +526,7 @@ -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:91 -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:89 --V{t#,#}+ Vt_timing_debug2_t____Vfork_h########__0__1 +-V{t#,#}+ Vt_timing_debug2_t____Vfork_h########__0__0 -V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::__VnoInFunc_do_assign -V{t#,#}+ Vt_timing_debug2___024root___eval_act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act diff --git a/test_regress/t/t_timing_dpi.cpp b/test_regress/t/t_timing_dpi.cpp new file mode 100644 index 000000000..d8b610a03 --- /dev/null +++ b/test_regress/t/t_timing_dpi.cpp @@ -0,0 +1,12 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Toru Niina. +// SPDX-License-Identifier: CC0-1.0 + +#include "Vt_timing_dpi__Dpi.h" + +int tb_c_wait() { + tb_sv_wait(10); + return 0; +} diff --git a/test_regress/t/t_timing_dpi.pl b/test_regress/t/t_timing_dpi.pl new file mode 100755 index 000000000..2c494cced --- /dev/null +++ b/test_regress/t/t_timing_dpi.pl @@ -0,0 +1,28 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This file ONLY is placed under the Creative Commons Public Domain, for +# any use, without warranty, 2023 by Toru Niina. +# SPDX-License-Identifier: CC0-1.0 + + +scenarios(vlt => 1); + +if (!$Self->have_coroutines) { + skip("No coroutine support") +} +else { + compile( + v_flags2 => ["t/t_timing_dpi.cpp"], + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); + + execute( + check_finished => 1, + ); +} + +ok(1); +1 diff --git a/test_regress/t/t_timing_dpi.v b/test_regress/t/t_timing_dpi.v new file mode 100644 index 000000000..63d3bd43d --- /dev/null +++ b/test_regress/t/t_timing_dpi.v @@ -0,0 +1,46 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Toru Niina. +// SPDX-License-Identifier: CC0-1.0 + +`ifdef TEST_VERBOSE + `define WRITE_VERBOSE(msg) $write(msg) +`else + `define WRITE_VERBOSE(msg) +`endif + +`default_nettype none +`timescale 1ns/1ps + +module t; + + localparam cycle = 1000.0 / 100.0; + localparam halfcycle = 0.5 * cycle; + + logic clk = '0; + + import "DPI-C" context task tb_c_wait(); + + export "DPI-C" task tb_sv_wait; + task automatic tb_sv_wait(input int n); + `WRITE_VERBOSE("tb_sv_wait start..."); + repeat(n) @(negedge clk); + `WRITE_VERBOSE("tb_sv_wait done!"); + endtask + + always #halfcycle clk = ~clk; + + initial begin + `WRITE_VERBOSE("test start"); + repeat(10) @(posedge clk); + `WRITE_VERBOSE("calling tb_c_wait..."); + tb_c_wait(); + `WRITE_VERBOSE("tb_c_wait finish"); + repeat(10) @(posedge clk); + $write("*-* All Finished *-*\n"); + $finish; + end + + initial #(cycle*30) $stop; // timeout +endmodule diff --git a/test_regress/t/t_timing_events.v b/test_regress/t/t_timing_events.v index 24065256b..b9ae56964 100644 --- a/test_regress/t/t_timing_events.v +++ b/test_regress/t/t_timing_events.v @@ -28,6 +28,13 @@ module t; $finish; end + int x; + initial begin + x = # 1_1 'd 12_34; // Checks we parse _ correctly + if (x != 1234) $stop; + if ($time != 11) $stop; + end + initial #21 $stop; // timeout endmodule diff --git a/test_regress/t/t_timing_fork_nba.pl b/test_regress/t/t_timing_fork_nba.pl new file mode 100755 index 000000000..372246cb3 --- /dev/null +++ b/test_regress/t/t_timing_fork_nba.pl @@ -0,0 +1,23 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2023 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +if (!$Self->have_coroutines) { + skip("No coroutine support"); +} +else { + compile( + verilator_flags2 => ["--exe --timing"], + ); +} + +ok(1); +1; diff --git a/test_regress/t/t_timing_fork_nba.v b/test_regress/t/t_timing_fork_nba.v new file mode 100644 index 000000000..013367bdf --- /dev/null +++ b/test_regress/t/t_timing_fork_nba.v @@ -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, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + reg b = 0, c = 1; + + always @(posedge clk) begin + fork + b <= c; + c <= b; + join + end +endmodule diff --git a/test_regress/t/t_timing_fork_taskcall.pl b/test_regress/t/t_timing_fork_taskcall.pl new file mode 100755 index 000000000..439181d0a --- /dev/null +++ b/test_regress/t/t_timing_fork_taskcall.pl @@ -0,0 +1,28 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2023 by Antmicro Ltd. 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 + +scenarios(simulator => 1); + +if (!$Self->have_coroutines) { + skip("No coroutine support"); +} +else { + compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); + + execute( + check_finished => 1, + ); +} + +ok(1); +1; diff --git a/test_regress/t/t_timing_fork_taskcall.v b/test_regress/t/t_timing_fork_taskcall.v new file mode 100644 index 000000000..f2b45b66b --- /dev/null +++ b/test_regress/t/t_timing_fork_taskcall.v @@ -0,0 +1,23 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module t; + task foo; + #1 if ($time != 1) $stop; + #1 if ($time != 2) $stop; + #1 if ($time != 3) $stop; + endtask + + initial fork + foo; + foo; + foo; + #4 begin + $write("*-* All Finished *-*\n"); + $finish; + end + join +endmodule diff --git a/test_regress/t/t_timing_trace.out b/test_regress/t/t_timing_trace.out index f1a84fcb5..9328aceca 100644 --- a/test_regress/t/t_timing_trace.out +++ b/test_regress/t/t_timing_trace.out @@ -1,5 +1,4 @@ $version Generated by VerilatedVcd $end -$date Tue Nov 22 16:48:14 2022 $end $timescale 1ps $end $scope module TOP $end @@ -7,11 +6,11 @@ $timescale 1ps $end $var wire 32 + CLK_HALF_PERIOD [31:0] $end $var wire 32 * CLK_PERIOD [31:0] $end $var wire 1 $ a $end - $var wire 1 % b $end - $var wire 1 & c $end - $var wire 1 ) clk $end - $var wire 1 ' d $end - $var event 1 ( ev $end + $var wire 1 ) b $end + $var wire 1 % c $end + $var wire 1 ( clk $end + $var wire 1 & d $end + $var event 1 ' ev $end $var wire 1 # rst $end $upscope $end $upscope $end @@ -21,60 +20,77 @@ $enddefinitions $end #0 1# 0$ -0% +1% 0& -0' -0) +1' +0( +1) b00000000000000000000000000001010 * b00000000000000000000000000000101 + #5 -1) +1( #10 -0# -1% -1( -0) +0% +1' +0( #15 -1) -#20 1( -0) +#20 +1% +1' +0( #25 -1) +1( #30 -0) +0% +1' +0( #35 -1) +1( #40 -0) +1% +1' +0( #45 -1) +1( #50 -0) +0% +1' +0( #55 -1) +1( #60 -0) +1% +1' +0( #65 -1) +1( #70 -0) +0% +1' +0( #75 -1) +1( #80 -0) +1% +1' +0( #85 -1) +1( #90 -0) +0% +1' +0( #95 -1) +1( #100 +1% +1' +0( 0) #105 -1) -#110 -1# -0% 1( -0) +#110 +1' +0( +1) diff --git a/test_regress/t/t_timing_trace.v b/test_regress/t/t_timing_trace.v index 9893856cc..fa1471eb5 100644 --- a/test_regress/t/t_timing_trace.v +++ b/test_regress/t/t_timing_trace.v @@ -29,18 +29,16 @@ module t; clk = 0; a = 0; c = 0; - b = 0; + b = ~b; d = 0; - #CLK_PERIOD; - rst = 0; - b = 1; - -> ev ; - #CLK_PERIOD; - -> ev ; + fork #(10 * CLK_PERIOD) b = 0; join_none - #(9 * CLK_PERIOD); - -> ev ; + while (b) begin + c = ~c; + -> ev ; + #CLK_PERIOD; + end $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_timing_trace_fst.out b/test_regress/t/t_timing_trace_fst.out index a0170939c..44e15ea4b 100644 --- a/test_regress/t/t_timing_trace_fst.out +++ b/test_regress/t/t_timing_trace_fst.out @@ -1,5 +1,5 @@ $date - Tue Nov 22 18:14:18 2022 + Fri Mar 31 18:34:51 2023 $end $version @@ -26,8 +26,8 @@ $enddefinitions $end $dumpvars 1) 0( -0' -0& +1' +1& 0% 0$ 1# @@ -38,50 +38,66 @@ $end 1$ #10 0$ -0# -1& +0' 1) #15 1$ #20 0$ 1) +1' #25 1$ #30 0$ +0' +1) #35 1$ #40 0$ +1) +1' #45 1$ #50 0$ +0' +1) #55 1$ #60 0$ +1) +1' #65 1$ #70 0$ +0' +1) #75 1$ #80 0$ +1) +1' #85 1$ #90 0$ +0' +1) #95 1$ #100 0$ +1) +1' +0& #105 1$ #110 0$ +1& 1) -0& -1# diff --git a/test_regress/t/t_timing_zerodly_unsup.out b/test_regress/t/t_timing_zerodly_unsup.out index 05ffbd7fb..bc85c6dcb 100644 --- a/test_regress/t/t_timing_zerodly_unsup.out +++ b/test_regress/t/t_timing_zerodly_unsup.out @@ -1,5 +1,6 @@ -%Error-ZERODLY: t/t_timing_zerodly_unsup.v:12:13: Unsupported: #0 delays do not schedule process resumption in the Inactive region +%Warning-ZERODLY: t/t_timing_zerodly_unsup.v:12:13: Unsupported: #0 delays do not schedule process resumption in the Inactive region 12 | #0 if (v) $finish; | ^ - ... For error description see https://verilator.org/warn/ZERODLY?v=latest + ... For warning description see https://verilator.org/warn/ZERODLY?v=latest + ... Use "/* verilator lint_off ZERODLY */" and lint_on around source to disable this message. %Error: Exiting due to diff --git a/test_regress/t/t_trace_litendian.out b/test_regress/t/t_trace_ascendingrange.out similarity index 100% rename from test_regress/t/t_trace_litendian.out rename to test_regress/t/t_trace_ascendingrange.out diff --git a/test_regress/t/t_trace_litendian.pl b/test_regress/t/t_trace_ascendingrange.pl similarity index 92% rename from test_regress/t/t_trace_litendian.pl rename to test_regress/t/t_trace_ascendingrange.pl index 3ecfa6bc6..aedb9ce36 100755 --- a/test_regress/t/t_trace_litendian.pl +++ b/test_regress/t/t_trace_ascendingrange.pl @@ -14,7 +14,7 @@ scenarios(simulator => 1); # %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads. # Strangely, asking for more threads makes it go away. compile( - verilator_flags2 => ['--cc --trace --trace-params -Wno-LITENDIAN'], + verilator_flags2 => ['--cc --trace --trace-params -Wno-ASCRANGE'], threads => $Self->{vltmt} ? 6 : 1 ); diff --git a/test_regress/t/t_trace_litendian.v b/test_regress/t/t_trace_ascendingrange.v similarity index 100% rename from test_regress/t/t_trace_litendian.v rename to test_regress/t/t_trace_ascendingrange.v diff --git a/test_regress/t/t_trace_litendian_fst.out b/test_regress/t/t_trace_ascendingrange_fst.out similarity index 100% rename from test_regress/t/t_trace_litendian_fst.out rename to test_regress/t/t_trace_ascendingrange_fst.out diff --git a/test_regress/t/t_trace_litendian_fst.pl b/test_regress/t/t_trace_ascendingrange_fst.pl similarity index 88% rename from test_regress/t/t_trace_litendian_fst.pl rename to test_regress/t/t_trace_ascendingrange_fst.pl index 754bea6fd..54e75cec9 100755 --- a/test_regress/t/t_trace_litendian_fst.pl +++ b/test_regress/t/t_trace_ascendingrange_fst.pl @@ -10,13 +10,13 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -top_filename("t/t_trace_litendian.v"); +top_filename("t/t_trace_ascendingrange.v"); # CI environment offers 2 VCPUs, 2 thread setting causes the following warning. # %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads. # Strangely, asking for more threads makes it go away. compile( - verilator_flags2 => ['--cc --trace-fst --trace-params -Wno-LITENDIAN'], + verilator_flags2 => ['--cc --trace-fst --trace-params -Wno-ASCRANGE'], threads => $Self->{vltmt} ? 6 : 1 ); diff --git a/test_regress/t/t_trace_litendian_fst_sc.out b/test_regress/t/t_trace_ascendingrange_fst_sc.out similarity index 100% rename from test_regress/t/t_trace_litendian_fst_sc.out rename to test_regress/t/t_trace_ascendingrange_fst_sc.out diff --git a/test_regress/t/t_trace_litendian_fst_sc.pl b/test_regress/t/t_trace_ascendingrange_fst_sc.pl similarity index 94% rename from test_regress/t/t_trace_litendian_fst_sc.pl rename to test_regress/t/t_trace_ascendingrange_fst_sc.pl index 40366a231..864a69b97 100755 --- a/test_regress/t/t_trace_litendian_fst_sc.pl +++ b/test_regress/t/t_trace_ascendingrange_fst_sc.pl @@ -14,13 +14,13 @@ if (!$Self->have_sc) { skip("No SystemC installed"); } else { - top_filename("t/t_trace_litendian.v"); + top_filename("t/t_trace_ascendingrange.v"); # CI environment offers 2 VCPUs, 2 thread setting causes the following warning. # %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads. # Strangely, asking for more threads makes it go away. compile( - verilator_flags2 => ['--sc --trace-fst --trace-params -Wno-LITENDIAN'], + verilator_flags2 => ['--sc --trace-fst --trace-params -Wno-ASCRANGE'], threads => $Self->{vltmt} ? 6 : 1 ); diff --git a/test_regress/t/t_trace_public.v b/test_regress/t/t_trace_public.v index c3d0c9603..e6ea04953 100644 --- a/test_regress/t/t_trace_public.v +++ b/test_regress/t/t_trace_public.v @@ -68,11 +68,11 @@ module little ( input clk ); - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE reg [0:7] i8; initial i8 = '0; reg [1:49] i48; initial i48 = '0; reg [63:190] i128; initial i128 = '0; - // verilator lint_on LITENDIAN + // verilator lint_on ASCRANGE always @ (posedge clk) begin i8 <= ~i8; diff --git a/test_regress/t/t_tri_pull_implicit.pl b/test_regress/t/t_tri_pull_implicit.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_tri_pull_implicit.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_tri_pull_implicit.v b/test_regress/t/t_tri_pull_implicit.v new file mode 100644 index 000000000..c0c45e4d6 --- /dev/null +++ b/test_regress/t/t_tri_pull_implicit.v @@ -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, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + + // verilator lint_off IMPLICIT + pulldown (pd); + pullup (pu); + + initial begin + if (pd != 0) $stop; + if (pu != 1) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_typedef_array.v b/test_regress/t/t_typedef_array.v index 018d6abbd..8fe08ffcc 100644 --- a/test_regress/t/t_typedef_array.v +++ b/test_regress/t/t_typedef_array.v @@ -8,9 +8,9 @@ typedef logic logic_alias_t; module t; logic_alias_t [6:1] signal; - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE logic_alias_t [1:6] signal2; - // verilator lint_on LITENDIAN + // verilator lint_on ASCRANGE initial begin signal[6:1] = 'b100001; diff --git a/test_regress/t/t_vpi_param.cpp b/test_regress/t/t_vpi_param.cpp index 8d769ae26..1f2c5d434 100644 --- a/test_regress/t/t_vpi_param.cpp +++ b/test_regress/t/t_vpi_param.cpp @@ -21,9 +21,16 @@ #include "verilated_vcd_c.h" #include "verilated_vpi.h" +#include "svdpi.h" + +#ifdef T_VPI_PARAM #include "Vt_vpi_param.h" #include "Vt_vpi_param__Dpi.h" -#include "svdpi.h" +#elif defined(T_VPI_PUBLIC_PARAMS) +#include "Vt_vpi_public_params.h" +#else +#error "Bad test" +#endif #endif diff --git a/test_regress/t/t_vpi_public_depth.cpp b/test_regress/t/t_vpi_public_depth.cpp new file mode 100644 index 000000000..ee1217bd4 --- /dev/null +++ b/test_regress/t/t_vpi_public_depth.cpp @@ -0,0 +1,247 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2010-2023 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#ifdef IS_VPI + +#include "vpi_user.h" + +#include + +#else + +#include "verilated.h" +#include "verilated_vcd_c.h" +#include "verilated_vpi.h" + +#include VM_PREFIX_INCLUDE +#ifdef T_VPI_PUBLIC_DEPTH +#include "Vt_vpi_public_depth__Dpi.h" +#elif defined(T_VPI_PUBLIC_DEPTH_OFF) +#include "Vt_vpi_public_depth_off__Dpi.h" +#else +#error "Bad test" +#endif +#include "svdpi.h" + +#endif + +#include +#include +#include + +// These require the above. Comment prevents clang-format moving them +#include "TestSimulator.h" +#include "TestVpi.h" + +// __FILE__ is too long +#define FILENM "t_vpi_public_depth.cpp" + +#define DEBUG \ + if (0) printf + +#define CHECK_RESULT_NZ(got) \ + if (!(got)) { \ + printf("%%Error: %s:%d: GOT = NULL EXP = !NULL\n", FILENM, __LINE__); \ + return __LINE__; \ + } + +#define CHECK_RESULT_Z(got) \ + if (got) { \ + printf("%%Error: %s:%d: GOT = !NULL EXP = NULL\n", FILENM, __LINE__); \ + return __LINE__; \ + } + +#define CHECK_RESULT(got, exp) \ + if ((got) != (exp)) { \ + std::cout << std::dec << "%Error: " << FILENM << ":" << __LINE__ << ": GOT = " << (got) \ + << " EXP = " << (exp) << std::endl; \ + return __LINE__; \ + } + +#define CHECK_RESULT_CSTR(got, exp) \ + if (std::strcmp((got), (exp))) { \ + printf("%%Error: %s:%d: GOT = '%s' EXP = '%s'\n", FILENM, __LINE__, \ + (got) ? (got) : "", (exp) ? (exp) : ""); \ + return __LINE__; \ + } + +void modDump(const TestVpiHandle& it, int n) { + while (TestVpiHandle hndl = vpi_scan(it)) { + const char* nm = vpi_get_str(vpiName, hndl); + for (int i = 0; i < n; i++) printf(" "); + printf("%s\n", nm); + TestVpiHandle subIt = vpi_iterate(vpiModule, hndl); + if (subIt) modDump(subIt, n + 1); + } +} + +extern "C" { +int mon_check() { +#ifdef TEST_VERBOSE + printf("-mon_check()\n"); +#endif + + TestVpiHandle it = vpi_iterate(vpiModule, NULL); + CHECK_RESULT_NZ(it); + // Uncomment to see what other simulators return + // modDump(it, 0); + // return 1; + + TestVpiHandle topmod; + // both somepackage and t exist at the top level + while ((topmod = vpi_scan(it))) { + if (vpi_get(vpiType, topmod) == vpiModule) break; + } + CHECK_RESULT_NZ(topmod); + + const char* t_name = vpi_get_str(vpiName, topmod); + CHECK_RESULT_NZ(t_name); + + // Icarus reports the top most module as "top" + if (std::strcmp(t_name, "top") == 0) { + it = vpi_iterate(vpiModule, topmod); + CHECK_RESULT_NZ(it); + CHECK_RESULT(vpi_get(vpiType, it), vpiModule); + topmod = vpi_scan(it); + t_name = vpi_get_str(vpiName, topmod); + CHECK_RESULT_NZ(t_name); + } + CHECK_RESULT_CSTR(t_name, "t"); + TestVpiHandle topmod_done_should_be_0 = (vpi_scan(it)); + it.freed(); // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle + CHECK_RESULT_Z(topmod_done_should_be_0); + + TestVpiHandle it2 = vpi_iterate(vpiModule, topmod); + CHECK_RESULT_NZ(it2); + + TestVpiHandle mod2 = vpi_scan(it2); + CHECK_RESULT_NZ(mod2); + + const char* mod_a_name = vpi_get_str(vpiName, mod2); + CHECK_RESULT_CSTR(mod_a_name, "\\mod.a "); + + TestVpiHandle it3 = vpi_iterate(vpiModule, mod2); + +#ifdef T_VPI_PUBLIC_DEPTH + CHECK_RESULT_NZ(it3); + TestVpiHandle mod3 = vpi_scan(it3); + CHECK_RESULT_NZ(mod3); + + const char* mod_c_name = vpi_get_str(vpiName, mod3); + if (std::strcmp(mod_c_name, "\\mod_b$ ") == 0) { + // Full visibility in other simulators, skip mod_b + TestVpiHandle mod4 = vpi_scan(it3); + CHECK_RESULT_NZ(mod4); + mod_c_name = vpi_get_str(vpiName, mod4); + } + CHECK_RESULT_CSTR(mod_c_name, "\\mod\\c$ "); + +#elif defined(T_VPI_PUBLIC_DEPTH_OFF) + CHECK_RESULT_Z(it3); +#endif + + return 0; // Ok +} +} +//====================================================================== + +#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) { + const std::unique_ptr contextp{new VerilatedContext}; + + uint64_t sim_time = 1100; + contextp->debug(0); + contextp->commandArgs(argc, argv); + // We're going to be checking for these errors so don't crash out + contextp->fatalOnVpiError(0); + + { + // Construct and destroy + const std::unique_ptr topp{ + new VM_PREFIX{contextp.get(), + // Note null name - we're flattening it out + ""}}; + } + + // Test second construction + 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(contextp->time()); +#endif + } + if (!contextp->gotFinish()) { + vl_fatal(FILENM, __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_public_depth.pl b/test_regress/t/t_vpi_public_depth.pl new file mode 100755 index 000000000..930a82c94 --- /dev/null +++ b/test_regress/t/t_vpi_public_depth.pl @@ -0,0 +1,32 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2010 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 + +scenarios(simulator => 1); + +skip("Known compiler limitation") + if $Self->cxx_version =~ /\(GCC\) 4.4/; + +compile( + make_top_shell => 0, + make_main => 0, + make_pli => 1, + iv_flags2 => ["-g2005-sv"], + verilator_flags2 => ["+define+USE_DOLLAR_C32 --exe --vpi --no-l2name $Self->{t_dir}/t_vpi_public_depth.cpp --public-depth 3"], + make_flags => 'CPPFLAGS_ADD=-DTEST_VPI_PUBLIC_DEPTH', + ); + +execute( + use_libvpi => 1, + check_finished => 1, + v_flags2, + ); + +ok(1); +1; diff --git a/test_regress/t/t_vpi_public_depth.v b/test_regress/t/t_vpi_public_depth.v new file mode 100644 index 000000000..eeaedae9a --- /dev/null +++ b/test_regress/t/t_vpi_public_depth.v @@ -0,0 +1,125 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2010 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +`ifndef IVERILOG +import "DPI-C" context function int mon_check(); +`endif + +package somepackage; + int someint; +endpackage + +module t (/*AUTOARG*/ + // Inputs + clk + ); + +`ifdef USE_DOLLAR_C32 +`systemc_header +extern "C" int mon_check(); +`verilog +`endif + + input clk; + + integer status; + + wire a, b, x; + + A \mod.a (/*AUTOINST*/ + // Outputs + .x (x), + // Inputs + .clk (clk), + .a (a), + .b (b)); + + // Test loop + initial begin +`ifdef IVERILOG + status = $mon_check(); +`elsif USE_DOLLAR_C32 + status = $c32("mon_check()"); +`else + status = mon_check(); +`endif + if (status!=0) begin + $write("%%Error: t_vpi_module.cpp:%0d: C Test failed\n", status); + $stop; + end + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule : t + +module A(/*AUTOARG*/ + // Outputs + x, + // Inputs + clk, a, b + ); + + input clk; + + input a, b; + output x; + + wire y, c; + + B \mod_b$ (/*AUTOINST*/ + // Outputs + .y (y), + // Inputs + .b (b), + .c (c)); + + C \mod\c$ (/*AUTOINST*/ + // Outputs + .x (x), + // Inputs + .clk (clk), + .a (a), + .y (y)); + +endmodule : A + +module B(/*AUTOARG*/ + // Outputs + y, + // Inputs + b, c + ); + input b, c; + + output reg y; + + always @(*) begin : myproc + y = b ^ c; + end + +endmodule + +module C(/*AUTOARG*/ + // Outputs + x, + // Inputs + clk, a, y + ); + + input clk; + + input a, y; + + output reg x; + + always @(posedge clk) begin + x <= a & y; + end + +endmodule diff --git a/test_regress/t/t_vpi_public_depth_off.pl b/test_regress/t/t_vpi_public_depth_off.pl new file mode 100755 index 000000000..64de60c4d --- /dev/null +++ b/test_regress/t/t_vpi_public_depth_off.pl @@ -0,0 +1,34 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2010 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 + +scenarios(simulator => 1); + +pli_filename("t_vpi_public_depth.cpp"); +top_filename("t/t_vpi_public_depth.v"); + +skip("Known compiler limitation") + if $Self->cxx_version =~ /\(GCC\) 4.4/; + +compile( + make_top_shell => 0, + make_main => 0, + make_pli => 1, + iv_flags2 => ["-g2005-sv"], + verilator_flags2 => ["+define+USE_DOLLAR_C32 --exe --vpi --no-l2name $Self->{t_dir}/t_vpi_public_depth.cpp --public-depth 2"], + make_flags => 'CPPFLAGS_ADD=-DTEST_VPI_PUBLIC_DEPTH_OFF', + ); + +execute( + use_libvpi => 1, + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_vpi_public_params.pl b/test_regress/t/t_vpi_public_params.pl new file mode 100755 index 000000000..34224a797 --- /dev/null +++ b/test_regress/t/t_vpi_public_params.pl @@ -0,0 +1,34 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2010 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 + +scenarios(simulator => 1); + +# same vpi script should work with --public-params instead of inline publics +pli_filename("t_vpi_param.cpp"); + +skip("Known compiler limitation") + if $Self->cxx_version =~ /\(GCC\) 4.4/; + +compile( + make_top_shell => 0, + make_main => 0, + make_pli => 1, + iv_flags2 => ["-g2005-sv -D USE_VPI_NOT_DPI"], + v_flags2 => ["+define+USE_VPI_NOT_DPI"], + verilator_flags2 => ["--exe --vpi --no-l2name $Self->{t_dir}/t_vpi_param.cpp --public-params "], + ); + +execute( + use_libvpi => 1, + check_finished => 1 + ); + +ok(1); +1; diff --git a/test_regress/t/t_vpi_public_params.v b/test_regress/t/t_vpi_public_params.v new file mode 100644 index 000000000..6286e7481 --- /dev/null +++ b/test_regress/t/t_vpi_public_params.v @@ -0,0 +1,58 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2010 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 USE_VPI_NOT_DPI +//We call it via $c so we can verify DPI isn't required - see bug572 +`else +import "DPI-C" context function int mon_check(); +`endif + +// Copy of t_vpi_public_params.v but with the inline public taken out +module t #( + parameter int WIDTH = 32 + ) (/*AUTOARG*/ + // Inputs + clk + ); + +`ifdef VERILATOR +`systemc_header +extern "C" int mon_check(); +`verilog +`endif + + input clk; + + localparam int DEPTH = 16; + localparam longint PARAM_LONG = 64'hFEDCBA9876543210; + localparam string PARAM_STR = "'some string value'"; + + reg [WIDTH-1:0] mem0 [DEPTH:1]; + integer i, status; + + // Test loop + initial begin +`ifdef VERILATOR + status = $c32("mon_check()"); +`endif +`ifdef IVERILOG + status = $mon_check(); +`endif +`ifndef USE_VPI_NOT_DPI + status = mon_check(); +`endif + + if (status!=0) begin + $write("%%Error: t_vpi_param.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_var.v b/test_regress/t/t_vpi_var.v index ebc707eb3..80188af11 100644 --- a/test_regress/t/t_vpi_var.v +++ b/test_regress/t/t_vpi_var.v @@ -30,9 +30,9 @@ extern "C" int mon_check(); reg [2:1] fourthreetwoone[4:3] /*verilator public_flat_rw @(posedge clk) */; reg 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 /*verilator public_flat_rw*/; - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE reg [0:61] quads[2:3] /*verilator public_flat_rw @(posedge clk) */; - // verilator lint_on LITENDIAN + // verilator lint_on ASCRANGE reg [31:0] count /*verilator public_flat_rd */; reg [31:0] half_count /*verilator public_flat_rd */; diff --git a/test_regress/t/t_vpi_var2.v b/test_regress/t/t_vpi_var2.v index ca24c06f2..dc04a1c7f 100644 --- a/test_regress/t/t_vpi_var2.v +++ b/test_regress/t/t_vpi_var2.v @@ -39,11 +39,11 @@ extern "C" int mon_check(); reg [2:1] twoone; reg [2:1] fourthreetwoone[4:3]; reg 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; - // verilator lint_off LITENDIAN + // verilator lint_off ASCRANGE reg [0:61] quads[2:3] /*verilator public_flat_rw @(posedge clk)*/; /*verilator public_off*/ reg invisible1; - // verilator lint_on LITENDIAN + // verilator lint_on ASCRANGE /*verilator public_flat_rd_on*/ reg [31:0] count; diff --git a/test_regress/t/t_wait_order.out b/test_regress/t/t_wait_order.out new file mode 100644 index 000000000..20887769e --- /dev/null +++ b/test_regress/t/t_wait_order.out @@ -0,0 +1,17 @@ +%Error-UNSUPPORTED: t/t_wait_order.v:16:23: Unsupported: wait_order + 16 | wait_order (a, b) wif[0] = '1; + | ^ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_wait_order.v:25:23: Unsupported: wait_order + 25 | wait_order (a, b) else welse[1] = '1; + | ^ +%Error-UNSUPPORTED: t/t_wait_order.v:28:23: Unsupported: wait_order + 28 | wait_order (b, a) else nelse[1] = '1; + | ^ +%Error-UNSUPPORTED: t/t_wait_order.v:32:23: Unsupported: wait_order + 32 | wait_order (a, b) wif[2] = '1; else welse[2] = '1; + | ^ +%Error-UNSUPPORTED: t/t_wait_order.v:35:23: Unsupported: wait_order + 35 | wait_order (b, a) nif[2] = '1; else nelse[2] = '1; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_wait_order.pl b/test_regress/t/t_wait_order.pl new file mode 100755 index 000000000..2e33e5676 --- /dev/null +++ b/test_regress/t/t_wait_order.pl @@ -0,0 +1,20 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 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 + +scenarios(simulator => 1); + +compile( + expect_filename => $Self->{golden_filename}, + verilator_flags2 => ['--timing'], + fails => $Self->{vlt_all}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_wait_order.v b/test_regress/t/t_wait_order.v new file mode 100644 index 000000000..3fa46a808 --- /dev/null +++ b/test_regress/t/t_wait_order.v @@ -0,0 +1,62 @@ +// 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 + +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0); + +module t(/*AUTOARG*/); + + event a, b, c; + bit wif[10], welse[10]; + bit nif[10], nelse[10]; + + initial begin + wait_order (a, b) wif[0] = '1; + end +`ifdef FAIL_ASSERT_1 + initial begin + wait_order (b, a) nif[0] = '1; + end +`endif + + initial begin + wait_order (a, b) else welse[1] = '1; + end + initial begin + wait_order (b, a) else nelse[1] = '1; + end + + initial begin + wait_order (a, b) wif[2] = '1; else welse[2] = '1; + end + initial begin + wait_order (b, a) nif[2] = '1; else nelse[2] = '1; + end + + initial begin + #10; + -> a; + #10; + -> b; + #10; + -> c; + #10; + + `checkd(wif[0], 1'b1); + `checkd(nif[0], 1'b0); + + `checkd(welse[1], 1'b0); + `checkd(nelse[1], 1'b1); + + `checkd(wif[2], 1'b1); + `checkd(welse[2], 1'b0); + `checkd(nif[2], 1'b0); + `checkd(nelse[2], 1'b1); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_xml_begin_hier.out b/test_regress/t/t_xml_begin_hier.out new file mode 100644 index 000000000..0a71073d6 --- /dev/null +++ b/test_regress/t/t_xml_begin_hier.out @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_regress/t/t_xml_begin_hier.pl b/test_regress/t/t_xml_begin_hier.pl new file mode 100755 index 000000000..d25c191ff --- /dev/null +++ b/test_regress/t/t_xml_begin_hier.pl @@ -0,0 +1,25 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2012 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 + +scenarios(vlt => 1); + +my $out_filename = "$Self->{obj_dir}/V$Self->{name}.xml"; + +compile( + verilator_flags2 => ['--no-std', '--xml-only'], + verilator_make_gmake => 0, + make_top_shell => 0, + make_main => 0, + ); + +files_identical("$out_filename", $Self->{golden_filename}); + +ok(1); +1; diff --git a/test_regress/t/t_xml_begin_hier.v b/test_regress/t/t_xml_begin_hier.v new file mode 100644 index 000000000..1e29f0133 --- /dev/null +++ b/test_regress/t/t_xml_begin_hier.v @@ -0,0 +1,33 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Risto Pejasinovic. +// SPDX-License-Identifier: CC0-1.0 + +module submod2 (); +endmodule + +module submod #( +)(); + if(1) begin : submod_gen + wire l1_sig; + if(1) begin : nested_gen + submod2 submod_nested(); + end + submod2 submod_l1(); + end + submod2 submod_l0(); +endmodule + +module test( +); + genvar N; + generate for(N=0; N<2; N=N+1) + begin : FOR_GENERATE + submod submod_for(); + if(1) begin + submod submod_2(); + end + submod submod_3(); + end endgenerate +endmodule diff --git a/test_regress/t/t_xml_debugcheck.out b/test_regress/t/t_xml_debugcheck.out index ff409ca14..11a9f8501 100644 --- a/test_regress/t/t_xml_debugcheck.out +++ b/test_regress/t/t_xml_debugcheck.out @@ -22,7 +22,7 @@ - + @@ -49,7 +49,7 @@ - + @@ -600,25 +600,25 @@ - - - - - - - - - - - - + + + + + + + + + + + + - + - + @@ -647,10 +647,13 @@ - - - - + + + + + + + @@ -673,10 +676,13 @@ - - - - + + + + + + + @@ -1505,10 +1511,13 @@ - - - - + + + + + + + @@ -1609,7 +1618,7 @@ - + @@ -1668,7 +1677,7 @@ - + @@ -1688,7 +1697,7 @@ - + @@ -1763,7 +1772,7 @@ - + @@ -1775,7 +1784,7 @@ - + @@ -1795,16 +1804,18 @@ - + + + - +