diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6e504dcfa..8959e8ea1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,9 +39,11 @@ jobs: - os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }} - os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }} - m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }} - # Build -m32 only on ubuntu-22.04 + # Build -m32 only on ubuntu-22.04 clang++ - {os: ubuntu-18.04, m32: 1} - {os: ubuntu-20.04, m32: 1} + - compiler: { cc: gcc, cxx: g++ } + m32: 1 include: # Build GCC 10 on ubuntu-20.04 - os: ubuntu-20.04 @@ -111,6 +113,8 @@ jobs: # Build -m32 only on ubuntu-22.04 - {os: ubuntu-18.04, m32: 1} - {os: ubuntu-20.04, m32: 1} + - compiler: { cc: gcc, cxx: g++ } + m32: 1 include: # Test with GCC 10 on ubuntu-20.04 without m32 - {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: dist-vlt-0} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000..312b59474 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,84 @@ +# Build and push verilator docker image when tags are pushed to the repository. +# The following secrets must be configured in the github repository: +# DOCKER_HUB_NAMESPACE: docker hub namespace. +# DOCKER_HUB_USER: user name for logging into docker hub +# DOCKER_HUB_ACCESS_TOKEN: docker hub access token. +name: Build Verilator Container + +on: + push: + tags: [ 'v*' ] + workflow_dispatch: + inputs: + manual_tag: + description: 'Git tag to use for image build' + required: true + type: string + add_latest_tag: + description: 'Tag workflow_dispatch docker image as "latest"' + required: true + type: boolean + default: false + +jobs: + + build: + + runs-on: ubuntu-22.04 + + strategy: + matrix: + contexts: + - "ci/docker/run:verilator" + # - "ci/docker/buildenv:verilator-buildenv" + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Extract context variables + run: | + echo "${{ matrix.contexts }}" | sed -r 's/(.*):.*/build_context=\1/' >> "$GITHUB_ENV" + echo "${{ matrix.contexts }}" | sed -r 's/.*:(.*)/image_name=\1/' >> "$GITHUB_ENV" + echo "git_tag=${GITHUB_REF#refs/*/}" >> "$GITHUB_ENV" + + - name: Use manual tag + if: ${{ inputs.manual_tag }} + run: | + echo "git_tag=${{ inputs.manual_tag }}" >> "$GITHUB_ENV" + + - name: Docker meta + id: docker_meta + uses: docker/metadata-action@v4 + with: + images: | + ${{ secrets.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }} + tags: | + type=match,pattern=(v.*),group=1,enable=${{ startsWith(github.ref, 'refs/tags/v') }} + type=raw,value=${{ inputs.manual_tag }},enable=${{ inputs.manual_tag != '' }} + type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + buildkitd-flags: --debug + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_HUB_USER }} + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + - name: Build and Push to Docker + uses: docker/build-push-action@v4 + if: startsWith(github.ref, 'refs/tags/v') + with: + context: ${{ env.build_context }} + build-args: SOURCE_COMMIT=${{ env.git_tag }} + platforms: linux/arm64,linux/amd64 + push: ${{ !env.ACT && startsWith(github.ref, 'refs/tags/v') }} + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f323c05f..13957138e 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.010 + VERSION 5.012 HOMEPAGE_URL https://verilator.org LANGUAGES CXX ) diff --git a/Changes b/Changes index 637d4196d..daf8c20ad 100644 --- a/Changes +++ b/Changes @@ -8,6 +8,70 @@ The changes in each Verilator version are described below. The contributors that suggested a given feature are shown in []. Thanks! +Verilator 5.012 2023-06-13 +========================== + +**Major:** + +* With -j or --build-jobs, multithread Verilator's emit phase of Verilation. [Kamil Rakoczy, Antmicro Ltd] + Additional Verilator-internal stages will become multithreaded over time. + +**Minor:** + +* Add --main-top-name option for C main TOP name (#4235) (#4249). [Don Williamson] +* Add creating __inputs.vpp file with --debug (#4177). [Tudor Timi] +* Add NEWERSTD warning when using feature in newer language standard (#4168) (#4172). [Ethan Sifferman] +* Add warning that timing controls in DPI exports are unsupported (#4238). [Krzysztof Bieganski, Antmicro Ltd] +* Support std::process class (#4212). [Aleksander Kiryk, Antmicro Ltd] +* Support inside expressions with strings and doubles (#4138) (#4139). [Krzysztof Boroński] +* Support get_randstate/set_randstate class method functions. +* Support for condition operator on class objects (#4214). [Ryszard Rozak, Antmicro Ltd] +* Support array max (#4275). [Aleksander Kiryk, Antmicro Ltd] +* Optimize VPI callValueCbs (#4155). [Hennadii Chernyshchyk] +* Configure for faster C++ linking using 'mold', if it is installed. +* Fix crash on duplicate imported modules (#3231). [Robert Balas] +* Fix false WIDTHEXPAND on array declarations (#3959). [JOTEGO] +* Fix marking overridden methods as coroutines (#4120) (#4169). [Krzysztof Bieganski, Antmicro Ltd] +* Fix SystemC signal copy macro use (#4135). [Josep Sans] +* Fix duplicate static names in blocks in functions (#4144) (#4160). [Stefan Wallentowitz] +* Fix initialization order of initial static after function/task (#4159). [Kamil Rakoczy, Antmicro Ltd] +* Fix linking AstRefDType if it has parameterized class ref (#4164) (#4170). [Ryszard Rozak, Antmicro Ltd] +* Fix crash caused by $display() optimization (#4165) (#4166). [Tudor Timi] +* Fix arrays of unpacked structs (#4173). [Risto Pejašinović] +* Fix $fscanf of decimals overflowing variables (#4174). [Ahmed El-Mahmoudy] +* Fix super.new missing data type (#4147). [Tudor Timi] +* Fix missing class forward declarations (#4151). [Krzysztof Boroński] +* Fix hashes of instances of parameterized classes (#4182). [Ryszard Rozak, Antmicro Ltd] +* Fix forced assignments that override non-continuous assignments (#4183) (#4192). [Krzysztof Bieganski, Antmicro Ltd] +* Fix wide structure VL_TOSTRING_W generation (#4188) (#4189). [Aylon Chaim Porat] +* Fix references to members of parameterized base classes (#4196). [Ryszard Rozak, Antmicro Ltd] +* Fix tracing undefined alignment (#4201) (#4288) [John Wehle] +* Fix class specific same methods for AstVarScope, AstVar, and AstScope (#4203) (#4250). [John Wehle] +* Fix dotted references in parameterized classes (#4206). [Ryszard Rozak, Antmicro Ltd] +* Fix bit selections under parameterized classes (#4210). [Ryszard Rozak, Antmicro Ltd] +* Fix duplicate std:: declaration with -I (#4215). [Harald Pretl] +* Fix deep traversal of class inheritance timing (#4216). [Krzysztof Boroński] +* Fix class parameters of enum types (#4219). [Ryszard Rozak, Antmicro Ltd] +* Fix static methods with prototypes (#4220). [Ryszard Rozak, Antmicro Ltd] +* Fix LATCH warning on function local variables (#4221) (#4284) [Julien Margetts] +* Fix VCD scope types (#4227) (#4282). [Àlex Torregrosa] +* Fix incorrect multi-driven lint warning (#4231) (#4248). [Adrien Le Masle] +* Fix missing assignment for wide unpacked structs (#4233). [Jiamin Zhu] +* Fix unpacked struct == and != operators (#4234) (#4240). [Risto Pejašinović] +* Fix AstStructSel clean when data type is structure (#4241) (#4244). [Risto Pejašinović] +* Fix function calls in with statements (#4245). [Ryszard Rozak, Antmicro Ltd] +* Fix operator == for unpacked struct, if elements are VlUnpacked arrays (#4247). [Risto Pejašinović] +* Fix STATIC lifetime for variables created from clocking items (#4262). [Krzysztof Boroński] +* Fix names of foreach blocks (#4264). [Ryszard Rozak, Antmicro Ltd] +* Fix iterated variables in foreach loops to have VAUTOM lifetimes (#4265). [Krzysztof Boroński] +* Fix missing assignment for wide class members (#4267). [Jiamin Zhu] +* Fix the global uses timing flag when forks exist (#4274). [Krzysztof Bieganski, Antmicro Ltd] +* Fix struct redefinition (#4276). [Aleksander Kiryk, Antmicro Ltd] +* Fix detection of wire/reg duplicates. +* Fix false IMPLICITSTATIC on package functions. +* Fix method calls on function return values. + + Verilator 5.010 2023-04-30 ========================== diff --git a/Makefile.in b/Makefile.in index c845f5e76..c74aadd73 100644 --- a/Makefile.in +++ b/Makefile.in @@ -424,17 +424,15 @@ YAPF_FLAGS = -i yapf: $(YAPF) $(YAPF_FLAGS) $(PY_FILES) -FLAKE8 = flake8 -FLAKE8_FLAGS = \ - --extend-exclude=fastcov.py \ - --ignore=E123,E129,E251,E402,E501,W503,W504,E701 - PYLINT = pylint PYLINT_FLAGS = --score=n --disable=R0801 +RUFF = ruff +RUFF_FLAGS = check --ignore=E402,E501,E701 + lint-py: - -$(FLAKE8) $(FLAKE8_FLAGS) $(PY_PROGRAMS) -$(PYLINT) $(PYLINT_FLAGS) $(PY_PROGRAMS) + -$(RUFF) $(RUFF_FLAGS) $(PY_PROGRAMS) format-pl-exec: -chmod a+x test_regress/t/*.pl diff --git a/README.rst b/README.rst index 8e8e57bb8..1b8cc8435 100644 --- a/README.rst +++ b/README.rst @@ -13,6 +13,8 @@ :target: https://codecov.io/gh/verilator/verilator .. image:: https://github.com/verilator/verilator/workflows/build/badge.svg :target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild +.. image:: https://img.shields.io/docker/pulls/verilator/verilator + :target: https://hub.docker.com/r/verilator/verilator Welcome to Verilator @@ -57,17 +59,16 @@ files, the "Verilated" code. These Verilated C++/SystemC files are then compiled by a C++ compiler (gcc/clang/MSVC++), optionally along with a user's own C++/SystemC wrapper -file to instantiate the Verilated model. Executing the resulting executable -performs the design simulation. Verilator also supports linking Verilated -generated libraries, optionally encrypted, into other simulators. +file, to instantiate the Verilated model. Executing the resulting +executable performs the design simulation. Verilator also supports linking +Verilated generated libraries, optionally encrypted, into other simulators. Verilator may not be the best choice if you are expecting a full-featured replacement for a closed-source Verilog simulator, needs SDF annotation, mixed-signal simulation, or are doing a quick class project (we recommend `Icarus Verilog`_ for classwork.) However, if you are looking for a path to migrate SystemVerilog to C++/SystemC, or want high-speed simulation of -synthesizable designs containing limited verification constructs, Verilator -is the tool for you. +designs, Verilator is the tool for you. Performance @@ -83,11 +84,11 @@ as `Icarus Verilog`_. Another 2-10x speedup might be gained from multithreading (yielding 200-1000x total over interpreted simulators). Verilator has typically similar or better performance versus the -closed-source Verilog simulators (Carbon Design Systems Carbonator, +closed-source Verilog simulators (e.g., Carbon Design Systems Carbonator, Modelsim/Questa, Cadence Incisive/NC-Verilog, Synopsys VCS, VTOC, and Pragmatic CVer/CVC). But, Verilator is open-sourced, so you can spend on computes rather than licenses. Thus, Verilator gives you the best -cycles/dollar. +simulation cycles/dollar. Installation & Documentation diff --git a/bin/verilator b/bin/verilator index 5bab12f33..65acaf2f8 100755 --- a/bin/verilator +++ b/bin/verilator @@ -372,6 +372,7 @@ detailed descriptions of these arguments. --make Generate scripts for specified build tool -MAKEFLAGS Arguments to pass to make during --build --main Generate C++ main() file + --main-top-name Specify top name passed to Verilated model in generated C++ main --max-num-width Maximum number width (default: 64K) --Mdir Name of output object directory --MMD Create .d dependency files diff --git a/ci/ci-install.bash b/ci/ci-install.bash index d3372a798..96a223c81 100755 --- a/ci/ci-install.bash +++ b/ci/ci-install.bash @@ -61,7 +61,7 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then sudo apt-get update sudo apt-get install ccache help2man libfl-dev || sudo apt-get install ccache help2man libfl-dev - if [ "$CI_RUNS_ON" != "ubuntu-22.04" ]; then + if [ "$CI_RUNS_ON" = "ubuntu-20.04" ]; then # Some conflict of libunwind verison on 22.04, can live without it for now sudo apt-get install libgoogle-perftools-dev || sudo apt-get install libgoogle-perftools-dev @@ -70,6 +70,10 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then sudo apt-get install libsystemc libsystemc-dev || sudo apt-get install libsystemc libsystemc-dev fi + if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then + sudo apt-get install mold || + sudo apt-get install mold + fi if [ "$COVERAGE" = 1 ]; then yes yes | sudo cpan -fi Parallel::Forker fi @@ -102,8 +106,8 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then sudo apt-get install gdb gtkwave lcov libfl-dev ccache # Required for test_regress/t/t_dist_attributes.pl if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then - sudo apt-get install libclang-dev || - sudo apt-get install libclang-dev + sudo apt-get install libclang-dev mold || + sudo apt-get install libclang-dev mold pip3 install clang==14.0 fi if [ "$CI_RUNS_ON" = "ubuntu-20.04" ] || [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then diff --git a/ci/docker/run/Dockerfile b/ci/docker/run/Dockerfile index fa5c98519..24128ccc9 100644 --- a/ci/docker/run/Dockerfile +++ b/ci/docker/run/Dockerfile @@ -19,10 +19,16 @@ RUN apt-get update \ ccache \ flex \ git \ + help2man \ + libfl2 \ libfl-dev \ libgoogle-perftools-dev \ + numactl \ perl \ + perl-doc \ python3 \ + zlib1g \ + zlib1g-dev \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* @@ -39,7 +45,7 @@ RUN git clone "${REPO}" verilator && \ git checkout "${SOURCE_COMMIT}" && \ autoconf && \ ./configure && \ - make -j "$(nproc)" && \ + make && \ make install && \ cd .. && \ rm -r verilator diff --git a/configure.ac b/configure.ac index 3e45cc298..51af31653 100644 --- a/configure.ac +++ b/configure.ac @@ -5,12 +5,12 @@ # General Public License Version 3 or the Perl Artistic License Version 2.0. # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -# When releasing, also update header of Changes file, +# When releasing, also update header of Changes file, and CmakeLists.txt, # and commit using "devel release" or "Version bump" message # Then 'make maintainer-dist' #AC_INIT([Verilator],[#.### YYYY-MM-DD]) #AC_INIT([Verilator],[#.### devel]) -AC_INIT([Verilator],[5.010 2023-04-30], +AC_INIT([Verilator],[5.012 2023-06-13], [https://verilator.org], [verilator],[https://verilator.org]) @@ -470,6 +470,10 @@ m4_foreach([ldflag], [ AC_SUBST(CFG_LDLIBS_THREADS) AC_SUBST(CFG_LDFLAGS_THREADS_CMAKE) +# If 'mold' is installed, use it to link for faster buildtimes +_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -fuse-ld=mold) +_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_VERILATED, -fuse-ld=mold) + # When linking partially statically if test "$CFG_ENABLE_PARTIAL_STATIC" = "yes"; then _MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -static-libgcc) @@ -480,6 +484,7 @@ else LTCMALLOC=-ltcmalloc_minimal fi AC_SUBST(CFG_LDFLAGS_SRC) +AC_SUBST(CFG_LDFLAGS_VERILATED) # The pthread library is required by tcmalloc, so add it if it exists. If it # does not, the tcmalloc check below will fail anyway, and linking against diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 629517575..20c63956f 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -12,8 +12,10 @@ Alex Chadwick Aliaksei Chapyzhenka Ameya Vikram Singh Andreas Kuster +Andrei Kostovski Andrew Nolte Arkadiusz Kozdra +Aylon Chaim Porat Cameron Kirk Chris Randall Chuxuan Wang @@ -24,13 +26,16 @@ David Horton David Metz David Stanford David Turner +Don Williamson Drew Ranck Drew Taussig Driss Hafdi Edgar E. Iglesias Eric Rippey +Ethan Sifferman Eyck Jentzsch Fan Shupei +february cozzocrea Felix Neumärker Felix Yan Garrett Smith @@ -43,6 +48,7 @@ Guokai Chen Gustav Svensk G-A. Kamendje Harald Heckmann +Hennadii Chernyshchyk Howard Su Huang Rui Huanghuang Zhou @@ -69,7 +75,10 @@ Jiuyang Liu Joey Liu John Coiner John Demme +John Wehle +Jiamin Zhu Jonathan Drolet +Jose Loyola Joseph Nwabueze Josep Sans Josh Redford @@ -108,6 +117,7 @@ Mostafa Gamal Nandu Raj Nathan Kohagen Nathan Myers +Oleh Maksymenko Patrick Stewart Paul Wright Pawel Sagan @@ -136,6 +146,7 @@ Stephen Henry Steven Hugg Sören Tempel Teng Huang +Tim Hutt Tim Snyder Tobias Rosenkranz Tobias Wölfel @@ -143,6 +154,7 @@ Todd Strader Tomasz Gorochowik Topa Topino Toru Niina +Tudor Timi Tymoteusz Blazejczyk Udi Finkelstein Unai Martinez-Corral diff --git a/docs/guide/conf.py b/docs/guide/conf.py index 8755706ed..730b3e351 100644 --- a/docs/guide/conf.py +++ b/docs/guide/conf.py @@ -31,7 +31,7 @@ def get_vlt_version(): if match: try: data = os.popen('git log -n 1 --pretty=%cs').read() - except Exception: + except Exception: # pylint: disable=broad-except data = "" # fallback, and Sphinx will fill in today's date return "Devel " + match.group(1), data return "unknown", "unknown" diff --git a/docs/guide/environment.rst b/docs/guide/environment.rst index dc037cc8f..a8e4b4439 100644 --- a/docs/guide/environment.rst +++ b/docs/guide/environment.rst @@ -91,9 +91,29 @@ associated programs. .. option:: VERILATOR_ROOT - Specifies the directory containing the distribution kit. This is used - to find the executable, Perl library, and include files. If not - specified, it will come from a default optionally specified at configure - time (before Verilator was compiled). It should not be specified if - using a pre-compiled Verilator package as the hard-coded value should be - correct. See :ref:`Installation`. + The ``VERILATOR_ROOT`` environment variable is used in several places: + + * At ``./configure`` time: If set, it is embedded into the binary, and + at runtime if ``VERILATOR_ROOT`` is not set, the embedded value is + used for the runtime default. + + * When ``verilator`` is run: If ``VERILATOR_ROOT`` is set it will be + used to find the ``verilator_bin`` executable (this is the actual + Verilator binary; ``verilator`` is a Perl wrapper). If not set, the + ``verilator`` script uses other methods to find ``verilator_bin`` + (looking in the same directory and falling back to ``$PATH``). + + * When ``make`` is run on the Makefile generated by ``verilator``: The + value of ``VERILATOR_ROOT`` (falling back to the value embedded in the + binary if not set) is used to find the include files + (``include/verilated.mk``). + + If you are using a pre-compiled Verilator package, you should not need + to set ``VERILATOR_ROOT`` - the value embedded in the binary should be + correct. In fact this option *does not work* with Verilator packages + that have been installed with ``make install``. If a Verilator package + has been installed using ``./configure --prefix=/some/path && make + install`` and then moved to another location, you cannot use + ``VERILATOR_ROOT`` to point to the new version. + + See :ref:`Installation` for more details. diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 604db0702..a4d0af876 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -808,6 +808,13 @@ Summary: See also :vlopt:`--binary`. +.. option:: --main-top-name + + Specify the name passed to the Verilated model being constructed, in the + generated C++ main() function. + + If the string ``"-"`` is used, no top level scope is added. + .. option:: --max-num-width Set the maximum number literal width (e.g., in 1024'd22 this @@ -1467,8 +1474,11 @@ Summary: them systematically. The generated file is in the Verilator Configuration format, see - :ref:`Configuration Files`, and can directly be consumed by - Verilator. The standard file extension is ".vlt". + :ref:`Configuration Files`. The standard file extension is ".vlt". + These files can directly be consumed by Verilator, typically by placing + the filename as part of the Verilator command line options. Waiver files + need to be listed on the command line before listing the files they are + waiving. .. option:: -Wall diff --git a/docs/guide/faq.rst b/docs/guide/faq.rst index 4b161bc2d..621b0e3b5 100644 --- a/docs/guide/faq.rst +++ b/docs/guide/faq.rst @@ -356,8 +356,8 @@ Why do I get "undefined reference to \`VL_RAND_RESET_I' or \`Verilated::...'"? You need to link your compiled Verilated code against the :code:`verilated.cpp` file found in the include directory of the Verilator kit. This is one target in the ``$(VK_GLOBAL_OBJS)`` make variable, which -should be part of your Makefile's link rule. If you use :vlopt:`--exe`, -this is done for you. +should be part of your Makefile's link rule. If you use :vlopt:`--exe` or +:vlopt:`--binary`, this is done for you. Is the PLI supported? diff --git a/docs/guide/files.rst b/docs/guide/files.rst index d34ea6aa7..219979350 100644 --- a/docs/guide/files.rst +++ b/docs/guide/files.rst @@ -126,8 +126,10 @@ In specific debug and other modes, it also creates: - Debugging graph files (from --debug) * - *{prefix}{misc}*\ .tree - Debugging files (from --debug) - * - {mod_prefix}_{each_verilog_base_filename}*\ .vpp - - Pre-processed verilog (from --debug) + * - *{prefix}*\ __inputs\ .vpp + - Pre-processed verilog for all files (from --debug) + * - *{prefix}*\ _ *{each_verilog_base_filename}*\ .vpp + - Pre-processed verilog for each file (from --debug) After running Make, the C++ compiler may produce the following: diff --git a/docs/guide/install.rst b/docs/guide/install.rst index b6e50d16b..81da117c9 100644 --- a/docs/guide/install.rst +++ b/docs/guide/install.rst @@ -39,7 +39,7 @@ In brief, to install from git: :: # Prerequisites: - #sudo apt-get install git perl python3 make autoconf g++ flex bison ccache + #sudo apt-get install git help2man perl python3 make autoconf g++ flex bison ccache #sudo apt-get install libgoogle-perftools-dev numactl perl-doc #sudo apt-get install libfl2 # Ubuntu only (ignore if gives error) #sudo apt-get install libfl-dev # Ubuntu only (ignore if gives error) @@ -104,6 +104,7 @@ for good performance: :: sudo apt-get install ccache # If present at build, needed for run + sudo apt-get install mold # If present at build, needed for run sudo apt-get install libgoogle-perftools-dev numactl The following is optional but is recommended for nicely rendered command line @@ -124,9 +125,9 @@ Those developing Verilator itself may also want these (see internals.rst): :: - sudo apt-get install gdb graphviz cmake clang clang-format-14 gprof lcov + sudo apt-get install clang clang-format-14 cmake gdb gprof graphviz lcov sudo apt-get install libclang-dev yapf3 - sudo pip3 install clang sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe + sudo pip3 install clang sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe ruff cpan install Pod::Perldoc cpan install Parallel::Forker @@ -201,8 +202,9 @@ These are the installation options: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Our personal favorite is to always run Verilator in-place from its Git -directory. This allows the easiest experimentation and upgrading, and -allows many versions of Verilator to co-exist on a system. +directory (don't run ``make install``). This allows the easiest +experimentation and upgrading, and allows many versions of Verilator to +co-exist on a system. :: @@ -211,17 +213,18 @@ allows many versions of Verilator to co-exist on a system. ./configure # Running will use files from $VERILATOR_ROOT, so no install needed -Note after installing (below steps), a calling program or shell must set -the environment variable :option:`VERILATOR_ROOT` to point to this Git -directory, then execute ``$VERILATOR_ROOT/bin/verilator``, which will find -the path to all needed files. +Note after installing (see `Installation`_), a calling program or shell +must set the environment variable :option:`VERILATOR_ROOT` to point to this +Git directory, then execute ``$VERILATOR_ROOT/bin/verilator``, which will +find the path to all needed files. -2. Install into a specific location -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2. Install into a Specific Prefix +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -You may eventually be installing onto a project/company-wide "CAD" tools -disk that may support multiple versions of every tool. Tell configure the +You may be an OS package maintainer building a Verilator package, or you +may eventually be installing onto a project/company-wide "CAD" tools disk +that may support multiple versions of every tool. Tell configure the eventual destination directory name. We recommend that the destination location include the Verilator version name: @@ -232,7 +235,8 @@ location include the Verilator version name: # For the tarball, use the version number instead of git describe ./configure --prefix /CAD_DISK/verilator/`git describe | sed "s/verilator_//"` -Note after installing (below steps), if you use `modulecmd +Note after installing (see `Installation`_), you need to add the path to +the ``bin`` directory to your ``PATH``. Or, if you use `modulecmd `__, you'll want a module file like the following: @@ -245,23 +249,7 @@ following: prepend-path PKG_CONFIG_PATH $install_root/share/pkgconfig -3. Install into a Specific Path -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You may eventually install Verilator into a specific installation prefix, -as most GNU tools support: - -:: - - unset VERILATOR_ROOT # if your shell is bash - unsetenv VERILATOR_ROOT # if your shell is csh - ./configure --prefix /opt/verilator-VERSION - -Then after installing (below steps), you will need to add -``/opt/verilator-VERSION/bin`` to your ``$PATH`` environment variable. - - -4. Install System Globally +3. Install System Globally ^^^^^^^^^^^^^^^^^^^^^^^^^^ The final option is to eventually install Verilator globally, using @@ -273,8 +261,8 @@ configure's default system paths: unsetenv VERILATOR_ROOT # if your shell is csh ./configure -Then after installing (below), the binaries should be in a location -already in your ``$PATH`` environment variable. +Then after installing (see `Installation`_), the binaries should be in a +location already in your ``$PATH`` environment variable. Configure diff --git a/docs/guide/simulating.rst b/docs/guide/simulating.rst index 5f9440da9..8067b0366 100644 --- a/docs/guide/simulating.rst +++ b/docs/guide/simulating.rst @@ -143,7 +143,7 @@ Functional Coverage ------------------- With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will -translate functional coverage points the user has inserted manually win +translate functional coverage points the user has inserted manually in SystemVerilog code through into the Verilated model. Currently, all functional coverage points are specified using SystemVerilog diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index b88f2ce20..5dc90fba4 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -769,11 +769,14 @@ List Of Warnings 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 - automatic". + If the function is in a module, and does not require static behavior, + change it to "function automatic". - If the function requires static behavior, change it to "function - static". + If the function is in a module, and requires static behavior, change it + to "function static". + + If the function is in a package, it defaults to static, and label the + function's variables as static. Ignoring this warning will only suppress the lint check; it will simulate correctly. @@ -972,6 +975,21 @@ List Of Warnings (neither :vlopt:`--timing` nor :vlopt:`--no-timing` option was provided). +.. option:: NEWERSTD + + Warns that a feature requires a newer standard of Verilog or SystemVerilog + than the one specified by the :vlopt:`--language` option. For example, unsized + unbased literals (`'0`, `'1`, `'z`, `'x`) require 1800-2005 or later. + + To avoid this warning, use a Verilog or SystemVerilog standard that + supports the feature. Alternatively, modify your code to use a different + syntax that is supported by the Verilog/SystemVerilog standard specified + by the :vlopt:`--language` option. + + Ignoring this warning will only suppress the lint check; it will + simulate correctly. + + .. option:: NOLATCH .. TODO better example diff --git a/docs/internals.rst b/docs/internals.rst index c055aae8c..ca4b626e4 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -638,7 +638,22 @@ condition. See the `Timing Pass` section for more details. Timing Pass ~~~~~~~~~~~ -The visitor in ``V3Timing.cpp`` transforms each timing control into a ``co_await``. +There are two visitors in ``V3Timing.cpp``. + +The first one, ``TimingSuspendableVisitor``, does not perform any AST +transformations. It is responsible for marking processes and C++ functions that +contain timing controls as suspendable. Processes that call suspendable +functions are also marked as suspendable. Functions that call, are overridden +by, or override suspendable functions are marked as suspendable as well. + +The visitor keeps a dependency graph of functions and processes to handle such +cases. A function or process is dependent on a function if it calls it. A +virtual class method is dependent on another class method if it calls it, +overrides it, or is overriden by it. + +The second visitor in ``V3Timing.cpp``, ``TimingControlVisitor``, uses the +information provided by ``TimingSuspendableVisitor`` and transforms each timing +control into a ``co_await``. * event controls are turned into ``co_await`` on a trigger scheduler's ``trigger`` method. The awaited trigger scheduler is the one corresponding to @@ -670,14 +685,9 @@ Each sub-statement of a ``fork`` is put in an ``AstBegin`` node for easier grouping. In a later step, each of these gets transformed into a new, separate function. See the `Forks` section for more detail. -Processes that use awaits are marked as suspendable. Later, during ``V3Sched``, -they are transformed into coroutines. Functions that use awaits get the return -type of ``VlCoroutine``. This immediately makes them coroutines. Note that if a -process calls a function that is a coroutine, the call gets wrapped in an -await, which means the process itself will be marked as suspendable. A virtual -function is a coroutine if any of its overriding or overridden functions are -coroutines. The visitor keeps a dependency graph of functions and processes to -handle such cases. +Suspendable functions get the return type of ``VlCoroutine``, which makes them +coroutines. Later, during ``V3Sched``, suspendable processes are also +transformed into coroutines. Scheduling with timing ~~~~~~~~~~~~~~~~~~~~~~ @@ -1503,8 +1513,19 @@ debug level 5, with the V3Width.cpp file at level 9. --debug ------- -When you run with ``--debug``, there are two primary output file types -placed into the obj_dir, .tree and .dot files. +When you run with ``--debug``, there are three primary output file types +placed into the obj_dir, .vpp, .tree and .dot files. + +.vpp Output +----------- + +Verilator creates a *{mod_prefix}*\ __inputs\ .vpp file containing all the +files that were read, filtered by preprocessing. This file can be fed back +into Verilator, replacing on the command line all of the previous input +files, to enable simplification of test cases. + +Verilator also creates .vpp files for each individual file passed on the +command line. .dot Output diff --git a/docs/spelling.txt b/docs/spelling.txt index f4660bed4..2dca6d079 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -260,6 +260,7 @@ Prateek Pre Preprocess Pretet +Pretl Priyadharshini Pullup Pulver @@ -472,6 +473,7 @@ constexpr constpool coredump coroutine +coroutines countbits countones cout @@ -602,6 +604,7 @@ genvar genvars getenv getline +ggdb gmake gmon gotFinish @@ -791,6 +794,7 @@ qrq radix randc randcase +randstate rarr rdtsc reStructuredText @@ -896,6 +900,7 @@ typedefs typename uint un +unbased undef undefineall undriven @@ -966,3 +971,4 @@ ypq yurivict zdave Øyvind + diff --git a/include/gtkwave/fstapi.c b/include/gtkwave/fstapi.c index df8b23866..205e43952 100644 --- a/include/gtkwave/fstapi.c +++ b/include/gtkwave/fstapi.c @@ -2052,7 +2052,8 @@ if(xc && !xc->already_in_close && !xc->already_in_flush) int zfd; int fourpack_duo = 0; #ifndef __MINGW32__ - char *fnam = (char *)malloc(strlen(xc->filename) + 5 + 1); + int fnam_len = strlen(xc->filename) + 5 + 1; + char *fnam = (char *)malloc(fnam_len); #endif fixup_offs = ftello(xc->handle); @@ -2142,7 +2143,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush) fflush(xc->handle); #ifndef __MINGW32__ - sprintf(fnam, "%s.hier", xc->filename); + snprintf(fnam, fnam_len, "%s.hier", xc->filename); unlink(fnam); free(fnam); #endif @@ -2821,7 +2822,7 @@ if(ctx && name && literal_arr && val_arr && (elem_count != 0)) uint32_t i; name_len = strlen(name); - elem_count_len = sprintf(elem_count_buf, "%" PRIu32, elem_count); + elem_count_len = snprintf(elem_count_buf, 16, "%" PRIu32, elem_count); literal_lens = (unsigned int *)calloc(elem_count, sizeof(unsigned int)); val_lens = (unsigned int *)calloc(elem_count, sizeof(unsigned int)); @@ -3909,7 +3910,8 @@ int pass_status = 1; if(!xc->fh) { fst_off_t offs_cache = ftello(xc->f); - char *fnam = (char *)malloc(strlen(xc->filename) + 6 + 16 + 32 + 1); + int fnam_len = strlen(xc->filename) + 6 + 16 + 32 + 1; + char *fnam = (char *)malloc(fnam_len); unsigned char *mem = (unsigned char *)malloc(FST_GZIO_LEN); fst_off_t hl, uclen; fst_off_t clen = 0; @@ -3928,7 +3930,7 @@ if(!xc->fh) htyp = xc->contains_hier_section_lz4duo ? FST_BL_HIER_LZ4DUO : FST_BL_HIER_LZ4; } - sprintf(fnam, "%s.hier_%d_%p", xc->filename, getpid(), (void *)xc); + snprintf(fnam, fnam_len, "%s.hier_%d_%p", xc->filename, getpid(), (void *)xc); fstReaderFseeko(xc, xc->f, xc->hier_pos, SEEK_SET); uclen = fstReaderUint64(xc->f); #ifndef __MINGW32__ @@ -4585,15 +4587,17 @@ if(sectype == FST_BL_ZWRAPPER) int zfd; int flen = strlen(xc->filename); char *hf; + int hf_len; seclen = fstReaderUint64(xc->f); uclen = fstReaderUint64(xc->f); if(!seclen) return(0); /* not finished compressing, this is a failed read */ - hf = (char *)calloc(1, flen + 16 + 32 + 1); + hf_len = flen + 16 + 32 + 1; + hf = (char *)calloc(1, hf_len); - sprintf(hf, "%s.upk_%d_%p", xc->filename, getpid(), (void *)xc); + snprintf(hf, hf_len, "%s.upk_%d_%p", xc->filename, getpid(), (void *)xc); fcomp = fopen(hf, "w+b"); if(!fcomp) { @@ -5189,16 +5193,16 @@ for(;;) if(beg_tim) { - if(dumpvars_state == 1) { wx_len = sprintf(wx_buf, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; } - wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", beg_tim); + if(dumpvars_state == 1) { wx_len = snprintf(wx_buf, 32, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; } + wx_len = snprintf(wx_buf, 32, "#%" PRIu64 "\n", beg_tim); fstWritex(xc, wx_buf, wx_len); - if(!dumpvars_state) { wx_len = sprintf(wx_buf, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; } + if(!dumpvars_state) { wx_len = snprintf(wx_buf, 32, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; } } if((xc->num_blackouts)&&(cur_blackout != xc->num_blackouts)) { if(beg_tim == xc->blackout_times[cur_blackout]) { - wx_len = sprintf(wx_buf, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off"); + wx_len = snprintf(wx_buf, 32, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off"); fstWritex(xc, wx_buf, wx_len); } } @@ -5332,7 +5336,7 @@ for(;;) clone_d[j] = srcdata[7-j]; } } - sprintf((char *)xc->temp_signal_value_buf, "%.16g", d); + snprintf((char *)xc->temp_signal_value_buf, xc->longest_signal_value_len + 1, "%.16g", d); value_change_callback(user_callback_data_pointer, beg_tim, idx+1, xc->temp_signal_value_buf); } } @@ -5360,7 +5364,7 @@ for(;;) } fstVcdID(vcdid_buf, idx+1); - wx_len = sprintf(wx_buf, "r%.16g %s\n", d, vcdid_buf); + wx_len = snprintf(wx_buf, 64, "r%.16g %s\n", d, vcdid_buf); fstWritex(xc, wx_buf, wx_len); } } @@ -5625,16 +5629,16 @@ for(;;) } } - if(dumpvars_state == 1) { wx_len = sprintf(wx_buf, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; } - wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", time_table[i]); + if(dumpvars_state == 1) { wx_len = snprintf(wx_buf, 32, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; } + wx_len = snprintf(wx_buf, 32, "#%" PRIu64 "\n", time_table[i]); fstWritex(xc, wx_buf, wx_len); - if(!dumpvars_state) { wx_len = sprintf(wx_buf, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; } + if(!dumpvars_state) { wx_len = snprintf(wx_buf, 32, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; } if((xc->num_blackouts)&&(cur_blackout != xc->num_blackouts)) { if(time_table[i] == xc->blackout_times[cur_blackout]) { - wx_len = sprintf(wx_buf, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off"); + wx_len = snprintf(wx_buf, 32, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off"); fstWritex(xc, wx_buf, wx_len); } } @@ -5884,7 +5888,7 @@ for(;;) clone_d[j] = srcdata[7-j]; } } - sprintf((char *)xc->temp_signal_value_buf, "%.16g", d); + snprintf((char *)xc->temp_signal_value_buf, xc->longest_signal_value_len + 1, "%.16g", d); value_change_callback(user_callback_data_pointer, time_table[i], idx+1, xc->temp_signal_value_buf); } } @@ -5910,7 +5914,7 @@ for(;;) } } - wx_len = sprintf(wx_buf, "r%.16g", d); + wx_len = snprintf(wx_buf, 32, "r%.16g", d); fstWritex(xc, wx_buf, wx_len); } } @@ -6016,7 +6020,7 @@ if(xc->signal_lens[facidx] == 1) } } - sprintf((char *)buf, "%.16g", d); + snprintf((char *)buf, 32, "%.16g", d); /* this will write 18 bytes */ } } @@ -6608,7 +6612,7 @@ if(xc->signal_lens[facidx] == 1) } } - sprintf(buf, "r%.16g", d); + snprintf(buf, 32, "r%.16g", d); /* this will write 19 bytes */ return(buf); } } diff --git a/include/verilated.cpp b/include/verilated.cpp index dc23517cc..af1f419b1 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -315,15 +315,28 @@ void VlRNG::srandom(uint64_t n) VL_MT_UNSAFE { 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: } +std::string VlRNG::get_randstate() const VL_MT_UNSAFE { + // Though not stated in IEEE, assumption is the string must be printable + const char* const stateCharsp = reinterpret_cast(&m_state); + static_assert(sizeof(m_state) == 16, ""); + std::string result{"R00112233445566770011223344556677"}; + for (int i = 0; i < sizeof(m_state); ++i) { + result[1 + i * 2] = 'a' + ((stateCharsp[i] >> 4) & 15); + result[1 + i * 2 + 1] = 'a' + (stateCharsp[i] & 15); + } + return result; +} +void VlRNG::set_randstate(const std::string& state) VL_MT_UNSAFE { + if (VL_UNLIKELY((state.length() != 1 + 2 * sizeof(m_state)) || (state[0] != 'R'))) { + VL_PRINTF_MT("%%Warning: set_randstate ignored as state string not from get_randstate\n"); + return; + } + char* const stateCharsp = reinterpret_cast(&m_state); + for (int i = 0; i < sizeof(m_state); ++i) { + stateCharsp[i] + = (((state[1 + i * 2] - 'a') & 15) << 4) | ((state[1 + i * 2 + 1] - 'a') & 15); + } +} static uint32_t vl_sys_rand32() VL_MT_SAFE { // Return random 32-bits using system library. @@ -1360,16 +1373,18 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf *p = t_tmp; } else if (obits <= VL_BYTESIZE) { CData* const p = va_arg(ap, CData*); - *p = owp[0]; + *p = VL_CLEAN_II(obits, obits, owp[0]); } else if (obits <= VL_SHORTSIZE) { SData* const p = va_arg(ap, SData*); - *p = owp[0]; + *p = VL_CLEAN_II(obits, obits, owp[0]); } else if (obits <= VL_IDATASIZE) { IData* const p = va_arg(ap, IData*); - *p = owp[0]; + *p = VL_CLEAN_II(obits, obits, owp[0]); } else if (obits <= VL_QUADSIZE) { QData* const p = va_arg(ap, QData*); - *p = VL_SET_QW(owp); + *p = VL_CLEAN_QQ(obits, obits, VL_SET_QW(owp)); + } else { + _vl_clean_inplace_w(obits, owp); } } } // switch @@ -3213,15 +3228,18 @@ void VerilatedAssertOneThread::fatal_different() VL_MT_SAFE { //=========================================================================== // VlDeleter:: Methods -void VlDeleter::deleteAll() { +void VlDeleter::deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_MT_SAFE { while (true) { - VerilatedLockGuard lock{m_mutex}; - if (m_newGarbage.empty()) break; - VerilatedLockGuard deleteLock{m_deleteMutex}; - std::swap(m_newGarbage, m_toDelete); - lock.unlock(); // So destructors can enqueue new objects + { + VerilatedLockGuard lock{m_mutex}; + if (m_newGarbage.empty()) break; + m_deleteMutex.lock(); + std::swap(m_newGarbage, m_toDelete); + // m_mutex is unlocked here, so destructors can enqueue new objects + } for (VlDeletable* const objp : m_toDelete) delete objp; m_toDelete.clear(); + m_deleteMutex.unlock(); } } diff --git a/include/verilated.h b/include/verilated.h index ac2d3c773..4ee19b681 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -175,6 +175,7 @@ public: /// Construct mutex (without locking it) VerilatedMutex() = default; ~VerilatedMutex() = default; + VL_UNCOPYABLE(VerilatedMutex); const VerilatedMutex& operator!() const { return *this; } // For -fthread_safety /// Acquire/lock mutex void lock() VL_ACQUIRE() VL_MT_SAFE { @@ -218,23 +219,10 @@ public: /// Construct and hold given mutex lock until destruction or unlock() explicit VerilatedLockGuard(VerilatedMutex& mutexr) VL_ACQUIRE(mutexr) VL_MT_SAFE : m_mutexr(mutexr) { // Need () or GCC 4.8 false warning - m_mutexr.lock(); + mutexr.lock(); } /// Destruct and unlock the mutex ~VerilatedLockGuard() 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); - } }; // Internals: Remember the calling thread at construction time, and make diff --git a/include/verilated.mk.in b/include/verilated.mk.in index 7a2d51ff6..4f138288b 100644 --- a/include/verilated.mk.in +++ b/include/verilated.mk.in @@ -31,6 +31,8 @@ CFG_CXXFLAGS_NO_UNUSED = @CFG_CXXFLAGS_NO_UNUSED@ CFG_CXXFLAGS_WEXTRA = @CFG_CXXFLAGS_WEXTRA@ # Compiler flags that enable coroutine support CFG_CXXFLAGS_COROUTINES = @CFG_CXXFLAGS_COROUTINES@ +# Linker flags +CFG_LDFLAGS_VERILATED = @CFG_LDFLAGS_VERILATED@ # Linker libraries for multithreading CFG_LDLIBS_THREADS = @CFG_LDLIBS_THREADS@ @@ -77,6 +79,8 @@ VPATH += .. VPATH += $(VERILATOR_ROOT)/include VPATH += $(VERILATOR_ROOT)/include/vltstd +LDFLAGS += $(CFG_LDFLAGS_VERILATED) + #OPT = -ggdb -DPRINTINITSTR -DDETECTCHANGE #OPT = -ggdb -DPRINTINITSTR CPPFLAGS += $(OPT) diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index 983f19b4c..ec5ff4eb3 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -152,9 +152,11 @@ extern const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE; // PLIi // Base macros // Return true if data[bit] set; not 0/1 return, but 0/non-zero return. +// Arguments must not have side effects #define VL_BITISSETLIMIT_W(data, width, bit) (((bit) < (width)) && VL_BITISSET_W(data, bit)) // Shift appropriate word by bit. Does not account for wrapping between two words +// Argument 'bit' must not have side effects #define VL_BITRSHIFT_W(data, bit) ((data)[VL_BITWORD_E(bit)] >> VL_BITBIT_E(bit)) // Create two 32-bit words from quadword @@ -758,6 +760,7 @@ static inline IData VL_ONEHOT0_W(int words, WDataInP const lwp) VL_PURE { static inline IData VL_CLOG2_I(IData lhs) VL_PURE { // There are faster algorithms, or fls GCC4 builtins, but rarely used + // In C++20 there will be std::bit_width(lhs) - 1 if (VL_UNLIKELY(!lhs)) return 0; --lhs; int shifts = 0; @@ -953,11 +956,19 @@ static inline void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE // EMIT_RULE: VL_MUL: oclean=dirty; lclean==clean; rclean==clean; // EMIT_RULE: VL_DIV: oclean=dirty; lclean==clean; rclean==clean; // EMIT_RULE: VL_MODDIV: oclean=dirty; lclean==clean; rclean==clean; -#define VL_DIV_III(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) / (rhs)) -#define VL_DIV_QQQ(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) / (rhs)) +static inline IData VL_DIV_III(int lbits, IData lhs, IData rhs) { + return (rhs == 0) ? 0 : lhs / rhs; +} +static inline QData VL_DIV_QQQ(int lbits, QData lhs, QData rhs) { + return (rhs == 0) ? 0 : lhs / rhs; +} #define VL_DIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 0)) -#define VL_MODDIV_III(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) % (rhs)) -#define VL_MODDIV_QQQ(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) % (rhs)) +static inline IData VL_MODDIV_III(int lbits, IData lhs, IData rhs) { + return (rhs == 0) ? 0 : lhs % rhs; +} +static inline QData VL_MODDIV_QQQ(int lbits, QData lhs, QData rhs) { + return (rhs == 0) ? 0 : lhs % rhs; +} #define VL_MODDIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 1)) static inline WDataOutP VL_ADD_W(int words, WDataOutP owp, WDataInP const lwp, diff --git a/include/verilated_std.sv b/include/verilated_std.sv index 9a1153c76..bb7fe1be8 100644 --- a/include/verilated_std.sv +++ b/include/verilated_std.sv @@ -26,10 +26,6 @@ // verilator lint_off TIMESCALEMOD // verilator lint_off UNUSEDSIGNAL package std; - // The process class is not implemented, but it's predeclared here, - // so the linter accepts references to it. - typedef class process; - class mailbox #(type T); protected int m_bound; protected T m_queue[$]; @@ -45,7 +41,7 @@ package std; task put(T message); `ifdef VERILATOR_TIMING if (m_bound != 0) - wait (m_queue.size() < m_bound); + wait (m_queue.size() < m_bound); m_queue.push_back(message); `endif endtask @@ -117,43 +113,80 @@ package std; endclass class process; - typedef enum { FINISHED, RUNNING, WAITING, SUSPENDED, KILLED } state; - static process _s_global_process; + typedef enum { + FINISHED = 0, + RUNNING = 1, + WAITING = 2, + SUSPENDED = 3, + KILLED = 4 + } state; + +`ifdef VERILATOR_TIMING + // Width visitor changes it to VlProcessRef + protected chandle m_process; +`endif + static function process self(); - // Unsupported, emulating with single process' state - if (!_s_global_process) _s_global_process = new; - return _s_global_process; + process p = new; +`ifdef VERILATOR_TIMING + $c(p.m_process, " = vlProcess;"); +`endif + return p; endfunction + + protected function void set_status(state s); +`ifdef VERILATOR_TIMING + $c(m_process, "->state(", s, ");"); +`endif + endfunction + function state status(); - // Unsupported, emulating with single process' state +`ifdef VERILATOR_TIMING + return state'($c(m_process, "->state()")); +`else return RUNNING; +`endif endfunction + function void kill(); - $error("std::process::kill() not supported"); + set_status(KILLED); 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"); + set_status(RUNNING); endfunction - // When really implemented, srandom must operates on the process, but for + + task await(); +`ifdef VERILATOR_TIMING + wait (status() == FINISHED || status() == KILLED); +`endif + endtask + + // When really implemented, srandom must operate on the process, but for // now rely on the srandom() that is automatically generated for all // classes. + // // function void srandom(int seed); // endfunction + + // The methods below work only if set_randstate is never applied to + // a state string created before another such string. Full support + // could use VlRNG class to store the state per process in VlProcess + // objects. function string get_randstate(); - // Could operate on all proceses for now - // No error, as harmless until set_randstate is called - return "NOT_SUPPORTED"; + string s; + + s.itoa($random); // Get a random number + set_randstate(s); // Pretend it's the state of RNG + return s; endfunction - function void set_randstate(string randstate); - $error("std::process::set_randstate() not supported"); - // Could operate on all proceses for now + + function void set_randstate(string s); + $urandom(s.atoi()); // Set the seed using a string endfunction endclass - endpackage diff --git a/include/verilated_threads.h b/include/verilated_threads.h index dfb949f2d..155862c9b 100644 --- a/include/verilated_threads.h +++ b/include/verilated_threads.h @@ -173,7 +173,7 @@ public: VerilatedLockGuard lock{m_mutex}; while (m_ready.empty()) { m_waiting = true; - m_cv.wait(lock); + m_cv.wait(m_mutex); } m_waiting = false; // As noted above this is inefficient if our ready list is ever diff --git a/include/verilated_timing.cpp b/include/verilated_timing.cpp index f644ed64f..30b3fbf7f 100644 --- a/include/verilated_timing.cpp +++ b/include/verilated_timing.cpp @@ -30,7 +30,16 @@ void VlCoroutineHandle::resume() { // main process if (VL_LIKELY(m_coro)) { VL_DEBUG_IF(VL_DBG_MSGF(" Resuming: "); dump();); - m_coro(); + if (m_process) { // If process state is managed with std::process + if (m_process->state() == VlProcess::KILLED) { + m_coro.destroy(); + } else { + m_process->state(VlProcess::RUNNING); + m_coro(); + } + } else { + m_coro(); + } m_coro = nullptr; } } @@ -95,8 +104,9 @@ void VlTriggerScheduler::resume(const char* eventDescription) { VL_DEBUG_IF(dump(eventDescription); VL_DBG_MSGF(" Resuming processes waiting for %s\n", eventDescription);); #endif - for (auto& susp : m_ready) susp.resume(); - m_ready.clear(); + std::swap(m_ready, m_resumeQueue); + for (VlCoroutineHandle& coro : m_resumeQueue) coro.resume(); + m_resumeQueue.clear(); commit(eventDescription); } diff --git a/include/verilated_timing.h b/include/verilated_timing.h index 019573185..1e63009c5 100644 --- a/include/verilated_timing.h +++ b/include/verilated_timing.h @@ -86,6 +86,36 @@ public: #endif }; +//=================================================================== +// VlProcess stores metadata of running processes + +class VlProcess final { + // MEMBERS + int m_state; // Current state of the process + +public: + // TYPES + enum : int { // Type int for compatibility with $c + FINISHED = 0, + RUNNING = 1, + WAITING = 2, + SUSPENDED = 3, + KILLED = 4, + }; + + // CONSTRUCTORS + VlProcess() + : m_state{RUNNING} {} + + // METHODS + int state() { return m_state; } + void state(int s) { m_state = s; } +}; + +using VlProcessRef = std::shared_ptr; + +inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); } + //============================================================================= // VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is // cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got @@ -96,25 +126,38 @@ class VlCoroutineHandle final { // MEMBERS std::coroutine_handle<> m_coro; // The wrapped coroutine handle + VlProcessRef m_process; // Data of the suspended process, null if not needed VlFileLineDebug m_fileline; public: // CONSTRUCTORS // Construct - VlCoroutineHandle() - : m_coro{nullptr} {} - VlCoroutineHandle(std::coroutine_handle<> coro, VlFileLineDebug fileline) + VlCoroutineHandle(VlProcessRef process) + : m_coro{nullptr} + , m_process{process} { + if (m_process) m_process->state(VlProcess::WAITING); + } + VlCoroutineHandle(std::coroutine_handle<> coro, VlProcessRef process, VlFileLineDebug fileline) : m_coro{coro} - , m_fileline{fileline} {} + , m_process{process} + , m_fileline{fileline} { + if (m_process) m_process->state(VlProcess::WAITING); + } // Move the handle, leaving a nullptr VlCoroutineHandle(VlCoroutineHandle&& moved) : m_coro{std::exchange(moved.m_coro, nullptr)} + , m_process{moved.m_process} , m_fileline{moved.m_fileline} {} // Destroy if the handle isn't null ~VlCoroutineHandle() { // Usually these coroutines should get resumed; we only need to clean up if we destroy a // model with some coroutines suspended - if (VL_UNLIKELY(m_coro)) m_coro.destroy(); + if (VL_UNLIKELY(m_coro)) { + m_coro.destroy(); + if (m_process && m_process->state() != VlProcess::KILLED) { + m_process->state(VlProcess::FINISHED); + } + } } // METHODS // Move the handle, leaving a null handle @@ -122,7 +165,7 @@ public: m_coro = std::exchange(moved.m_coro, nullptr); return *this; } - // Resume the coroutine if the handle isn't null + // Resume the coroutine if the handle isn't null and the process isn't killed void resume(); #ifdef VL_DEBUG void dump() const; @@ -173,21 +216,24 @@ public: void dump() const; #endif // Used by coroutines for co_awaiting a certain simulation time - auto delay(uint64_t delay, const char* filename = VL_UNKNOWN, int lineno = 0) { + auto delay(uint64_t delay, VlProcessRef process, const char* filename = VL_UNKNOWN, + int lineno = 0) { struct Awaitable { + VlProcessRef process; // Data of the suspended process, null if not needed VlDelayedCoroutineQueue& queue; uint64_t delay; VlFileLineDebug fileline; bool await_ready() const { return false; } // Always suspend void await_suspend(std::coroutine_handle<> coro) { - queue.push_back({delay, VlCoroutineHandle{coro, fileline}}); + queue.push_back({delay, VlCoroutineHandle{coro, process, fileline}}); // Move last element to the proper place in the max-heap std::push_heap(queue.begin(), queue.end()); } void await_resume() const {} }; - return Awaitable{m_queue, m_context.time() + delay, VlFileLineDebug{filename, lineno}}; + return Awaitable{process, m_queue, m_context.time() + delay, + VlFileLineDebug{filename, lineno}}; } }; @@ -207,6 +253,10 @@ class VlTriggerScheduler final { // (not resumable) VlCoroutineVec m_ready; // Coroutines that can be resumed (all coros from m_uncommitted are // moved here in commit()) + VlCoroutineVec m_resumeQueue; // Coroutines being resumed by resume(); kept as a field to + // avoid reallocation. Resumed coroutines are moved to + // m_resumeQueue to allow adding coroutines to m_ready + // during resume(). Outside of resume() should always be empty. public: // METHODS @@ -220,21 +270,23 @@ public: void dump(const char* eventDescription) const; #endif // Used by coroutines for co_awaiting a certain trigger - auto trigger(const char* eventDescription = VL_UNKNOWN, const char* filename = VL_UNKNOWN, - int lineno = 0) { + auto trigger(bool commit, VlProcessRef process, const char* eventDescription = VL_UNKNOWN, + const char* filename = VL_UNKNOWN, int lineno = 0) { VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n", eventDescription, filename, lineno);); struct Awaitable { VlCoroutineVec& suspended; // Coros waiting on trigger + VlProcessRef process; // Data of the suspended process, null if not needed VlFileLineDebug fileline; bool await_ready() const { return false; } // Always suspend void await_suspend(std::coroutine_handle<> coro) { - suspended.emplace_back(coro, fileline); + suspended.emplace_back(coro, process, fileline); } void await_resume() const {} }; - return Awaitable{m_uncommitted, VlFileLineDebug{filename, lineno}}; + return Awaitable{commit ? m_ready : m_uncommitted, process, + VlFileLineDebug{filename, lineno}}; } }; @@ -267,18 +319,19 @@ class VlDynamicTriggerScheduler final { // with destructive post updates, e.g. named events) // METHODS - auto awaitable(VlCoroutineVec& queue, const char* filename, int lineno) { + auto awaitable(VlProcessRef process, VlCoroutineVec& queue, const char* filename, int lineno) { struct Awaitable { + VlProcessRef process; // Data of the suspended process, null if not needed VlCoroutineVec& suspended; // Coros waiting on trigger VlFileLineDebug fileline; bool await_ready() const { return false; } // Always suspend void await_suspend(std::coroutine_handle<> coro) { - suspended.emplace_back(coro, fileline); + suspended.emplace_back(coro, process, fileline); } void await_resume() const {} }; - return Awaitable{queue, VlFileLineDebug{filename, lineno}}; + return Awaitable{process, queue, VlFileLineDebug{filename, lineno}}; } public: @@ -292,23 +345,26 @@ public: void dump() const; #endif // Used by coroutines for co_awaiting trigger evaluation - auto evaluation(const char* eventDescription, const char* filename, int lineno) { + auto evaluation(VlProcessRef process, const char* eventDescription, const char* filename, + int lineno) { VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n", eventDescription, filename, lineno);); - return awaitable(m_suspended, filename, lineno); + return awaitable(process, m_suspended, filename, lineno); } // Used by coroutines for co_awaiting the trigger post update step - auto postUpdate(const char* eventDescription, const char* filename, int lineno) { + auto postUpdate(VlProcessRef process, const char* eventDescription, const char* filename, + int lineno) { VL_DEBUG_IF( VL_DBG_MSGF(" Process waiting for %s at %s:%d awaiting the post update step\n", eventDescription, filename, lineno);); - return awaitable(m_post, filename, lineno); + return awaitable(process, m_post, filename, lineno); } // Used by coroutines for co_awaiting the resumption step (in 'act' eval) - auto resumption(const char* eventDescription, const char* filename, int lineno) { + auto resumption(VlProcessRef process, const char* eventDescription, const char* filename, + int lineno) { VL_DEBUG_IF(VL_DBG_MSGF(" Process waiting for %s at %s:%d awaiting resumption\n", eventDescription, filename, lineno);); - return awaitable(m_triggered, filename, lineno); + return awaitable(process, m_triggered, filename, lineno); } }; @@ -338,24 +394,27 @@ class VlForkSync final { public: // Create the join object and set the counter to the specified number - void init(size_t count) { m_join.reset(new VlJoin{count, {}}); } + void init(size_t count, VlProcessRef process) { m_join.reset(new VlJoin{count, {process}}); } // Called whenever any of the forked processes finishes. If the join counter reaches 0, the // main process gets resumed void done(const char* filename = VL_UNKNOWN, int lineno = 0); // Used by coroutines for co_awaiting a join - auto join(const char* filename = VL_UNKNOWN, int lineno = 0) { + auto join(VlProcessRef process, const char* filename = VL_UNKNOWN, int lineno = 0) { assert(m_join); VL_DEBUG_IF( VL_DBG_MSGF(" Awaiting join of fork at: %s:%d\n", filename, lineno);); struct Awaitable { + VlProcessRef process; // Data of the suspended process, null if not needed const std::shared_ptr join; // Join to await on VlFileLineDebug fileline; bool await_ready() { return join->m_counter == 0; } // Suspend if join still exists - void await_suspend(std::coroutine_handle<> coro) { join->m_susp = {coro, fileline}; } + void await_suspend(std::coroutine_handle<> coro) { + join->m_susp = {coro, process, fileline}; + } void await_resume() const {} }; - return Awaitable{m_join, VlFileLineDebug{filename, lineno}}; + return Awaitable{process, m_join, VlFileLineDebug{filename, lineno}}; } }; diff --git a/include/verilated_trace.h b/include/verilated_trace.h index 48b7e3558..7e8694b70 100644 --- a/include/verilated_trace.h +++ b/include/verilated_trace.h @@ -75,7 +75,7 @@ public: // Get an element from the front of the queue. Blocks if none available T get() VL_MT_SAFE_EXCLUDES(m_mutex) { VerilatedLockGuard lock{m_mutex}; - m_cv.wait(lock, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); }); + m_cv.wait(m_mutex, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); }); assert(!m_queue.empty()); T value = m_queue.front(); m_queue.pop_front(); @@ -447,7 +447,9 @@ public: if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits); } VL_ATTR_ALWINLINE void chgQData(uint32_t* oldp, QData newval, int bits) { - const uint64_t diff = *reinterpret_cast(oldp) ^ newval; + QData old; + std::memcpy(&old, oldp, sizeof(old)); + const uint64_t diff = old ^ newval; if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits); } VL_ATTR_ALWINLINE void chgWData(uint32_t* oldp, const WData* newvalp, int bits) { @@ -460,8 +462,9 @@ public: } VL_ATTR_ALWINLINE void chgEvent(uint32_t* oldp, VlEvent newval) { fullEvent(oldp, newval); } VL_ATTR_ALWINLINE void chgDouble(uint32_t* oldp, double newval) { - // cppcheck-suppress invalidPointerCast - if (VL_UNLIKELY(*reinterpret_cast(oldp) != newval)) fullDouble(oldp, newval); + double old; + std::memcpy(&old, oldp, sizeof(old)); + if (VL_UNLIKELY(old != newval)) fullDouble(oldp, newval); } }; diff --git a/include/verilated_trace_imp.h b/include/verilated_trace_imp.h index f4972d163..9f5792127 100644 --- a/include/verilated_trace_imp.h +++ b/include/verilated_trace_imp.h @@ -482,7 +482,7 @@ VL_ATTR_NOINLINE void VerilatedTrace::ParallelWorkerData::wa // We have been spinning for a while, so yield the thread VerilatedLockGuard lock{m_mutex}; m_waiting = true; - m_cv.wait(lock, [this] { return m_ready.load(std::memory_order_relaxed); }); + m_cv.wait(m_mutex, [this] { return m_ready.load(std::memory_order_relaxed); }); m_waiting = false; } @@ -872,7 +872,7 @@ void VerilatedTraceBuffer::fullIData(uint32_t* oldp, IData newval, int template <> void VerilatedTraceBuffer::fullQData(uint32_t* oldp, QData newval, int bits) { const uint32_t code = oldp - m_sigs_oldvalp; - *reinterpret_cast(oldp) = newval; + std::memcpy(oldp, &newval, sizeof(newval)); if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return; emitQData(code, newval, bits); } @@ -888,7 +888,7 @@ void VerilatedTraceBuffer::fullWData(uint32_t* oldp, const WData* newv template <> void VerilatedTraceBuffer::fullDouble(uint32_t* oldp, double newval) { const uint32_t code = oldp - m_sigs_oldvalp; - *reinterpret_cast(oldp) = newval; + std::memcpy(oldp, &newval, sizeof(newval)); if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return; // cppcheck-suppress invalidPointerCast emitDouble(code, newval); diff --git a/include/verilated_types.h b/include/verilated_types.h index 0214e8fb4..cbd05ad96 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -161,8 +161,8 @@ public: 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; + std::string get_randstate() const VL_MT_UNSAFE; + 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; @@ -1040,9 +1040,15 @@ void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename, template struct VlUnpacked final { +private: + // TYPES + using T_Key = IData; // Index type, for uniformity with other containers + using Unpacked = T_Value[T_Depth]; + +public: // MEMBERS // This should be the only data member, otherwise generated static initializers need updating - T_Value m_storage[T_Depth]; // Contents of the unpacked array + Unpacked m_storage; // Contents of the unpacked array // CONSTRUCTORS // Default constructors and destructor are used. Note however that C++20 requires that @@ -1053,6 +1059,7 @@ struct VlUnpacked final { // Default copy assignment operators are used. // METHODS +public: // Raw access WData* data() { return &m_storage[0]; } const WData* data() const { return &m_storage[0]; } @@ -1065,6 +1072,170 @@ struct VlUnpacked final { bool neq(const VlUnpacked& that) const { return neq(*this, that); } // Similar to 'neq' above, *this = that used for change detection void assign(const VlUnpacked& that) { *this = that; } + bool operator==(const VlUnpacked& that) const { return !neq(that); } + bool operator!=(const VlUnpacked& that) { return neq(that); } + + void sort() { std::sort(std::begin(m_storage), std::end(m_storage)); } + template + void sort(Func with_func) { + // with_func returns arbitrary type to use for the sort comparison + std::sort(std::begin(m_storage), std::end(m_storage), + [=](const T_Value& a, const T_Value& b) { + // index number is meaningless with sort, as it changes + return with_func(0, a) < with_func(0, b); + }); + } + // std::rbegin/std::rend not available until C++14 + void rsort() { + std::sort(std::begin(m_storage), std::end(m_storage), std::greater()); + } + template + void rsort(Func with_func) { + // with_func returns arbitrary type to use for the sort comparison + // std::rbegin/std::rend not available until C++14, so using > below + std::sort(std::begin(m_storage), std::end(m_storage), + [=](const T_Value& a, const T_Value& b) { + // index number is meaningless with sort, as it changes + return with_func(0, a) > with_func(0, b); + }); + } + void reverse() { std::reverse(std::begin(m_storage), std::end(m_storage)); } + void shuffle() { std::shuffle(std::begin(m_storage), std::end(m_storage), VlURNG{}); } + VlQueue unique() const { + VlQueue out; + std::set saw; + for (const auto& i : m_storage) { + const auto it = saw.find(i); + if (it == saw.end()) { + saw.insert(it, i); + out.push_back(i); + } + } + return out; + } + template + VlQueue unique(Func with_func) const { + VlQueue out; + std::set saw; + for (const auto& i : m_storage) { + const auto i_mapped = with_func(0, i); + const auto it = saw.find(i_mapped); + if (it == saw.end()) { + saw.insert(it, i_mapped); + out.push_back(i); + } + } + return out; + } + VlQueue unique_index() const { + VlQueue out; + IData index = 0; + std::set saw; + for (const auto& i : m_storage) { + const auto it = saw.find(i); + if (it == saw.end()) { + saw.insert(it, i); + out.push_back(index); + } + ++index; + } + return out; + } + template + VlQueue unique_index(Func with_func) const { + VlQueue out; + IData index = 0; + std::unordered_set saw; + for (const auto& i : m_storage) { + const auto i_mapped = with_func(index, i); + auto it = saw.find(i_mapped); + if (it == saw.end()) { + saw.insert(it, i_mapped); + out.push_back(index); + } + ++index; + } + return out; + } + template + VlQueue find(Func with_func) const { + VlQueue out; + IData index = 0; + for (const auto& i : m_storage) { + if (with_func(index, i)) out.push_back(i); + ++index; + } + return out; + } + template + VlQueue find_index(Func with_func) const { + VlQueue out; + IData index = 0; + for (const auto& i : m_storage) { + if (with_func(index, i)) out.push_back(index); + ++index; + } + return out; + } + template + VlQueue find_first(Func with_func) const { + // Can't use std::find_if as need index number + IData index = 0; + for (const auto& i : m_storage) { + if (with_func(index, i)) return VlQueue::cons(i); + ++index; + } + return VlQueue{}; + } + template + VlQueue find_first_index(Func with_func) const { + IData index = 0; + for (const auto& i : m_storage) { + if (with_func(index, i)) return VlQueue::cons(index); + ++index; + } + return VlQueue{}; + } + template + VlQueue find_last(Func with_func) const { + for (int i = T_Depth - 1; i >= 0; i--) { + if (with_func(i, m_storage[i])) return VlQueue::cons(m_storage[i]); + } + return VlQueue{}; + } + template + VlQueue find_last_index(Func with_func) const { + for (int i = T_Depth - 1; i >= 0; i--) { + if (with_func(i, m_storage[i])) return VlQueue::cons(i); + } + return VlQueue{}; + } + + // Reduction operators + VlQueue min() const { + const auto it = std::min_element(std::begin(m_storage), std::end(m_storage)); + return VlQueue::cons(*it); + } + template + VlQueue min(Func with_func) const { + const auto it = std::min_element(std::begin(m_storage), std::end(m_storage), + [&with_func](const IData& a, const IData& b) { + return with_func(0, a) < with_func(0, b); + }); + return VlQueue::cons(*it); + } + VlQueue max() const { + const auto it = std::max_element(std::begin(m_storage), std::end(m_storage)); + return VlQueue::cons(*it); + } + template + VlQueue max(Func with_func) const { + const auto it = std::max_element(std::begin(m_storage), std::end(m_storage), + [&with_func](const IData& a, const IData& b) { + return with_func(0, a) < with_func(0, b); + }); + return VlQueue::cons(*it); + } // Dumping. Verilog: str = $sformatf("%p", assoc) std::string to_string() const { @@ -1138,7 +1309,7 @@ public: } // Deletes all queued garbage objects. - void deleteAll() VL_MT_SAFE; + void deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_MT_SAFE; }; //=================================================================== diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index e9efd953c..b05967c1b 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -410,28 +410,14 @@ void VerilatedVcd::dumpHeader() { if (*np == ' ') np++; if (*np == '\t') break; // tab means signal name starts printIndent(1); - // Find character after name end - const char* sp = np; - while (*sp && *sp != ' ' && *sp != '\t' && !(*sp & '\x80')) sp++; - - printStr("$scope "); - if (*sp & '\x80') { - switch (*sp & 0x7f) { - case VLT_TRACE_SCOPE_STRUCT: printStr("struct "); break; - case VLT_TRACE_SCOPE_INTERFACE: printStr("interface "); break; - case VLT_TRACE_SCOPE_UNION: printStr("union "); break; - default: printStr("module "); - } - } else { - printStr("module "); - } + printStr("$scope module "); for (; *np && *np != ' ' && *np != '\t'; np++) { if (*np == '[') { printStr("["); } else if (*np == ']') { printStr("]"); - } else if (!(*np & '\x80')) { + } else { *m_writep++ = *np; } } diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 4d84eca49..083137c95 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -165,6 +165,7 @@ class VerilatedVpioVarBase VL_NOT_FINAL : public VerilatedVpio { protected: const VerilatedVar* m_varp = nullptr; const VerilatedScope* m_scopep = nullptr; + std::string m_fullname; const VerilatedRange& get_range() const { // Determine number of dimensions and return outermost return (m_varp->dims() > 1) ? m_varp->unpacked() : m_varp->packed(); @@ -173,11 +174,13 @@ protected: public: VerilatedVpioVarBase(const VerilatedVar* varp, const VerilatedScope* scopep) : m_varp{varp} - , m_scopep{scopep} {} + , m_scopep{scopep} + , m_fullname{std::string{m_scopep->name()} + '.' + name()} {} explicit VerilatedVpioVarBase(const VerilatedVpioVarBase* varp) { if (varp) { m_varp = varp->m_varp; m_scopep = varp->m_scopep; + m_fullname = varp->m_fullname; } } static VerilatedVpioVarBase* castp(vpiHandle h) { @@ -188,11 +191,7 @@ public: uint32_t size() const override { return get_range().elements(); } const VerilatedRange* rangep() const override { return &get_range(); } const char* name() const override { return m_varp->name(); } - const char* fullname() const override { - static thread_local std::string t_out; - t_out = std::string{m_scopep->name()} + "." + name(); - return t_out.c_str(); - } + const char* fullname() const override { return m_fullname.c_str(); } }; class VerilatedVpioParam final : public VerilatedVpioVarBase { @@ -646,23 +645,21 @@ public: continue; } VerilatedVpiCbHolder& ho = *it++; - if (VerilatedVpioVar* const varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) { - void* const newDatap = varop->varDatap(); - void* const prevDatap - = varop->prevDatap(); // Was malloced when we added the callback - VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n", - varop->fullname(), *(static_cast(newDatap)), - *(static_cast(prevDatap)), newDatap, - prevDatap);); - if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) { - VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n", - ho.id(), varop->fullname(), - *(static_cast(newDatap)));); - update.insert(varop); - vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value); - (ho.cb_rtnp())(ho.cb_datap()); - called = true; - } + VerilatedVpioVar* const varop + = reinterpret_cast(ho.cb_datap()->obj); + void* const newDatap = varop->varDatap(); + void* const prevDatap = varop->prevDatap(); // Was malloced when we added the callback + VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n", + varop->fullname(), *(static_cast(newDatap)), + *(static_cast(prevDatap)), newDatap, prevDatap);); + if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) { + VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n", + ho.id(), varop->fullname(), + *(static_cast(newDatap)));); + update.insert(varop); + vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value); + (ho.cb_rtnp())(ho.cb_datap()); + called = true; } if (was_last) break; } diff --git a/include/verilatedos.h b/include/verilatedos.h index 625552d4f..fdcbe64cd 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -37,8 +37,13 @@ //========================================================================= // Compiler pragma abstraction +#if defined(__clang__) +# define VL_CLANG_ATTR(attr) __attribute__(( attr )) +#else +# define VL_CLANG_ATTR(attr) +#endif + #ifdef __GNUC__ -# define VL_ATTR_ALIGNED(alignment) __attribute__((aligned(alignment))) # define VL_ATTR_ALWINLINE __attribute__((always_inline)) inline # define VL_ATTR_NOINLINE __attribute__((noinline)) # define VL_ATTR_COLD __attribute__((cold)) @@ -57,19 +62,6 @@ // All VL_ATTR_WEAK symbols must be marked with the macOS -U linker flag in verilated.mk.in # define VL_ATTR_WEAK __attribute__((weak)) # endif -# if defined(__clang__) -# define VL_ACQUIRE(...) __attribute__((annotate("ACQUIRE"))) __attribute__((acquire_capability(__VA_ARGS__))) -# define VL_ACQUIRE_SHARED(...) __attribute__((annotate("ACQUIRE_SHARED"))) __attribute__((acquire_shared_capability(__VA_ARGS__))) -# define VL_RELEASE(...) __attribute__((annotate("RELEASE"))) __attribute__((release_capability(__VA_ARGS__))) -# define VL_RELEASE_SHARED(...) __attribute__((annotate("RELEASE_SHARED"))) __attribute__((release_shared_capability(__VA_ARGS__))) -# define VL_TRY_ACQUIRE(...) __attribute__((try_acquire_capability(__VA_ARGS__))) -# define VL_TRY_ACQUIRE_SHARED(...) __attribute__((try_acquire_shared_capability(__VA_ARGS__))) -# define VL_CAPABILITY(x) __attribute__((capability(x))) -# define VL_REQUIRES(x) __attribute__((annotate("REQUIRES"))) __attribute__((requires_capability(x))) -# define VL_GUARDED_BY(x) __attribute__((annotate("GUARDED_BY"))) __attribute__((guarded_by(x))) -# define VL_EXCLUDES(x) __attribute__((annotate("EXCLUDES"))) __attribute__((locks_excluded(x))) -# define VL_SCOPED_CAPABILITY __attribute__((scoped_lockable)) -# endif # define VL_LIKELY(x) __builtin_expect(!!(x), 1) // Prefer over C++20 [[likely]] # define VL_UNLIKELY(x) __builtin_expect(!!(x), 0) // Prefer over C++20 [[unlikely]] # define VL_UNREACHABLE __builtin_unreachable() // C++23 std::unreachable() @@ -77,10 +69,58 @@ # define VL_PREFETCH_RW(p) __builtin_prefetch((p), 1) #endif +// Function acquires a capability/lock (-fthread-safety) +#define VL_ACQUIRE(...) \ + VL_CLANG_ATTR(annotate("ACQUIRE")) \ + VL_CLANG_ATTR(acquire_capability(__VA_ARGS__)) +// Function acquires a shared capability/lock (-fthread-safety) +#define VL_ACQUIRE_SHARED(...) \ + VL_CLANG_ATTR(annotate("ACQUIRE_SHARED")) \ + VL_CLANG_ATTR(acquire_shared_capability(__VA_ARGS__)) +// Function releases a capability/lock (-fthread-safety) +#define VL_RELEASE(...) \ + VL_CLANG_ATTR(annotate("RELEASE")) \ + VL_CLANG_ATTR(release_capability(__VA_ARGS__)) +// Function releases a shared capability/lock (-fthread-safety) +#define VL_RELEASE_SHARED(...) \ + VL_CLANG_ATTR(annotate("RELEASE_SHARED")) \ + VL_CLANG_ATTR(release_shared_capability(__VA_ARGS__)) +// Function returns bool if acquired a capability (-fthread-safety) +#define VL_TRY_ACQUIRE(...) \ + VL_CLANG_ATTR(try_acquire_capability(__VA_ARGS__)) +// Function returns bool if acquired shared (-fthread-safety) +#define VL_TRY_ACQUIRE_SHARED(...) \ + VL_CLANG_ATTR(try_acquire_shared_capability(__VA_ARGS__)) +// Function requires a capability inbound (-fthread-safety) +#define VL_CAPABILITY(x) \ + VL_CLANG_ATTR(capability(x)) +// Function requires not having a capability inbound (-fthread-safety) +#define VL_REQUIRES(x) \ + VL_CLANG_ATTR(annotate("REQUIRES")) \ + VL_CLANG_ATTR(requires_capability(x)) +// Name of capability/lock (-fthread-safety) +#define VL_GUARDED_BY(x) \ + VL_CLANG_ATTR(annotate("GUARDED_BY")) \ + VL_CLANG_ATTR(guarded_by(x)) +// The data that the annotated pointer points to is protected by the given capability. +// The pointer itself is not protected. +// Allowed on: pointer data member. (-fthread-safety) +#define VL_PT_GUARDED_BY(x) \ + VL_CLANG_ATTR(annotate("PT_GUARDED_BY")) \ + VL_CLANG_ATTR(pt_guarded_by(x)) +// Name of mutex protecting this variable (-fthread-safety) +#define VL_EXCLUDES(x) \ + VL_CLANG_ATTR(annotate("EXCLUDES")) \ + VL_CLANG_ATTR(locks_excluded(x)) +// Scoped threaded capability/lock (-fthread-safety) +#define VL_SCOPED_CAPABILITY \ + VL_CLANG_ATTR(scoped_lockable) +// Annotated function returns reference to the given capability. +// Allowed on: function, method. (-fthread-safety) +#define VL_RETURN_CAPABILITY(x) \ + VL_CLANG_ATTR(lock_returned(x)) + // Defaults for unsupported compiler features -#ifndef VL_ATTR_ALIGNED -# define VL_ATTR_ALIGNED(alignment) ///< Attribute to align structure to byte alignment -#endif #ifndef VL_ATTR_ALWINLINE # define VL_ATTR_ALWINLINE ///< Attribute to inline, even when not optimizing #endif @@ -111,19 +151,6 @@ #ifndef VL_ATTR_WEAK # define VL_ATTR_WEAK ///< Attribute that function external that is optionally defined #endif -#ifndef VL_CAPABILITY -# define VL_ACQUIRE(...) ///< Function acquires a capability/lock (-fthread-safety) -# define VL_ACQUIRE_SHARED(...) ///< Function acquires a shared capability/lock (-fthread-safety) -# define VL_RELEASE(...) ///< Function releases a capability/lock (-fthread-safety) -# define VL_RELEASE_SHARED(...) ///< Function releases a shared capability/lock (-fthread-safety) -# define VL_TRY_ACQUIRE(...) ///< Function returns bool if acquired a capability (-fthread-safety) -# define VL_TRY_ACQUIRE_SHARED(...) ///< Function returns bool if acquired shared (-fthread-safety) -# define VL_REQUIRES(x) ///< Function requires a capability inbound (-fthread-safety) -# define VL_EXCLUDES(x) ///< Function requires not having a capability inbound (-fthread-safety) -# define VL_CAPABILITY(x) ///< Name of capability/lock (-fthread-safety) -# define VL_GUARDED_BY(x) ///< Name of mutex protecting this variable (-fthread-safety) -# define VL_SCOPED_CAPABILITY ///< Scoped threaded capability/lock (-fthread-safety) -#endif #ifndef VL_LIKELY # define VL_LIKELY(x) (!!(x)) ///< Return boolean expression that is more often true # define VL_UNLIKELY(x) (!!(x)) ///< Return boolean expression that is more often false @@ -142,6 +169,7 @@ #ifndef VL_NO_LEGACY +# define VL_ATTR_ALIGNED(alignment) // Deprecated # define VL_FUNC __func__ // Deprecated # define VL_THREAD // Deprecated # define VL_THREAD_LOCAL thread_local // Deprecated @@ -149,56 +177,24 @@ #endif // Comment tag that Function is pure (and thus also VL_MT_SAFE) -#if defined(__clang__) -# define VL_PURE __attribute__((annotate("PURE"))) -#else -# define VL_PURE -#endif +#define VL_PURE VL_CLANG_ATTR(annotate("PURE")) // Comment tag that function is threadsafe -#if defined(__clang__) -# define VL_MT_SAFE __attribute__((annotate("MT_SAFE"))) -#else -# define VL_MT_SAFE -#endif +#define VL_MT_SAFE VL_CLANG_ATTR(annotate("MT_SAFE")) // 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 +#define VL_MT_STABLE VL_CLANG_ATTR(annotate("MT_STABLE")) // Comment tag that function is threadsafe, only // during normal operation (post-init) -#if defined(__clang__) -# define VL_MT_SAFE_POSTINIT __attribute__((annotate("MT_SAFE_POSTINIT"))) -#else -# define VL_MT_SAFE_POSTINIT -#endif +#define VL_MT_SAFE_POSTINIT VL_CLANG_ATTR(annotate("MT_SAFE_POSTINIT")) // Attribute that function is clang threadsafe and uses given mutex -#if defined(__clang__) -# define VL_MT_SAFE_EXCLUDES(mutex) __attribute__((annotate("MT_SAFE_EXCLUDES"))) VL_EXCLUDES(mutex) -#else -# define VL_MT_SAFE_EXCLUDES(mutex) VL_EXCLUDES(mutex) -#endif +#define VL_MT_SAFE_EXCLUDES(mutex) VL_CLANG_ATTR(annotate("MT_SAFE_EXCLUDES")) VL_EXCLUDES(mutex) // Comment tag that function is not threadsafe -#if defined(__clang__) -# define VL_MT_UNSAFE __attribute__((annotate("MT_UNSAFE"))) -#else -# define VL_MT_UNSAFE -#endif +#define VL_MT_UNSAFE VL_CLANG_ATTR(annotate("MT_UNSAFE")) // Comment tag that function is not threadsafe // protected to make sure single-caller -#if defined(__clang__) -# define VL_MT_UNSAFE_ONE __attribute__((annotate("MT_UNSAFE_ONE"))) -#else -# define VL_MT_UNSAFE_ONE -#endif +#define VL_MT_UNSAFE_ONE VL_CLANG_ATTR(annotate("MT_UNSAFE_ONE")) // Comment tag that function is entry point of parallelization -#if defined(__clang__) -# define VL_MT_START __attribute__((annotate("MT_START"))) -#else -# define VL_MT_START -#endif +#define VL_MT_START VL_CLANG_ATTR(annotate("MT_START")) #ifndef VL_NO_LEGACY # define VL_ULL(c) (c##ULL) // Add appropriate suffix to 64-bit constant (deprecated) @@ -324,6 +320,7 @@ extern "C" void __gcov_dump(); // Now that C++ requires these standard types the vl types are deprecated #include #include +#include #ifndef VL_NO_LEGACY using vluint8_t = uint8_t; ///< 8-bit unsigned type (backward compatibility) @@ -448,11 +445,14 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read() #define VL_SIZEBITS_E (VL_EDATASIZE - 1) ///< Bit mask for bits in a quad /// Return mask for words with 1's where relevant bits are (0=all bits) +/// Arguments must not have side effects #define VL_MASK_I(nbits) (((nbits) & VL_SIZEBITS_I) ? ((1U << ((nbits) & VL_SIZEBITS_I)) - 1) : ~0) /// Return mask for quads with 1's where relevant bits are (0=all bits) +/// Arguments must not have side effects #define VL_MASK_Q(nbits) \ (((nbits) & VL_SIZEBITS_Q) ? ((1ULL << ((nbits) & VL_SIZEBITS_Q)) - 1ULL) : ~0ULL) /// Return mask for EData with 1's where relevant bits are (0=all bits) +/// Arguments must not have side effects #define VL_MASK_E(nbits) VL_MASK_I(nbits) #define VL_EUL(n) VL_UL(n) // Make constant number EData sized @@ -474,8 +474,12 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read() // #defines, to avoid requiring math.h on all compile runs #ifdef _MSC_VER -# define VL_TRUNC(n) (((n) < 0) ? std::ceil((n)) : std::floor((n))) -# define VL_ROUND(n) (((n) < 0) ? std::ceil((n)-0.5) : std::floor((n) + 0.5)) +static inline double VL_TRUNC(double n) { + return (n < 0) ? std::ceil(n) : std::floor(n); +} +static inline double VL_ROUND(double n) { + return (n < 0) ? std::ceil(n-0.5) : std::floor(n + 0.5); +} #else # define VL_TRUNC(n) std::trunc(n) # define VL_ROUND(n) std::round(n) diff --git a/nodist/clang_check_attributes b/nodist/clang_check_attributes index 36c5be05f..50409714a 100755 --- a/nodist/clang_check_attributes +++ b/nodist/clang_check_attributes @@ -33,6 +33,26 @@ def fully_qualified_name(node): return [node.displayname] if node.displayname else [] +# Returns True, if `class_node` contains node +# that matches `member` spelling +def check_class_member_exists(class_node, member): + for child in class_node.get_children(): + if member.spelling == child.spelling: + return True + return False + + +# Returns Base class (if found) of `class_node` +# that is of type `base_type` +def get_base_class(class_node, base_type): + for child in class_node.get_children(): + if child.kind is CursorKind.CXX_BASE_SPECIFIER: + base_class = child.type + if base_type.spelling == base_class.spelling: + return base_class + return None + + @dataclass class VlAnnotations: mt_start: bool = False @@ -251,7 +271,10 @@ class CallAnnotationsValidator: self._call_location: Optional[FunctionInfo] = None self._caller: Optional[FunctionInfo] = None self._level: int = 0 - self._constructor_context: int = 0 + self._constructor_context: list[clang.cindex.Cursor] = [] + + def is_constructor_context(self): + return len(self._constructor_context) > 0 def compile_and_analyze_file(self, source_file: str, compiler_args: list[str], @@ -341,7 +364,7 @@ class CallAnnotationsValidator: return None refn = find_object_ref(node) - if self._constructor_context and not refn: + if self.is_constructor_context() and not refn: # we are in constructor and no object reference means # we are calling local method. It is MT safe # only if this method is also only calling local methods or @@ -353,20 +376,44 @@ class CallAnnotationsValidator: elif refn and refn.kind == CursorKind.MEMBER_REF_EXPR and refn.referenced: refn = refn.referenced refna = VlAnnotations.from_nodes_list(refn.get_children()) - if refna.guarded or self._constructor_context: + if refna.guarded: is_mt_safe = True + if self.is_constructor_context() and refn.semantic_parent: + # we are in constructor, so calling local members is MT_SAFE, + # make sure object that we are calling is local to the constructor + constructor_class = self._constructor_context[ + -1].semantic_parent + if refn.semantic_parent.spelling == constructor_class.spelling: + if check_class_member_exists(constructor_class, refn): + is_mt_safe = True + else: + # check if this class inherits from some base class + base_class = get_base_class(constructor_class, + refn.semantic_parent) + if base_class: + if check_class_member_exists( + base_class.get_declaration(), refn): + is_mt_safe = True # variable elif refn and refn.kind == CursorKind.DECL_REF_EXPR and refn.referenced: - # This is probably a local or an argument. - # Calling methods on local pointers or references is MT-safe, - # but on argument pointers or references is not. - if "*" not in refn.type.spelling and "&" not in refn.type.spelling: - is_mt_safe = True - # local variable - if refn.referenced.kind == CursorKind.VAR_DECL: - is_mt_safe = True + if refn.get_definition(): + if refn.referenced.semantic_parent: + if refn.referenced.semantic_parent.kind in [ + CursorKind.FUNCTION_DECL, CursorKind.CXX_METHOD + ]: + # This is a local or an argument. + # Calling methods on local pointers or references is MT-safe, + # but on argument pointers or references is not. + if "*" not in refn.type.spelling and "&" not in refn.type.spelling: + is_mt_safe = True + # local variable + if refn.referenced.kind == CursorKind.VAR_DECL: + is_mt_safe = True + else: + # Global variable in different translation unit, unsafe + pass elif refn and refn.kind == CursorKind.CALL_EXPR: - if self._constructor_context: + if self.is_constructor_context(): # call to local function from constructor context # safe if this function also calling local methods or # MT-safe methods @@ -442,18 +489,18 @@ class CallAnnotationsValidator: # Constructors are OK in MT-safe context # only if they call local methods or MT-safe functions. - if ctx.is_mt_safe_context() or self._constructor_context: - self._constructor_context += 1 + if ctx.is_mt_safe_context() or self.is_constructor_context(): + self._constructor_context.append(refd) self.iterate_children(refd.get_children(), self.dispatch_node_inside_definition) - self._constructor_context -= 1 + self._constructor_context.pop() # stable tree context if ctx.is_stabe_tree_context(): - self._constructor_context += 1 + self._constructor_context.append(refd) self.iterate_children(refd.get_children(), self.dispatch_node_inside_definition) - self._constructor_context -= 1 + self._constructor_context.pop() # pure context if ctx.is_pure_context(): diff --git a/src/V3Active.cpp b/src/V3Active.cpp index 87194907f..fc40166c0 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -183,7 +183,7 @@ public: << " (not all control paths of combinational always assign a value)\n" << nodep->warnMore() << "... Suggest use of always_latch for intentional latches"); - if (dumpGraph() >= 9) dumpDotFilePrefixed("latch_" + vrp->name()); + if (dumpGraphLevel() >= 9) dumpDotFilePrefixed("latch_" + vrp->name()); } vertp->user(false); // Clear again (see above) vrp->varp()->isLatched(latch_detected); @@ -320,7 +320,8 @@ private: // VISITORS void visit(AstVarRef* nodep) override { const AstVar* const varp = nodep->varp(); - if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()) { + if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx() + && !varp->isFuncLocalSticky()) { m_graph.addAssignment(nodep); } } @@ -333,6 +334,8 @@ private: m_graph.addPathVertex(branchp, "ELSE"); iterateAndNextConstNull(nodep->elsesp()); m_graph.currentp(parentp); + } else { + iterateChildrenConst(nodep); } } //-------------------- @@ -652,5 +655,5 @@ public: void V3Active::activeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { ActiveVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("active", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("active", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3ActiveTop.cpp b/src/V3ActiveTop.cpp index 29d9df091..16cfe6e95 100644 --- a/src/V3ActiveTop.cpp +++ b/src/V3ActiveTop.cpp @@ -145,5 +145,5 @@ public: void V3ActiveTop::activeTopAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { ActiveTopVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("activetop", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("activetop", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 15969106a..6135eabde 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -20,6 +20,7 @@ #include "V3Assert.h" #include "V3Ast.h" +#include "V3Error.h" #include "V3Global.h" #include "V3Stats.h" @@ -544,5 +545,5 @@ public: void V3Assert::assertAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { AssertVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("assert", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("assert", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Assert.h b/src/V3Assert.h index 0715dd405..dcf26756a 100644 --- a/src/V3Assert.h +++ b/src/V3Assert.h @@ -21,7 +21,6 @@ #include "verilatedos.h" #include "V3Ast.h" -#include "V3Error.h" //============================================================================ diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index 51a12ad14..341204593 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -496,5 +496,5 @@ public: void V3AssertPre::assertPreAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { AssertPreVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("assertpre", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("assertpre", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index dcc43148c..0be9e89a5 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1217,7 +1217,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo if (logsp->fail()) v3fatal("Can't write " << filename); *logsp << "Verilator Tree Dump (format 0x3900) from to \n"; - if (editCountGbl() == editCountLast() && ::dumpTree() < 9) { + if (editCountGbl() == editCountLast() && ::dumpTreeLevel() < 9) { *logsp << '\n'; *logsp << "No changes since last dump!\n"; } else { @@ -1227,7 +1227,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo } } if (doDump && v3Global.opt.debugEmitV()) V3EmitV::debugEmitV(filename + ".v"); - if (doCheck && (v3Global.opt.debugCheck() || ::dumpTree())) { + if (doCheck && (v3Global.opt.debugCheck() || ::dumpTreeLevel())) { // Error check checkTree(); // Broken isn't part of check tree because it can munge iterp's diff --git a/src/V3Ast.h b/src/V3Ast.h index 6a286bde8..b871314c0 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -461,6 +461,7 @@ public: TRIGGER_SCHEDULER, DYNAMIC_TRIGGER_SCHEDULER, FORK_SYNC, + PROCESS_REFERENCE, // Unsigned and two state; fundamental types UINT32, UINT64, @@ -493,6 +494,7 @@ public: "VlTriggerScheduler", "VlDynamicTriggerScheduler", "VlFork", + "VlProcessRef", "IData", "QData", "LOGIC_IMPLICIT", @@ -500,13 +502,20 @@ public: return names[m_e]; } const char* dpiType() const { - static const char* const names[] - = {"%E-unk", "svBit", "char", "void*", "char", - "int", "%E-integer", "svLogic", "long long", "double", - "short", "%E-time", "const char*", "%E-untyped", "dpiScope", - "const char*", "%E-mtaskstate", "%E-triggervec", "%E-dly-sched", "%E-trig-sched", - "%E-dyn-sched", "%E-fork", "IData", "QData", "%E-logic-implct", - " MAX"}; + static const char* const names[] = {"%E-unk", "svBit", + "char", "void*", + "char", "int", + "%E-integer", "svLogic", + "long long", "double", + "short", "%E-time", + "const char*", "%E-untyped", + "dpiScope", "const char*", + "%E-mtaskstate", "%E-triggervec", + "%E-dly-sched", "%E-trig-sched", + "%E-dyn-sched", "%E-fork", + "%E-proc-ref", "IData", + "QData", "%E-logic-implct", + " MAX"}; return names[m_e]; } static void selfTest() { @@ -545,6 +554,7 @@ public: case TRIGGER_SCHEDULER: return 0; // opaque case DYNAMIC_TRIGGER_SCHEDULER: return 0; // opaque case FORK_SYNC: return 0; // opaque + case PROCESS_REFERENCE: return 0; // opaque case UINT32: return 32; case UINT64: return 64; default: return 0; @@ -584,7 +594,7 @@ public: return (m_e == EVENT || m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR || m_e == MTASKSTATE || m_e == TRIGGERVEC || m_e == DELAY_SCHEDULER || m_e == TRIGGER_SCHEDULER || m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC - || m_e == DOUBLE || m_e == UNTYPED); + || m_e == PROCESS_REFERENCE || m_e == DOUBLE || m_e == UNTYPED); } bool isDouble() const VL_MT_SAFE { return m_e == DOUBLE; } bool isEvent() const { return m_e == EVENT; } @@ -1817,6 +1827,7 @@ public: // ACCESSORS for specific types // Alas these can't be virtual or they break when passed a nullptr + inline bool isClassHandleValue() const; inline bool isZero() const; inline bool isOne() const; inline bool isNeqZero() const; @@ -1915,8 +1926,6 @@ public: // Iterate and insert - assumes tree format virtual void addNextStmt(AstNode* newp, AstNode* belowp); // When calling, "this" is second argument - virtual void addBeforeStmt(AstNode* newp, - AstNode* belowp); // When calling, "this" is second argument // METHODS - Iterate on a tree // Clone or return nullptr if nullptr diff --git a/src/V3AstInlines.h b/src/V3AstInlines.h index 9ca54f137..d2325c14b 100644 --- a/src/V3AstInlines.h +++ b/src/V3AstInlines.h @@ -41,6 +41,10 @@ bool AstNode::isString() const VL_MT_STABLE { } bool AstNode::isSigned() const VL_MT_STABLE { return dtypep() && dtypep()->isSigned(); } +bool AstNode::isClassHandleValue() const { + return (VN_IS(this, Const) && VN_AS(this, Const)->num().isNull()) + || VN_IS(dtypep(), ClassRefDType); +} bool AstNode::isZero() const { return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqZero()); } diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index 10d90c33f..a74eeec6f 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -438,6 +438,7 @@ public: bool isEvent() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::EVENT; } bool isTriggerVec() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::TRIGGERVEC; } bool isForkSync() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::FORK_SYNC; } + bool isProcessRef() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::PROCESS_REFERENCE; } bool isDelayScheduler() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::DELAY_SCHEDULER; } @@ -887,6 +888,7 @@ public: if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep(); } AstNodeDType* getChildDTypep() const override { return childDTypep(); } + AstNodeUOrStructDType* getChildStructp() const; AstNodeDType* subDTypep() const override VL_MT_STABLE { return m_refDTypep ? m_refDTypep : childDTypep(); } diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 635b9edb4..3ccd63be5 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -975,6 +975,20 @@ public: , m_num(this, 1, on) { dtypeSetBit(); } + class All0 {}; + AstConst(FileLine* fl, All0) + : ASTGEN_SUPER_Const(fl) + , m_num(this, "'0") { + initWithNumber(); + fl->warnOff(V3ErrorCode::NEWERSTD, true); + } + class All1 {}; + AstConst(FileLine* fl, All1) + : ASTGEN_SUPER_Const(fl) + , m_num(this, "'1") { + initWithNumber(); + fl->warnOff(V3ErrorCode::NEWERSTD, true); + } class Null {}; AstConst(FileLine* fl, Null) : ASTGEN_SUPER_Const(fl) @@ -1092,7 +1106,7 @@ public: // METHODS string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } - bool cleanOut() const override { return false; } + bool cleanOut() const override { return true; } bool same(const AstNode*) const override { return true; } }; class AstFError final : public AstNodeExpr { @@ -1436,7 +1450,7 @@ public: void name(const string& name) override { m_name = name; } string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } - bool cleanOut() const override { return false; } + bool cleanOut() const override { return true; } bool same(const AstNode* samep) const override { return true; } // dtype comparison does it int instrCount() const override { return widthInstrs(); } AstVar* varp() const { return m_varp; } @@ -2108,14 +2122,14 @@ class AstWith final : public AstNodeExpr { // Children: expression (equation establishing the with) // @astgen op1 := indexArgRefp : AstLambdaArgRef // @astgen op2 := valueArgRefp : AstLambdaArgRef - // @astgen op3 := exprp : AstNodeExpr + // @astgen op3 := exprp : List[AstNode] public: AstWith(FileLine* fl, AstLambdaArgRef* indexArgRefp, AstLambdaArgRef* valueArgRefp, AstNodeExpr* exprp) : ASTGEN_SUPER_With(fl) { this->indexArgRefp(indexArgRefp); this->valueArgRefp(valueArgRefp); - this->exprp(exprp); + this->addExprp(exprp); } ASTGEN_MEMBERS_AstWith; bool same(const AstNode* /*samep*/) const override { return true; } diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index fc998872b..90e475d15 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -86,6 +86,7 @@ private: bool m_recursive : 1; // Recursive or part of recursion bool m_underGenerate : 1; // Under generate (for warning) bool m_virtual : 1; // Virtual method in class + bool m_fromStd : 1; // Part of std VLifetime m_lifetime; // Lifetime protected: AstNodeFTask(VNType t, FileLine* fl, const string& name, AstNode* stmtsp) @@ -110,7 +111,8 @@ protected: , m_pureVirtual{false} , m_recursive{false} , m_underGenerate{false} - , m_virtual{false} { + , m_virtual{false} + , m_fromStd{false} { addStmtsp(stmtsp); cname(name); // Might be overridden by dpi import/export } @@ -170,6 +172,8 @@ public: bool underGenerate() const { return m_underGenerate; } void isVirtual(bool flag) { m_virtual = flag; } bool isVirtual() const { return m_virtual; } + void isFromStd(bool flag) { m_fromStd = flag; } + bool isFromStd() const { return m_fromStd; } void lifetime(const VLifetime& flag) { m_lifetime = flag; } VLifetime lifetime() const { return m_lifetime; } bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } @@ -272,10 +276,13 @@ public: class AstNodeProcedure VL_NOT_FINAL : public AstNode { // IEEE procedure: initial, final, always // @astgen op2 := stmtsp : List[AstNode] // Note: op1 is used in some sub-types only - bool m_suspendable = false; // Is suspendable by a Delay, EventControl, etc. + bool m_suspendable : 1; // Is suspendable by a Delay, EventControl, etc. + bool m_needProcess : 1; // Implements part of a process that allocates std::process protected: AstNodeProcedure(VNType t, FileLine* fl, AstNode* stmtsp) : AstNode{t, fl} { + m_needProcess = false; + m_suspendable = false; addStmtsp(stmtsp); } @@ -286,6 +293,8 @@ public: bool isJustOneBodyStmt() const { return stmtsp() && !stmtsp()->nextp(); } bool isSuspendable() const { return m_suspendable; } void setSuspendable() { m_suspendable = true; } + bool needProcess() const { return m_needProcess; } + void setNeedProcess() { m_needProcess = true; } }; class AstNodeRange VL_NOT_FINAL : public AstNode { // A range, sized or unsized @@ -308,8 +317,6 @@ public: // METHODS void addNextStmt(AstNode* newp, AstNode* belowp) override; // Stop statement searchback here - void addBeforeStmt(AstNode* newp, - AstNode* belowp) override; // Stop statement searchback here void dump(std::ostream& str = std::cout) const override; }; class AstNodeAssign VL_NOT_FINAL : public AstNodeStmt { @@ -578,6 +585,7 @@ private: bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user) bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code bool m_dpiTraceInit : 1; // DPI trace_init + bool m_needProcess : 1; // Implements part of a process that allocates std::process public: AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "") : ASTGEN_SUPER_CFunc(fl) { @@ -597,6 +605,7 @@ public: m_isLoose = false; m_isInline = false; m_isVirtual = false; + m_needProcess = false; m_entryPoint = false; m_pure = false; m_dpiContext = false; @@ -665,6 +674,8 @@ public: void isInline(bool flag) { m_isInline = flag; } bool isVirtual() const { return m_isVirtual; } void isVirtual(bool flag) { m_isVirtual = flag; } + bool needProcess() const { return m_needProcess; } + void setNeedProcess() { m_needProcess = true; } bool entryPoint() const { return m_entryPoint; } void entryPoint(bool flag) { m_entryPoint = flag; } bool pure() const { return m_pure; } @@ -809,6 +820,8 @@ class AstClassExtends final : public AstNode { // @astgen op1 := childDTypep : Optional[AstNodeDType] // @astgen op2 := classOrPkgsp : Optional[AstNode] const bool m_isImplements = false; // class implements + bool m_parameterized = false; // has parameters in its statement + public: AstClassExtends(FileLine* fl, AstNode* classOrPkgsp, bool isImplements) : ASTGEN_SUPER_ClassExtends(fl) @@ -819,8 +832,12 @@ public: void dump(std::ostream& str) const override; bool hasDType() const override { return true; } string verilogKwd() const override { return isImplements() ? "implements" : "extends"; } - AstClass* classp() const; // Class being extended (after link) + // Class being extended (after link and instantiation if needed) + AstClass* classOrNullp() const; + AstClass* classp() const; // Like above, but throws error if nulll bool isImplements() const { return m_isImplements; } + void parameterized(bool flag) { m_parameterized = flag; } + bool parameterized() const { return m_parameterized; } }; class AstClocking final : public AstNode { // Parents: MODULE @@ -1359,6 +1376,7 @@ public: 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; + bool same(const AstNode* samep) const override; string nameDotless() const; string nameVlSym() const { return string{"vlSymsp->"} + nameDotless(); } AstNodeModule* modp() const { return m_modp; } @@ -1628,6 +1646,7 @@ class AstVar final : public AstNode { bool m_usedLoopIdx : 1; // Variable subject of for unrolling bool m_usedVirtIface : 1; // Signal used through a virtual interface bool m_funcLocal : 1; // Local variable for a function + bool m_funcLocalSticky : 1; // As m_funcLocal but remains set if var is moved to a static bool m_funcReturn : 1; // Return variable for a function bool m_attrScBv : 1; // User force bit vector attribute bool m_attrIsolateAssign : 1; // User isolate_assignments attribute @@ -1671,6 +1690,7 @@ class AstVar final : public AstNode { m_sigUserRdPublic = false; m_sigUserRWPublic = false; m_funcLocal = false; + m_funcLocalSticky = false; m_funcReturn = false; m_attrScBv = false; m_attrIsolateAssign = false; @@ -1758,6 +1778,7 @@ public: } ASTGEN_MEMBERS_AstVar; void dump(std::ostream& str) const override; + bool same(const AstNode* samep) const override; 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; } @@ -1833,7 +1854,10 @@ public: void isContinuously(bool flag) { m_isContinuously = flag; } void isStatic(bool flag) { m_isStatic = flag; } void isIfaceParent(bool flag) { m_isIfaceParent = flag; } - void funcLocal(bool flag) { m_funcLocal = flag; } + void funcLocal(bool flag) { + m_funcLocal = flag; + if (flag) m_funcLocalSticky = true; + } void funcReturn(bool flag) { m_funcReturn = flag; } void hasStrengthAssignment(bool flag) { m_hasStrengthAssignment = flag; } bool hasStrengthAssignment() { return m_hasStrengthAssignment; } @@ -1915,6 +1939,7 @@ public: bool isStatic() const VL_MT_SAFE { return m_isStatic; } bool isLatched() const { return m_isLatched; } bool isFuncLocal() const { return m_funcLocal; } + bool isFuncLocalSticky() const { return m_funcLocalSticky; } bool isFuncReturn() const { return m_funcReturn; } bool isPullup() const { return m_isPullup; } bool isPulldown() const { return m_isPulldown; } @@ -2000,6 +2025,7 @@ public: bool maybePointedTo() const override { return true; } string name() const override VL_MT_STABLE { return scopep()->name() + "->" + varp()->name(); } void dump(std::ostream& str) const override; + bool same(const AstNode* samep) const override; bool hasDType() const override { return true; } 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 @@ -3240,8 +3266,6 @@ public: int instrCount() const override { return INSTR_COUNT_BRANCH; } bool same(const AstNode* /*samep*/) const override { return true; } // Stop statement searchback here - void addBeforeStmt(AstNode* newp, AstNode* belowp) override; - // Stop statement searchback here void addNextStmt(AstNode* newp, AstNode* belowp) override; bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } }; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 6c300058f..45a99e7ce 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -391,8 +391,10 @@ string AstVar::verilogKwd() const { return "wreal"; } else if (varType() == VVarType::IFACEREF) { return "ifaceref"; - } else { + } else if (dtypep()) { return dtypep()->name(); + } else { + return "UNKNOWN"; } } @@ -782,6 +784,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const VL_M info.m_type = "VlDynamicTriggerScheduler"; } else if (bdtypep->isForkSync()) { info.m_type = "VlForkSync"; + } else if (bdtypep->isProcessRef()) { + info.m_type = "VlProcessRef"; } else if (bdtypep->isEvent()) { info.m_type = "VlEvent"; } else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width @@ -1269,44 +1273,17 @@ AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) { //====================================================================== // Special walking tree inserters -void AstNode::addBeforeStmt(AstNode* newp, AstNode*) { - UASSERT_OBJ(backp(), newp, "Can't find current statement to addBeforeStmt"); - // Look up; virtual call will find where to put it - this->backp()->addBeforeStmt(newp, this); -} void AstNode::addNextStmt(AstNode* newp, AstNode*) { UASSERT_OBJ(backp(), newp, "Can't find current statement to addNextStmt"); // Look up; virtual call will find where to put it this->backp()->addNextStmt(newp, this); } -void AstNodeStmt::addBeforeStmt(AstNode* newp, AstNode*) { - // Insert newp before current node - this->addHereThisAsNext(newp); -} void AstNodeStmt::addNextStmt(AstNode* newp, AstNode*) { // Insert newp after current node this->addNextHere(newp); } -void AstWhile::addBeforeStmt(AstNode* newp, AstNode* belowp) { - // Special, as statements need to be put in different places - // Belowp is how we came to recurse up to this point - // Preconditions insert first just before themselves (the normal rule - // for other statement types) - if (belowp == precondsp()) { - // Must have been first statement in precondsp list, so newp is new first statement - belowp->addHereThisAsNext(newp); - } else if (belowp == condp()) { - // Goes before condition, IE in preconditions - addPrecondsp(newp); - } else if (belowp == stmtsp()) { - // Was first statement in body, so new front - belowp->addHereThisAsNext(newp); - } else { - belowp->v3fatalSrc("Doesn't look like this was really under the while"); - } -} void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) { // Special, as statements need to be put in different places // Belowp is how we came to recurse up to this point @@ -1377,6 +1354,7 @@ void AstNode::dump(std::ostream& str) const { void AstNodeProcedure::dump(std::ostream& str) const { this->AstNode::dump(str); if (isSuspendable()) str << " [SUSP]"; + if (needProcess()) str << " [NPRC]"; } void AstAlways::dump(std::ostream& str) const { @@ -1499,13 +1477,20 @@ void AstClassExtends::dump(std::ostream& str) const { this->AstNode::dump(str); if (isImplements()) str << " [IMPLEMENTS]"; } -AstClass* AstClassExtends::classp() const { - const AstClassRefDType* refp = VN_CAST(dtypep(), ClassRefDType); - if (VL_UNLIKELY(!refp)) { // LinkDot uses this for 'super.' - refp = VN_AS(childDTypep(), ClassRefDType); +AstClass* AstClassExtends::classOrNullp() const { + const AstNodeDType* const dtp = dtypep() ? dtypep() : childDTypep(); + const AstClassRefDType* const refp = VN_CAST(dtp, ClassRefDType); + if (refp && !refp->paramsp()) { + // Class already resolved + return refp->classp(); + } else { + return nullptr; } - UASSERT_OBJ(refp, this, "class extends non-ref"); - return refp->classp(); +} +AstClass* AstClassExtends::classp() const { + AstClass* const clsp = classOrNullp(); + UASSERT_OBJ(clsp, this, "Extended class is unresolved"); + return clsp; } void AstClassRefDType::dump(std::ostream& str) const { this->AstNodeDType::dump(str); @@ -1669,10 +1654,19 @@ void AstLogOr::dump(std::ostream& str) const { this->AstNodeExpr::dump(str); if (sideEffect()) str << " [SIDE]"; } + void AstMemberDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str << "member"; } +AstNodeUOrStructDType* AstMemberDType::getChildStructp() const { + AstNodeDType* subdtp = skipRefp(); + while (AstNodeArrayDType* const asubdtp = VN_CAST(subdtp, NodeArrayDType)) { + subdtp = asubdtp->subDTypep(); + } + return VN_CAST(subdtp, NodeUOrStructDType); // Maybe nullptr +} + void AstMemberSel::dump(std::ostream& str) const { this->AstNodeExpr::dump(str); str << " -> "; @@ -1829,6 +1823,7 @@ void AstNodeUOrStructDType::dump(std::ostream& str) const { this->AstNodeDType::dump(str); if (packed()) str << " [PACKED]"; if (isFourstate()) str << " [4STATE]"; + if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep()); } void AstNodeDType::dump(std::ostream& str) const { this->AstNode::dump(str); @@ -2071,6 +2066,10 @@ void AstVarScope::dump(std::ostream& str) const { str << " ->UNLINKED"; } } +bool AstVarScope::same(const AstNode* samep) const { + const AstVarScope* const asamep = static_cast(samep); + return varp()->same(asamep->varp()) && scopep()->same(asamep->scopep()); +} void AstNodeVarRef::dump(std::ostream& str) const { this->AstNodeExpr::dump(str); if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep()); @@ -2134,12 +2133,23 @@ void AstVar::dump(std::ostream& str) const { if (!lifetime().isNone()) str << " [" << lifetime().ascii() << "] "; str << " " << varType(); } +bool AstVar::same(const AstNode* samep) const { + const AstVar* const asamep = static_cast(samep); + return name() == asamep->name() && varType() == asamep->varType(); +} void AstScope::dump(std::ostream& str) const { this->AstNode::dump(str); str << " [abovep=" << reinterpret_cast(aboveScopep()) << "]"; str << " [cellp=" << reinterpret_cast(aboveCellp()) << "]"; str << " [modp=" << reinterpret_cast(modp()) << "]"; } +bool AstScope::same(const AstNode* samep) const { + const AstScope* const asamep = static_cast(samep); + return name() == asamep->name() + && ((!aboveScopep() && !asamep->aboveScopep()) + || (aboveScopep() && asamep->aboveScopep() + && aboveScopep()->name() == asamep->aboveScopep()->name())); +} void AstScopeName::dump(std::ostream& str) const { this->AstNodeExpr::dump(str); if (dpiExport()) str << " [DPIEX]"; @@ -2302,6 +2312,7 @@ void AstCFunc::dump(std::ostream& str) const { if (isDestructor()) str << " [DTOR]"; if (isVirtual()) str << " [VIRT]"; if (isCoroutine()) str << " [CORO]"; + if (needProcess()) str << " [NPRC]"; } const char* AstCAwait::broken() const { BROKEN_RTN(m_sensesp && !m_sensesp->brokeExists()); diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index b7707754d..d18f4be1d 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -40,6 +40,39 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### +class RenameStaticVisitor final : public VNVisitor { +private: + // STATE + const std::set& m_staticFuncVarsr; // Static variables from m_ftaskp + AstNodeFTask* const m_ftaskp; // Current function/task + + // VISITORS + void visit(AstVarRef* nodep) override { + const auto it = m_staticFuncVarsr.find(nodep->varp()); + if (it != m_staticFuncVarsr.end()) nodep->name((*it)->name()); + iterateChildren(nodep); + } + + void visit(AstInitialStatic* nodep) override { + iterateChildren(nodep); + nodep->unlinkFrBack(); + m_ftaskp->addHereThisAsNext(nodep); + } + + void visit(AstNode* nodep) override { iterateChildren(nodep); } + +public: + // CONSTRUCTORS + RenameStaticVisitor(std::set& staticFuncVars, AstNodeFTask* ftaskp, AstNode* nodep) + : m_staticFuncVarsr(staticFuncVars) + , m_ftaskp(ftaskp) { + iterateChildren(nodep); + } + ~RenameStaticVisitor() override = default; +}; + +//###################################################################### + class BeginState final { private: // NODE STATE @@ -78,6 +111,7 @@ private: string dot(const string& a, const string& b) { if (a == "") return b; + if (b == "") return a; return a + "__DOT__" + b; } @@ -123,25 +157,6 @@ private: } } - void renameAndStaticsRecurse(AstNode* const nodep) { - // Rename references and move InitialStatic items - if (AstVarRef* const varrefp = VN_CAST(nodep, VarRef)) { - const auto it = m_staticFuncVars.find(varrefp->varp()); - if (it != m_staticFuncVars.end()) varrefp->name((*it)->name()); - } - - if (nodep->op1p()) renameAndStaticsRecurse(nodep->op1p()); - if (nodep->op2p()) renameAndStaticsRecurse(nodep->op2p()); - if (nodep->op3p()) renameAndStaticsRecurse(nodep->op3p()); - if (nodep->op4p()) renameAndStaticsRecurse(nodep->op4p()); - if (nodep->nextp()) renameAndStaticsRecurse(nodep->nextp()); - - if (VN_IS(nodep, InitialStatic)) { - nodep->unlinkFrBack(); - m_ftaskp->addHereThisAsNext(nodep); - } - } - // VISITORS void visit(AstFork* nodep) override { // Keep begins in forks to group their statements together @@ -197,7 +212,7 @@ private: m_ftaskp = nodep; m_liftedp = nullptr; iterateChildren(nodep); - renameAndStaticsRecurse(nodep); + RenameStaticVisitor{m_staticFuncVars, m_ftaskp, nodep}; if (m_liftedp) { // Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced if (AstNode* const stmtsp = nodep->stmtsp()) { @@ -246,7 +261,8 @@ private: void visit(AstVar* nodep) override { // If static variable, move it outside a function. if (nodep->lifetime().isStatic() && m_ftaskp) { - const std::string newName = m_ftaskp->name() + "__Vstatic__" + nodep->name(); + const std::string newName + = m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name()); nodep->name(newName); nodep->unlinkFrBack(); m_ftaskp->addHereThisAsNext(nodep); @@ -392,5 +408,5 @@ void V3Begin::debeginAll(AstNetlist* nodep) { { BeginVisitor{nodep, &state}; } if (state.anyFuncInBegin()) { BeginRelinkVisitor{nodep, &state}; } } // Destruct before checking - V3Global::dumpCheckGlobalTree("begin", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("begin", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Branch.cpp b/src/V3Branch.cpp index 019f442c5..e84e041b5 100644 --- a/src/V3Branch.cpp +++ b/src/V3Branch.cpp @@ -45,10 +45,12 @@ private: // AstFTask::user1() -> int. Number of references const VNUser1InUse m_inuser1; - // STATE + // STATE - across all visitors + std::vector m_cfuncsp; // List of all tasks + + // STATE - for current visit position (use VL_RESTORER) int m_likely = false; // Excuses for branch likely taken int m_unlikely = false; // Excuses for branch likely not taken - std::vector m_cfuncsp; // List of all tasks // METHODS diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index a079006ec..3c1265c67 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -257,5 +257,5 @@ void V3CCtors::cctorsAll() { UINFO(2, __FUNCTION__ << ": " << endl); evalAsserts(); { CCtorsVisitor{v3Global.rootp()}; } - V3Global::dumpCheckGlobalTree("cctors", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("cctors", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3CUse.cpp b/src/V3CUse.cpp index 9085ba8d9..0bbab0886 100644 --- a/src/V3CUse.cpp +++ b/src/V3CUse.cpp @@ -45,12 +45,16 @@ class CUseVisitor final : public VNVisitor { const VNUser1InUse m_inuser1; // MEMBERS - bool m_impOnly = false; // In details needed only for implementation AstNodeModule* const m_modp; // Current module std::set> m_didUse; // What we already used + bool m_dtypesImplOnly = false; // METHODS void addNewUse(AstNode* nodep, VUseType useType, const string& name) { + if (m_dtypesImplOnly + && (useType == VUseType::INT_INCLUDE || useType == VUseType::INT_FWD_CLASS)) + return; + if (m_didUse.emplace(useType, name).second) { AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name}; m_modp->addStmtsp(newp); @@ -61,12 +65,29 @@ class CUseVisitor final : public VNVisitor { // VISITORS void visit(AstClassRefDType* nodep) override { if (nodep->user1SetOnce()) return; // Process once - if (!m_impOnly) addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name()); - // Need to include extends() when we implement, but no need for pointers to know - VL_RESTORER(m_impOnly); + addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name()); + } + void visit(AstCFunc* nodep) override { + if (nodep->user1SetOnce()) return; // Process once + iterateAndNextNull(nodep->argsp()); + { - m_impOnly = true; - iterateChildren(nodep->classp()); // This also gets all extend classes + VL_RESTORER(m_dtypesImplOnly); + m_dtypesImplOnly = true; + + iterateAndNextNull(nodep->initsp()); + iterateAndNextNull(nodep->stmtsp()); + iterateAndNextNull(nodep->finalsp()); + } + } + void visit(AstCReturn* nodep) override { + if (nodep->user1SetOnce()) return; // Process once + if (m_dtypesImplOnly) { + for (AstNode* exprp = nodep->op1p(); exprp; exprp = exprp->nextp()) { + if (exprp->dtypep()) iterate(exprp->dtypep()); + } + } else { + iterateChildren(nodep); } } void visit(AstNodeDType* nodep) override { @@ -79,6 +100,8 @@ class CUseVisitor final : public VNVisitor { if (stypep && stypep->classOrPackagep()) { addNewUse(nodep, VUseType::INT_INCLUDE, stypep->classOrPackagep()->name()); iterateChildren(stypep); + } else if (AstClassRefDType* const classp = VN_CAST(nodep->skipRefp(), ClassRefDType)) { + addNewUse(nodep, VUseType::INT_FWD_CLASS, classp->name()); } } void visit(AstNode* nodep) override { @@ -115,5 +138,5 @@ void V3CUse::cUseAll() { // for each output file and put under that CUseVisitor{modp}; } - V3Global::dumpCheckGlobalTree("cuse", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("cuse", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Case.cpp b/src/V3Case.cpp index 4ae5969ad..c8cd473c2 100644 --- a/src/V3Case.cpp +++ b/src/V3Case.cpp @@ -566,7 +566,7 @@ public: void V3Case::caseAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { CaseVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("case", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("case", 0, dumpTreeLevel() >= 3); } void V3Case::caseLint(AstNodeCase* nodep) { UINFO(4, __FUNCTION__ << ": " << endl); diff --git a/src/V3Cast.cpp b/src/V3Cast.cpp index 4f253beed..eaecd7320 100644 --- a/src/V3Cast.cpp +++ b/src/V3Cast.cpp @@ -122,6 +122,31 @@ private: if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp()); if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp()); } + void visit(AstNodeCond* nodep) override { + // All class types are castable to each other. If they are of different types, + // a compilation error will be thrown, so an explicit cast is required. Types were + // already checked by V3Width and dtypep of a condition operator is a type of their + // common base class, so both classes can be safetly casted. + const AstClassRefDType* const thenClassDtypep + = VN_CAST(nodep->thenp()->dtypep(), ClassRefDType); + const AstClassRefDType* const elseClassDtypep + = VN_CAST(nodep->elsep()->dtypep(), ClassRefDType); + const bool castRequired = thenClassDtypep && elseClassDtypep + && (thenClassDtypep->classp() != elseClassDtypep->classp()); + if (castRequired) { + const AstClass* const commonBaseClassp + = VN_AS(nodep->dtypep(), ClassRefDType)->classp(); + if (thenClassDtypep->classp() != commonBaseClassp) { + AstNodeExpr* thenp = nodep->thenp()->unlinkFrBack(); + nodep->thenp(new AstCCast{thenp->fileline(), thenp, nodep}); + } + if (elseClassDtypep->classp() != commonBaseClassp) { + AstNodeExpr* elsep = nodep->elsep()->unlinkFrBack(); + nodep->elsep(new AstCCast{elsep->fileline(), elsep, nodep}); + } + } + visit(static_cast(nodep)); + } void visit(AstNodeTriop* nodep) override { iterateChildren(nodep); nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1() | nodep->thsp()->user1()); @@ -161,7 +186,8 @@ private: && !VN_IS(backp, NodeCCall) && !VN_IS(backp, CMethodHard) && !VN_IS(backp, SFormatF) && !VN_IS(backp, ArraySel) && !VN_IS(backp, StructSel) && !VN_IS(backp, RedXor) && (nodep->varp()->basicp() && !nodep->varp()->basicp()->isTriggerVec() - && !nodep->varp()->basicp()->isForkSync()) + && !nodep->varp()->basicp()->isForkSync() + && !nodep->varp()->basicp()->isProcessRef()) && backp->width() && castSize(nodep) != castSize(nodep->varp())) { // Cast vars to IData first, else below has upper bits wrongly set // CData x=3; out = (QData)(x<<30); @@ -208,5 +234,5 @@ public: void V3Cast::castAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { CastVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("cast", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("cast", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Class.cpp b/src/V3Class.cpp index 3c726a3a6..3653b8b2d 100644 --- a/src/V3Class.cpp +++ b/src/V3Class.cpp @@ -27,6 +27,7 @@ #include "V3Ast.h" #include "V3Global.h" +#include "V3UniqueNames.h" VL_DEFINE_DEBUG_FUNCTIONS; @@ -41,6 +42,7 @@ private: // MEMBERS string m_prefix; // String prefix to add to name based on hier + V3UniqueNames m_names; // For unique naming of structs and unions AstNodeModule* m_modp = nullptr; // Current module AstNodeModule* m_classPackagep = nullptr; // Package moving into const AstScope* m_classScopep = nullptr; // Package moving scopes into @@ -62,6 +64,7 @@ private: AstClassPackage* const packagep = new AstClassPackage{nodep->fileline(), nodep->origName()}; packagep->name(nodep->name() + "__Vclpkg"); + nodep->editCountInc(); nodep->classOrPackagep(packagep); packagep->classp(nodep); v3Global.rootp()->addModulesp(packagep); @@ -177,14 +180,15 @@ private: } void setStructModulep(AstNodeUOrStructDType* const dtypep) { - // Give it a pointer to its package and a final name + // Give struct a pointer to its package and a final name + dtypep->editCountInc(); dtypep->classOrPackagep(m_classPackagep ? m_classPackagep : m_modp); - dtypep->name(dtypep->name() + (VN_IS(dtypep, UnionDType) ? "__union" : "__struct") - + cvtToStr(dtypep->uniqueNum())); + dtypep->name( + m_names.get(dtypep->name() + (VN_IS(dtypep, UnionDType) ? "__union" : "__struct"))); for (const AstMemberDType* itemp = dtypep->membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) { - AstNodeUOrStructDType* const subp = VN_CAST(itemp->skipRefp(), NodeUOrStructDType); + AstNodeUOrStructDType* const subp = itemp->getChildStructp(); // Recurse only into anonymous unpacked structs inside this definition, // other unpacked structs will be reached from another typedefs if (subp && !subp->packed() && subp->name().empty()) setStructModulep(subp); @@ -244,5 +248,5 @@ public: void V3Class::classAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { ClassVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("class", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("class", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index 8d0718700..f69d0e153 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -234,6 +234,11 @@ private: operandTriop(nodep); setClean(nodep, nodep->cleanOut()); } + void visit(AstStructSel* nodep) override { + iterateChildren(nodep); + AstStructDType* dtypep = VN_CAST(nodep->dtypep()->skipRefp(), StructDType); + setClean(nodep, dtypep && !dtypep->packed()); + } void visit(AstUCFunc* nodep) override { iterateChildren(nodep); computeCppWidth(nodep); @@ -298,7 +303,11 @@ private: } void visit(AstWith* nodep) override { iterateChildren(nodep); - ensureCleanAndNext(nodep->exprp()); + setClean(nodep, true); + } + void visit(AstCReturn* nodep) override { + iterateChildren(nodep); + ensureClean(nodep->lhsp()); setClean(nodep, true); } void visit(AstIntfRef* nodep) override { @@ -325,5 +334,5 @@ public: void V3Clean::cleanAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { CleanVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("clean", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("clean", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index bf374d2a3..c72730ffb 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -224,5 +224,5 @@ public: void V3Clock::clockAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { ClockVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("clock", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("clock", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Combine.cpp b/src/V3Combine.cpp index 44e9b2954..a4d425229 100644 --- a/src/V3Combine.cpp +++ b/src/V3Combine.cpp @@ -238,5 +238,5 @@ public: void V3Combine::combineAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); CombineVisitor::apply(nodep); - V3Global::dumpCheckGlobalTree("combine", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("combine", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Common.cpp b/src/V3Common.cpp index b5f5a4c8c..4dc587a10 100644 --- a/src/V3Common.cpp +++ b/src/V3Common.cpp @@ -63,6 +63,7 @@ static void makeVlToString(AstIface* nodep) { } static void makeVlToString(AstNodeUOrStructDType* nodep) { AstNodeModule* const modp = nodep->classOrPackagep(); + UASSERT_OBJ(modp, nodep, "Unlinked struct package"); AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"}; funcp->argTypes("const " + EmitCBase::prefixNameProtect(nodep) + "& obj"); @@ -81,11 +82,13 @@ static void makeVlToString(AstNodeUOrStructDType* nodep) { } stmt += VIdProtect::protect(itemp->prettyName()) + ":\" + "; if (VN_IS(itemp->dtypep()->skipRefp(), BasicDType) && itemp->isWide()) { - stmt += "VL_TO_STRING_W"; + stmt += "VL_TO_STRING_W("; + stmt += cvtToStr(itemp->widthWords()); + stmt += ", "; } else { - stmt += "VL_TO_STRING"; + stmt += "VL_TO_STRING("; } - stmt += "(obj." + itemp->nameProtect() + ");\n"; + stmt += "obj." + itemp->nameProtect() + ");\n"; funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt}); } funcp->addStmtsp(new AstCStmt{nodep->fileline(), "out += \"}\";\n"}); @@ -172,5 +175,5 @@ void V3Common::commonAll() { if (!dtypep->packed()) makeVlToString(dtypep); } } - V3Global::dumpCheckGlobalTree("common", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("common", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 47f1a4b05..f0656eaa5 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -3064,7 +3064,7 @@ private: && nodep->displayType() == VDisplayType::DT_WRITE))) return false; if ((prevp->filep() && !nodep->filep()) || (!prevp->filep() && nodep->filep()) - || !prevp->filep()->sameTree(nodep->filep())) + || (prevp->filep() && nodep->filep() && !prevp->filep()->sameTree(nodep->filep()))) return false; if (!prevp->fmtp() || prevp->fmtp()->nextp() || !nodep->fmtp() || nodep->fmtp()->nextp()) return false; @@ -3405,9 +3405,9 @@ private: TREEOPS("AstCond {$lhsp.isNeqZero}", "replaceWIteratedRhs(nodep)"); TREEOP ("AstCond{$condp.castNot, $thenp, $elsep}", "AstCond{$condp->castNot()->lhsp(), $elsep, $thenp}"); TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isAllOnes, $elsep}", "AstLogOr {$condp, $elsep}"); // a?1:b == a||b - TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b + TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero, !$elsep.isClassHandleValue}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isAllOnes}", "AstLogOr {AstNot{$condp}, $thenp}"); // a?b:1 == ~a||b - TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isZero, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b + TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isZero, !$thenp.isClassHandleValue, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b TREEOP ("AstNodeCond{!$condp.width1, operandBoolShift(nodep->condp())}", "replaceBoolShift(nodep->condp())"); // Prefer constants on left, since that often needs a shift, it lets // constant red remove the shift @@ -3759,7 +3759,7 @@ void V3Const::constifyAllLint(AstNetlist* nodep) { ConstVisitor visitor{ConstVisitor::PROC_V_WARN, /* globalPass: */ true}; (void)visitor.mainAcceptEdit(nodep); } // Destruct before checking - V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("const", 0, dumpTreeLevel() >= 3); } void V3Const::constifyCpp(AstNetlist* nodep) { @@ -3768,7 +3768,7 @@ void V3Const::constifyCpp(AstNetlist* nodep) { ConstVisitor visitor{ConstVisitor::PROC_CPP, /* globalPass: */ true}; (void)visitor.mainAcceptEdit(nodep); } // Destruct before checking - V3Global::dumpCheckGlobalTree("const_cpp", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("const_cpp", 0, dumpTreeLevel() >= 3); } AstNode* V3Const::constifyEdit(AstNode* nodep) { @@ -3792,7 +3792,7 @@ void V3Const::constifyAllLive(AstNetlist* nodep) { ConstVisitor visitor{ConstVisitor::PROC_LIVE, /* globalPass: */ true}; (void)visitor.mainAcceptEdit(nodep); } // Destruct before checking - V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("const", 0, dumpTreeLevel() >= 3); } void V3Const::constifyAll(AstNetlist* nodep) { @@ -3802,7 +3802,7 @@ void V3Const::constifyAll(AstNetlist* nodep) { ConstVisitor visitor{ConstVisitor::PROC_V_EXPENSIVE, /* globalPass: */ true}; (void)visitor.mainAcceptEdit(nodep); } // Destruct before checking - V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("const", 0, dumpTreeLevel() >= 3); } AstNode* V3Const::constifyExpensiveEdit(AstNode* nodep) { diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index 340668e71..280e652cc 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -567,5 +567,5 @@ public: void V3Coverage::coverage(AstNetlist* rootp) { UINFO(2, __FUNCTION__ << ": " << endl); { CoverageVisitor{rootp}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("coverage", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("coverage", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3CoverageJoin.cpp b/src/V3CoverageJoin.cpp index 8b087f09f..dbfc26862 100644 --- a/src/V3CoverageJoin.cpp +++ b/src/V3CoverageJoin.cpp @@ -117,5 +117,5 @@ public: void V3CoverageJoin::coverageJoin(AstNetlist* rootp) { UINFO(2, __FUNCTION__ << ": " << endl); { CoverageJoinVisitor{rootp}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("coveragejoin", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("coveragejoin", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index 5faad31e1..197b6658c 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -62,9 +62,10 @@ private: // TYPES using AssignMap = std::multimap; - // STATE - AstNodeModule* m_modp = nullptr; // Current module - AstSelLoopVars* m_selloopvarsp = nullptr; // Current loop vars + // STATE - across all visitors + const bool m_elimUserVars; // Allow removal of user's vars + const bool m_elimDTypes; // Allow removal of DTypes + const bool m_elimCells; // Allow removal of Cells // List of all encountered to avoid another loop through tree std::vector m_varsp; std::vector m_dtypesp; @@ -73,13 +74,14 @@ private: std::vector m_cellsp; std::vector m_classesp; std::vector m_typedefsp; - AssignMap m_assignMap; // List of all simple assignments for each variable - const bool m_elimUserVars; // Allow removal of user's vars - const bool m_elimDTypes; // Allow removal of DTypes - const bool m_elimCells; // Allow removal of Cells bool m_sideEffect = false; // Side effects discovered in assign RHS + // STATE - for current visit position (use VL_RESTORER) + bool m_inAssign = false; // Currently in an assign + AstNodeModule* m_modp = nullptr; // Current module + AstSelLoopVars* m_selloopvarsp = nullptr; // Current loop vars + // METHODS void checkAll(AstNode* nodep) { @@ -107,18 +109,16 @@ private: void visit(AstNodeModule* nodep) override { if (m_modp) m_modp->user1Inc(); // e.g. Class under Package VL_RESTORER(m_modp); - { - m_modp = nodep; - if (!nodep->dead()) { - iterateChildren(nodep); - checkAll(nodep); - if (AstClass* const classp = VN_CAST(nodep, Class)) { - if (classp->extendsp()) classp->extendsp()->user1Inc(); - if (classp->classOrPackagep()) classp->classOrPackagep()->user1Inc(); - m_classesp.push_back(classp); - // TODO we don't reclaim dead classes yet - graph implementation instead? - classp->user1Inc(); - } + m_modp = nodep; + if (!nodep->dead()) { + iterateChildren(nodep); + checkAll(nodep); + if (AstClass* const classp = VN_CAST(nodep, Class)) { + if (classp->extendsp()) classp->extendsp()->user1Inc(); + if (classp->classOrPackagep()) classp->classOrPackagep()->user1Inc(); + m_classesp.push_back(classp); + // TODO we don't reclaim dead classes yet - graph implementation instead? + classp->user1Inc(); } } } @@ -282,9 +282,13 @@ private: // See if simple assignments to variables may be eliminated because // that variable is never used. // Similar code in V3Life - VL_RESTORER(m_sideEffect); + const bool assignInAssign = m_inAssign; // Might be Assign(..., ExprStmt(Assign), ...) { + VL_RESTORER(m_inAssign); + VL_RESTORER(m_sideEffect); + m_inAssign = true; m_sideEffect = false; + if (assignInAssign) m_sideEffect = true; iterateAndNextNull(nodep->rhsp()); checkAll(nodep); // Has to be direct assignment without any EXTRACTing. @@ -299,6 +303,7 @@ private: } iterateNull(nodep->timingControlp()); } + if (assignInAssign) m_sideEffect = true; // Parent assign shouldn't optimize } //----- @@ -539,29 +544,29 @@ void V3Dead::deadifyModules(AstNetlist* nodep) { { DeadVisitor{nodep, false, false, false, false, !v3Global.opt.topIfacesSupported()}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTree() >= 6); + V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTreeLevel() >= 6); } void V3Dead::deadifyDTypes(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { DeadVisitor{nodep, false, true, false, false, false}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTreeLevel() >= 3); } void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { DeadVisitor{nodep, false, true, true, false, false}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTreeLevel() >= 3); } void V3Dead::deadifyAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { DeadVisitor{nodep, true, true, false, true, false}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTreeLevel() >= 3); } void V3Dead::deadifyAllScoped(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { DeadVisitor{nodep, true, true, true, true, false}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index 1a491422d..760026163 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -92,21 +92,23 @@ private: const VNUser4InUse m_inuser4; const VNUser5InUse m_inuser5; - // STATE + // STATE - across all visitors + std::unordered_map m_scopeVecMap; // Next var number for each scope + std::set m_timingDomains; // Timing resume domains + using VarMap = std::map, AstVar*>; + VarMap m_modVarMap; // Table of new var names created under module + VDouble0 m_statSharedSet; // Statistic tracking + + // STATE - for current visit position (use VL_RESTORER) AstActive* m_activep = nullptr; // Current activate const AstCFunc* m_cfuncp = nullptr; // Current public C Function AstAssignDly* m_nextDlyp = nullptr; // Next delayed assignment in a list of assignments AstNodeProcedure* m_procp = nullptr; // Current process - std::set m_timingDomains; // Timing resume domains bool m_inDly = false; // True in delayed assignments bool m_inLoop = false; // True in for loops bool m_inInitial = false; // True in static initializers and initial blocks bool m_inSuspendableOrFork = false; // True in suspendable processes and forks bool m_ignoreBlkAndNBlk = false; // Suppress delayed assignment BLKANDNBLK - using VarMap = std::map, AstVar*>; - VarMap m_modVarMap; // Table of new var names created under module - VDouble0 m_statSharedSet; // Statistic tracking - std::unordered_map m_scopeVecMap; // Next var number for each scope // METHODS @@ -424,10 +426,8 @@ private: } void visit(AstCFunc* nodep) override { VL_RESTORER(m_cfuncp); - { - m_cfuncp = nodep; - iterateChildren(nodep); - } + m_cfuncp = nodep; + iterateChildren(nodep); } void visit(AstActive* nodep) override { m_activep = nodep; @@ -443,10 +443,12 @@ private: void visit(AstNodeProcedure* nodep) override { VL_RESTORER(m_inSuspendableOrFork); m_inSuspendableOrFork = nodep->isSuspendable(); - m_procp = nodep; - m_timingDomains.clear(); - iterateChildren(nodep); - m_procp = nullptr; + { + VL_RESTORER(m_procp); + m_procp = nodep; + m_timingDomains.clear(); + iterateChildren(nodep); + } if (m_timingDomains.empty()) return; if (auto* const actp = VN_AS(nodep->user3p(), Active)) { // Merge all timing domains (and possibly the active's domain) to create a sentree for @@ -618,10 +620,8 @@ private: } void visit(AstWhile* nodep) override { VL_RESTORER(m_inLoop); - { - m_inLoop = true; - iterateChildren(nodep); - } + m_inLoop = true; + iterateChildren(nodep); } //-------------------- @@ -641,5 +641,5 @@ public: void V3Delayed::delayedAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { DelayedVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("delayed", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("delayed", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Depth.cpp b/src/V3Depth.cpp index 2ca383362..32797e069 100644 --- a/src/V3Depth.cpp +++ b/src/V3Depth.cpp @@ -166,5 +166,5 @@ public: void V3Depth::depthAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { DepthVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("depth", 0, dumpTree() >= 6); + V3Global::dumpCheckGlobalTree("depth", 0, dumpTreeLevel() >= 6); } diff --git a/src/V3DepthBlock.cpp b/src/V3DepthBlock.cpp index 889601de2..4adebda13 100644 --- a/src/V3DepthBlock.cpp +++ b/src/V3DepthBlock.cpp @@ -127,5 +127,5 @@ public: void V3DepthBlock::depthBlockAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { DepthBlockVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("deepblock", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("deepblock", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Descope.cpp b/src/V3Descope.cpp index 3c1eeb518..1d4866faf 100644 --- a/src/V3Descope.cpp +++ b/src/V3Descope.cpp @@ -285,5 +285,5 @@ public: void V3Descope::descopeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { DescopeVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("descope", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("descope", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3DfgAstToDfg.cpp b/src/V3DfgAstToDfg.cpp index 1afaaddff..da265a1ff 100644 --- a/src/V3DfgAstToDfg.cpp +++ b/src/V3DfgAstToDfg.cpp @@ -334,17 +334,20 @@ class AstToDfgVisitor final : public VNVisitor { const uint32_t bEnd = b.m_lsb + bWidth; const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1; - varp->varp()->v3warn( // - MULTIDRIVEN, - "Bits [" // - << overlapEnd << ":" << b.m_lsb << "] of signal " - << varp->varp()->prettyNameQ() - << " have multiple combinational drivers\n" - << a.m_fileline->warnOther() << "... Location of first driver\n" - << a.m_fileline->warnContextPrimary() << '\n' - << b.m_fileline->warnOther() << "... Location of other driver\n" - << b.m_fileline->warnContextSecondary() << varp->varp()->warnOther() - << "... Only the first driver will be respected"); + if (a.m_fileline->operatorCompare(*b.m_fileline) != 0) { + varp->varp()->v3warn( // + MULTIDRIVEN, + "Bits [" // + << overlapEnd << ":" << b.m_lsb << "] of signal " + << varp->varp()->prettyNameQ() + << " have multiple combinational drivers\n" + << a.m_fileline->warnOther() << "... Location of first driver\n" + << a.m_fileline->warnContextPrimary() << '\n' + << b.m_fileline->warnOther() << "... Location of other driver\n" + << b.m_fileline->warnContextSecondary() + << varp->varp()->warnOther() + << "... Only the first driver will be respected"); + } // If the first driver completely covers the range of the second driver, // we can just delete the second driver completely, otherwise adjust the @@ -437,6 +440,7 @@ class AstToDfgVisitor final : public VNVisitor { void visit(AstCell* nodep) override { markReferenced(nodep); } void visit(AstNodeProcedure* nodep) override { markReferenced(nodep); } void visit(AstVar* nodep) override { + if (nodep->isSc()) return; // No need to (and in fact cannot) handle variables with unsupported dtypes if (!DfgVertex::isSupportedDType(nodep->dtypep())) return; // Mark ports as having external references diff --git a/src/V3DfgOptimizer.cpp b/src/V3DfgOptimizer.cpp index d3b361bf6..a299cae0e 100644 --- a/src/V3DfgOptimizer.cpp +++ b/src/V3DfgOptimizer.cpp @@ -237,7 +237,7 @@ void V3DfgOptimizer::extract(AstNetlist* netlistp) { UINFO(2, __FUNCTION__ << ": " << endl); // Extract more optimization candidates DataflowExtractVisitor::apply(netlistp); - V3Global::dumpCheckGlobalTree("dfg-extract", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("dfg-extract", 0, dumpTreeLevel() >= 3); } void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) { @@ -267,7 +267,7 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) { // Build the DFG of this module const std::unique_ptr dfg{V3DfgPasses::astToDfg(*modp, ctx)}; - if (dumpDfg() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-input"); + if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-input"); // Extract the cyclic sub-graphs. We do this because a lot of the optimizations assume a // DAG, and large, mostly acyclic graphs could not be optimized due to the presence of @@ -284,7 +284,7 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) { // For each cyclic component for (auto& component : cyclicComponents) { - if (dumpDfg() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source"); + if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source"); // TODO: Apply optimizations safe for cyclic graphs // Add back under the main DFG (we will convert everything back in one go) dfg->addGraph(*component); @@ -292,7 +292,7 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) { // For each acyclic component for (auto& component : acyclicComponents) { - if (dumpDfg() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source"); + if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source"); // Optimize the component V3DfgPasses::optimize(*component, ctx); // Add back under the main DFG (we will convert everything back in one go) @@ -300,9 +300,9 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) { } // Convert back to Ast - if (dumpDfg() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-optimized"); + if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-optimized"); AstModule* const resultModp = V3DfgPasses::dfgToAst(*dfg, ctx); UASSERT_OBJ(resultModp == modp, modp, "Should be the same module"); } - V3Global::dumpCheckGlobalTree("dfg-optimize", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("dfg-optimize", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index edeb007dd..3edfe5a88 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -285,7 +285,7 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) { const auto apply = [&](int dumpLevel, const string& name, std::function pass) { pass(); - if (dumpDfg() >= dumpLevel) { + if (dumpDfgLevel() >= dumpLevel) { const string strippedName = VString::removeWhitespace(name); const string label = ctx.prefix() + "pass-" + cvtToStr(passNumber) + "-" + strippedName; @@ -294,7 +294,7 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) { ++passNumber; }; - if (dumpDfg() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "input"); + if (dumpDfgLevel() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "input"); apply(3, "input ", [&]() {}); apply(4, "inlineVars ", [&]() { inlineVars(dfg); }); apply(4, "cse0 ", [&]() { cse(dfg, ctx.m_cseContext0); }); @@ -304,5 +304,5 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) { apply(4, "cse1 ", [&]() { cse(dfg, ctx.m_cseContext1); }); } apply(4, "removeVars ", [&]() { removeVars(dfg, ctx.m_removeVarsContext); }); - if (dumpDfg() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "optimized"); + if (dumpDfgLevel() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "optimized"); } diff --git a/src/V3DupFinder.cpp b/src/V3DupFinder.cpp index 49fcf2808..f41d9b1df 100644 --- a/src/V3DupFinder.cpp +++ b/src/V3DupFinder.cpp @@ -100,5 +100,5 @@ void V3DupFinder::dumpFile(const string& filename, bool tree) { } void V3DupFinder::dumpFilePrefixed(const string& nameComment, bool tree) { - if (dump()) dumpFile(v3Global.debugFilename(nameComment) + ".hash", tree); + if (dumpLevel()) dumpFile(v3Global.debugFilename(nameComment) + ".hash", tree); } diff --git a/src/V3EmitCBase.cpp b/src/V3EmitCBase.cpp index 1bec37b9c..5a173f38b 100644 --- a/src/V3EmitCBase.cpp +++ b/src/V3EmitCBase.cpp @@ -79,6 +79,10 @@ string EmitCBaseVisitorConst::cFuncArgs(const AstCFunc* nodep) { args += prefixNameProtect(EmitCParentModule::get(nodep)); args += "* vlSelf"; } + if (nodep->needProcess()) { + if (!args.empty()) args += ", "; + args += "VlProcessRef vlProcess"; + } if (!nodep->argTypes().empty()) { if (!args.empty()) args += ", "; args += nodep->argTypes(); @@ -224,21 +228,12 @@ void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) { } 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)) { - if (usep->useType() == useType) { - if (usep->useType().isInclude()) { - puts("#include \"" + prefixNameProtect(usep) + ".h\"\n"); - } - if (usep->useType().isFwdClass()) { - puts("class " + prefixNameProtect(usep) + ";\n"); - } - nl = "\n"; - } - } - } - puts(nl); + bool nl = false; + forModCUse(modp, useType, [&](string entry) { + puts(entry); + nl = true; + }); + if (nl) puts("\n"); } void EmitCBaseVisitorConst::emitTextSection(const AstNodeModule* modp, VNType type) { diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index 5ae0074c4..4be6b7b91 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -113,6 +113,21 @@ public: void emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, bool withScope); void emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, bool cLinkage = false); void emitVarDecl(const AstVar* nodep, bool asRef = false); + template + static void forModCUse(const AstNodeModule* modp, VUseType useType, F action) { + for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) { + if (AstCUse* const usep = VN_CAST(itemp, CUse)) { + if (usep->useType() == useType) { + if (usep->useType().isInclude()) { + action("#include \"" + prefixNameProtect(usep) + ".h\"\n"); + } + if (usep->useType().isFwdClass()) { + action("class " + prefixNameProtect(usep) + ";\n"); + } + } + } + } + } void emitModCUse(const AstNodeModule* modp, VUseType useType); void emitTextSection(const AstNodeModule* modp, VNType type); diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index 9160e5068..195e41ea8 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -417,6 +417,15 @@ void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPoint puts(selfPointer); comma = true; } + if (nodep->funcp()->needProcess()) { + if (comma) puts(", "); + if (VN_IS(nodep->backp(), CAwait)) { + puts("vlProcess"); + } else { + puts("std::make_shared()"); + } + comma = true; + } if (!nodep->argTypes().empty()) { if (comma) puts(", "); puts(nodep->argTypes()); @@ -695,6 +704,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP return ""; } else if (basicp && basicp->isForkSync()) { return ""; + } else if (basicp && basicp->isProcessRef()) { + return ""; } else if (basicp && basicp->isDelayScheduler()) { return ""; } else if (basicp && basicp->isTriggerScheduler()) { diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 1149c728f..be40a5f53 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -354,6 +354,8 @@ public: && !VN_IS(nodep->rhsp(), CMethodHard) // && !VN_IS(nodep->rhsp(), VarRef) // && !VN_IS(nodep->rhsp(), AssocSel) // + && !VN_IS(nodep->rhsp(), MemberSel) // + && !VN_IS(nodep->rhsp(), StructSel) // && !VN_IS(nodep->rhsp(), ArraySel)) { // Wide functions assign into the array directly, don't need separate assign statement m_wideTempRefp = VN_AS(nodep->lhsp(), VarRef); @@ -467,7 +469,7 @@ public: void visit(AstLambdaArgRef* nodep) override { putbs(nodep->nameProtect()); } void visit(AstWith* nodep) override { // With uses a C++11 lambda - putbs("[=]("); + putbs("[&]("); if (auto* const argrefp = nodep->indexArgRefp()) { putbs(argrefp->dtypep()->cType(argrefp->nameProtect(), false, false)); puts(","); @@ -475,10 +477,9 @@ public: if (auto* const argrefp = nodep->valueArgRefp()) { putbs(argrefp->dtypep()->cType(argrefp->nameProtect(), false, false)); } - // Probably fragile, V3Task may need to convert to a AstCReturn - puts(") { return "); + puts(") {\n"); iterateAndNextConstNull(nodep->exprp()); - puts("; }\n"); + puts("}\n"); } void visit(AstNodeCase* nodep) override { // LCOV_EXCL_LINE // In V3Case... @@ -792,7 +793,7 @@ public: void visit(AstSysFuncAsTask* nodep) override { if (!nodep->lhsp()->isWide()) puts("(void)"); iterateAndNextConstNull(nodep->lhsp()); - if (!nodep->lhsp()->isWide()) puts(";"); + if (!nodep->lhsp()->isWide()) puts(";\n"); } void visit(AstStackTraceF* nodep) override { puts("VL_STACKTRACE_N()"); } void visit(AstStackTraceT* nodep) override { puts("VL_STACKTRACE();\n"); } @@ -1043,7 +1044,9 @@ public: } void visit(AstCCast* nodep) override { // Extending a value of the same word width is just a NOP. - if (nodep->size() <= VL_IDATASIZE) { + if (const AstClassRefDType* const classDtypep = VN_CAST(nodep->dtypep(), ClassRefDType)) { + puts("(" + classDtypep->cType("", false, false) + ")("); + } else if (nodep->size() <= VL_IDATASIZE) { puts("(IData)("); } else { puts("(QData)("); diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 63a06a956..04e38e172 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -17,11 +17,15 @@ #include "config_build.h" #include "verilatedos.h" +#include "V3Ast.h" #include "V3EmitC.h" #include "V3EmitCConstInit.h" #include "V3Global.h" #include +#include +#include +#include #include VL_DEFINE_DEBUG_FUNCTIONS; @@ -219,7 +223,7 @@ class EmitCHeader final : public EmitCConstInit { emitted.insert(sdtypep); for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) { - AstNodeUOrStructDType* subp = VN_CAST(itemp->skipRefp(), NodeUOrStructDType); + AstNodeUOrStructDType* const subp = itemp->getChildStructp(); if (subp && !subp->packed()) { // Recurse if it belongs to the current module if (subp->classOrPackagep() == modp) { @@ -235,6 +239,20 @@ class EmitCHeader final : public EmitCConstInit { puts(itemp->dtypep()->cType(itemp->nameProtect(), false, false)); puts(";\n"); } + + puts("\nbool operator==(const " + EmitCBase::prefixNameProtect(sdtypep) + + "& rhs) const {\n"); + puts("return "); + for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + if (itemp != sdtypep->membersp()) puts("\n && "); + puts(itemp->nameProtect() + " == " + "rhs." + itemp->nameProtect()); + } + puts(";\n"); + puts("}\n"); + puts("bool operator!=(const " + EmitCBase::prefixNameProtect(sdtypep) + + "& rhs) const {\n"); + puts("return !(*this == rhs);\n}\n"); puts("};\n"); } void emitStructs(const AstNodeModule* modp) { @@ -287,11 +305,9 @@ class EmitCHeader final : public EmitCConstInit { + ".h\"\n"); } } - emitModCUse(modp, VUseType::INT_INCLUDE); // Forward declarations required by this AstNodeModule puts("\nclass " + symClassName() + ";\n"); - emitModCUse(modp, VUseType::INT_FWD_CLASS); // From `systemc_header emitTextSection(modp, VNType::atScHdr); @@ -300,6 +316,7 @@ class EmitCHeader final : public EmitCConstInit { // Open class body {{{ puts("\nclass "); + if (!VN_IS(modp, Class)) puts("alignas(VL_CACHE_LINE_BYTES) "); puts(prefixNameProtect(modp)); if (const AstClass* const classp = VN_CAST(modp, Class)) { puts(" : public "); @@ -329,11 +346,7 @@ class EmitCHeader final : public EmitCConstInit { emitTextSection(modp, VNType::atScInt); // Close class body - if (!VN_IS(modp, Class)) { - puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n"); - } else { - puts("};\n"); - } + puts("};\n"); // }}} // Emit out of class function declarations @@ -364,6 +377,19 @@ class EmitCHeader final : public EmitCConstInit { if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n"); if (v3Global.usesTiming()) puts("#include \"verilated_timing.h\"\n"); + std::set cuse_set; + auto add_to_cuse_set = [&](string s) { cuse_set.insert(s); }; + + forModCUse(modp, VUseType::INT_INCLUDE, add_to_cuse_set); + forModCUse(modp, VUseType::INT_FWD_CLASS, add_to_cuse_set); + if (const AstClassPackage* const packagep = VN_CAST(modp, ClassPackage)) { + forModCUse(packagep->classp(), VUseType::INT_INCLUDE, add_to_cuse_set); + forModCUse(packagep->classp(), VUseType::INT_FWD_CLASS, add_to_cuse_set); + } + + for (const string& s : cuse_set) puts(s); + puts("\n"); + emitAll(modp); if (const AstClassPackage* const packagep = VN_CAST(modp, ClassPackage)) { diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index 679f392ce..f47b5bcf9 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -22,6 +22,7 @@ #include "V3EmitCFunc.h" #include "V3Global.h" #include "V3String.h" +#include "V3ThreadPool.h" #include "V3UniqueNames.h" #include @@ -53,6 +54,7 @@ class EmitCGatherDependencies final : VNVisitorConst { } else if (const AstNodeUOrStructDType* const dtypep = VN_CAST(nodep, NodeUOrStructDType)) { if (!dtypep->packed()) { + UASSERT_OBJ(dtypep->classOrPackagep(), nodep, "Unlinked struct package"); m_dependencies.insert(EmitCBase::prefixNameProtect(dtypep->classOrPackagep())); } } @@ -145,7 +147,7 @@ class EmitCGatherDependencies final : VNVisitorConst { } public: - static const std::set gather(AstCFunc* cfuncp) { + static const std::set gather(AstCFunc* cfuncp) VL_MT_STABLE { const EmitCGatherDependencies visitor{cfuncp}; return std::move(visitor.m_dependencies); } @@ -564,7 +566,8 @@ class EmitCImp final : EmitCFunc { ~EmitCImp() override = default; public: - static void main(const AstNodeModule* modp, bool slow, std::deque& cfilesr) { + static void main(const AstNodeModule* modp, bool slow, + std::deque& cfilesr) VL_MT_STABLE { EmitCImp{modp, slow, cfilesr}; } }; @@ -908,7 +911,7 @@ class EmitCTrace final : EmitCFunc { ~EmitCTrace() override = default; public: - static void main(AstNodeModule* modp, bool slow, std::deque& cfilesr) { + static void main(AstNodeModule* modp, bool slow, std::deque& cfilesr) VL_MT_STABLE { EmitCTrace{modp, slow, cfilesr}; } }; @@ -921,24 +924,37 @@ void V3EmitC::emitcImp() { // Make parent module pointers available. const EmitCParentModule emitCParentModule; std::list> cfiles; + std::list> futures; // Process each module in turn for (const AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) { if (VN_IS(nodep, Class)) continue; // Imped with ClassPackage const AstNodeModule* const modp = VN_AS(nodep, NodeModule); cfiles.emplace_back(); - EmitCImp::main(modp, /* slow: */ true, cfiles.back()); + auto& slowCfilesr = cfiles.back(); + futures.push_back(V3ThreadPool::s().enqueue( + [modp, &slowCfilesr]() { EmitCImp::main(modp, /* slow: */ true, slowCfilesr); })); cfiles.emplace_back(); - EmitCImp::main(modp, /* slow: */ false, cfiles.back()); + auto& fastCfilesr = cfiles.back(); + futures.push_back(V3ThreadPool::s().enqueue( + [modp, &fastCfilesr]() { EmitCImp::main(modp, /* slow: */ false, fastCfilesr); })); } // Emit trace routines (currently they can only exist in the top module) if (v3Global.opt.trace() && !v3Global.opt.lintOnly()) { cfiles.emplace_back(); - EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true, cfiles.back()); + auto& slowCfilesr = cfiles.back(); + futures.push_back(V3ThreadPool::s().enqueue([&slowCfilesr]() { + EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true, slowCfilesr); + })); cfiles.emplace_back(); - EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false, cfiles.back()); + auto& fastCfilesr = cfiles.back(); + futures.push_back(V3ThreadPool::s().enqueue([&fastCfilesr]() { + EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false, fastCfilesr); + })); } + // Wait for futures + V3ThreadPool::waitForFutures(futures); for (const auto& collr : cfiles) { for (const auto cfilep : collr) v3Global.rootp()->addFilesp(cfilep); } diff --git a/src/V3EmitCMain.cpp b/src/V3EmitCMain.cpp index 10216025b..3ea952903 100644 --- a/src/V3EmitCMain.cpp +++ b/src/V3EmitCMain.cpp @@ -51,6 +51,14 @@ private: // Not defining main_time/vl_time_stamp, so v3Global.opt.addCFlags("-DVL_TIME_CONTEXT"); // On MSVC++ anyways + // Optional main top name argument, with empty string replacement + string topArg; + string topName = v3Global.opt.mainTopName(); + if (!topName.empty()) { + if (topName == "-") topName = ""; + topArg = ", \"" + topName + "\""; + } + // Heavily commented output, as users are likely to look at or copy this code ofp()->putsHeader(); puts("// DESCRIPTION: main() calling loop, created with Verilator --main\n"); @@ -71,7 +79,7 @@ private: puts("// Construct the Verilated model, from Vtop.h generated from Verilating\n"); puts("const std::unique_ptr<" + topClassName() + "> topp{new " + topClassName() - + "{contextp.get()}};\n"); + + "{contextp.get()" + topArg + "}};\n"); puts("\n"); puts("// Simulate until $finish\n"); diff --git a/src/V3EmitCModel.cpp b/src/V3EmitCModel.cpp index e7debc3cd..d63708828 100644 --- a/src/V3EmitCModel.cpp +++ b/src/V3EmitCModel.cpp @@ -89,7 +89,7 @@ class EmitCModel final : public EmitCFunc { puts("\n"); puts("// This class is the main interface to the Verilated model\n"); - puts("class " + topClassName() + " VL_NOT_FINAL : "); + puts("class alignas(VL_CACHE_LINE_BYTES) " + topClassName() + " VL_NOT_FINAL : "); if (optSystemC()) { // SC_MODULE, but with multiple-inheritance of VerilatedModel puts("public ::sc_core::sc_module, "); @@ -238,7 +238,7 @@ class EmitCModel final : public EmitCFunc { puts("std::unique_ptr traceConfig() const override final;\n"); } - puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n"); + puts("};\n"); ofp()->putsEndGuard(); diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 59b299d7b..04ea78814 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -440,7 +440,8 @@ void EmitCSyms::emitSymHdr() { } puts("\n// SYMS CLASS (contains all model state)\n"); - puts("class " + symClassName() + " final : public VerilatedSyms {\n"); + puts("class alignas(VL_CACHE_LINE_BYTES)" + symClassName() + + " final : public VerilatedSyms {\n"); ofp()->putsPrivate(false); // public: puts("// INTERNAL STATE\n"); @@ -554,7 +555,7 @@ void EmitCSyms::emitSymHdr() { puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n"); puts("void " + protect("__Vdeserialize") + "(VerilatedDeserialize& os);\n"); } - puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n"); + puts("};\n"); ofp()->putsEndGuard(); VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 67c0a274a..8314ec15c 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -424,6 +424,12 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { iterateAndNextConstNull(nodep->exprsp()); puts(")"); } + void visit(AstExprStmt* nodep) override { + putfs(nodep, "$_EXPRSTMT(\n"); + iterateAndNextConstNull(nodep->stmtsp()); + putbs(", "); + puts(");\n"); + } void visit(AstCMethodHard* nodep) override { iterateConst(nodep->fromp()); @@ -434,6 +440,15 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } puts(")"); } + void visit(AstCMethodCall* nodep) override { + iterateConst(nodep->fromp()); + puts("." + nodep->name() + "("); + for (AstNode* pinp = nodep->argsp(); pinp; pinp = pinp->nextp()) { + if (pinp != nodep->argsp()) puts(", "); + iterateConst(pinp); + } + puts(")"); + } // Operators virtual void emitVerilogFormat(AstNode* nodep, const string& format, AstNode* lhsp = nullptr, diff --git a/src/V3EmitXml.cpp b/src/V3EmitXml.cpp index 5dc1ed39e..5466bf2f4 100644 --- a/src/V3EmitXml.cpp +++ b/src/V3EmitXml.cpp @@ -188,7 +188,7 @@ class EmitXmlFileVisitor final : public VNVisitorConst { void visit(AstVar* nodep) override { const VVarType typ = nodep->varType(); const string kw = nodep->verilogKwd(); - const string vt = nodep->dtypep()->name(); + const string vt = nodep->dtypep() ? nodep->dtypep()->name() : ""; outputTag(nodep, ""); if (nodep->isIO()) { puts(" dir="); @@ -225,7 +225,7 @@ class EmitXmlFileVisitor final : public VNVisitorConst { void visit(AstPin* nodep) override { // What we call a pin in verilator is a port in the IEEE spec. outputTag(nodep, "port"); // IEEE: vpiPort - if (nodep->modVarp()->isIO()) { + if (nodep->modVarp() && nodep->modVarp()->isIO()) { puts(" direction=\"" + nodep->modVarp()->direction().xmlKwd() + "\""); } puts(" portIndex=\"" + cvtToStr(nodep->pinNum()) + "\""); // IEEE: vpiPortIndex @@ -254,7 +254,7 @@ class EmitXmlFileVisitor final : public VNVisitorConst { void visit(AstNodeCCall* nodep) override { outputTag(nodep, ""); puts(" func="); - putsQuoted(nodep->funcp()->name()); + putsQuoted(nodep->funcp() ? nodep->funcp()->name() : nodep->name()); outputChildrenEnd(nodep, ""); } @@ -401,7 +401,7 @@ private: } } void visit(AstCell* nodep) override { - if (nodep->modp()->dead()) return; + if (nodep->modp() && nodep->modp()->dead()) return; if (!m_hasChildren) m_os << ">\n"; m_os << "fileline()->xmlDetailedLocation() << " name=\"" << nodep->name() << "\"" diff --git a/src/V3Error.cpp b/src/V3Error.cpp index 6fc051edc..a0fa7a3d3 100644 --- a/src/V3Error.cpp +++ b/src/V3Error.cpp @@ -220,10 +220,10 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra) tellManual(2); } #ifndef V3ERROR_NO_GLOBAL_ - if (dumpTree() || debug()) { + if (dumpTreeLevel() || debug()) { V3Broken::allowMidvisitorCheck(true); V3ThreadPool::s().requestExclusiveAccess([&]() VL_REQUIRES(m_mutex) { - if (dumpTree()) { + if (dumpTreeLevel()) { v3Global.rootp()->dumpTreeFile( v3Global.debugFilename("final.tree", 990)); } diff --git a/src/V3Error.h b/src/V3Error.h index 01578fa0e..da7a17a9b 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -54,17 +54,17 @@ public: // Boolean information we track per-line, but aren't errors I_CELLDEFINE, // Inside cell define from `celldefine/`endcelldefine I_COVERAGE, // Coverage is on/off from /*verilator coverage_on/off*/ - I_TRACING, // Tracing is on/off from /*verilator tracing_on/off*/ - I_LINT, // All lint messages - I_UNUSED, // Unused genvar, parameter or signal message (Backward Compatibility) I_DEF_NETTYPE_WIRE, // `default_nettype is WIRE (false=NONE) + I_LINT, // All lint messages I_TIMING, // Enable timing from /*verilator timing_on/off*/ + I_TRACING, // Tracing is on/off from /*verilator tracing_on/off*/ + I_UNUSED, // Unused genvar, parameter or signal message (Backward Compatibility) // Error codes: - E_PORTSHORT, // Error: Output port is connected to a constant, electrical short - E_UNSUPPORTED, // Error: Unsupported (generally) - E_TASKNSVAR, // Error: Task I/O not simple E_NEEDTIMINGOPT, // Error: --timing/--no-timing option not specified E_NOTIMING, // Timing control encountered with --no-timing + E_PORTSHORT, // Error: Output port is connected to a constant, electrical short + E_TASKNSVAR, // Error: Task I/O not simple + E_UNSUPPORTED, // Error: Unsupported (generally) // // Warning codes: EC_FIRST_WARN, // Just a code so the program knows where to start warnings @@ -116,6 +116,7 @@ public: MODDUP, // Duplicate module MULTIDRIVEN, // Driven from multiple blocks MULTITOP, // Multiple top level modules + NEWERSTD, // Newer language standard required NOLATCH, // No latch detected in always_latch block NULLPORT, // Null port detected in module definition PINCONNECTEMPTY,// Cell pin connected by name with empty reference @@ -155,10 +156,10 @@ public: VARHIDDEN, // Hiding variable WAITCONST, // Wait condition is constant WIDTH, // Width mismatch - WIDTHTRUNC, // Width mismatch- lhs < rhs - WIDTHEXPAND, // Width mismatch- lhs > rhs - WIDTHXZEXPAND, // Width mismatch- lhs > rhs xz filled WIDTHCONCAT, // Unsized numbers/parameters in concatenations + WIDTHEXPAND, // Width mismatch- lhs > rhs + WIDTHTRUNC, // Width mismatch- lhs < rhs + WIDTHXZEXPAND, // Width mismatch- lhs > rhs xz filled ZERODLY, // #0 delay _ENUM_MAX // ***Add new elements below also*** @@ -180,9 +181,9 @@ public: // Leading spaces indicate it can't be disabled. " MIN", " INFO", " FATAL", " FATALEXIT", " FATALSRC", " ERROR", " FIRST_NAMED", // Boolean - " I_CELLDEFINE", " I_COVERAGE", " I_TRACING", " I_LINT", " I_UNUSED", " I_DEF_NETTYPE_WIRE", " I_TIMING", + " I_CELLDEFINE", " I_COVERAGE", " I_DEF_NETTYPE_WIRE", " I_LINT", " I_TIMING", " I_TRACING", " I_UNUSED", // Errors - "PORTSHORT", "UNSUPPORTED", "TASKNSVAR", "NEEDTIMINGOPT", "NOTIMING", + "NEEDTIMINGOPT", "NOTIMING", "PORTSHORT", "TASKNSVAR", "UNSUPPORTED", // Warnings " EC_FIRST_WARN", "ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA", @@ -195,7 +196,7 @@ public: "IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE", "INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE", "LATCH", "LITENDIAN", "MINTYPMAXDLY", "MODDUP", - "MULTIDRIVEN", "MULTITOP", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY", + "MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY", "PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY", "SELRANGE", "SHORTREAL", "SPLITVAR", "STATICVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", @@ -203,7 +204,7 @@ public: "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", "UNPACKED", "UNSIGNED", "UNUSEDGENVAR", "UNUSEDPARAM", "UNUSEDSIGNAL", "USERERROR", "USERFATAL", "USERINFO", "USERWARN", - "VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHTRUNC", "WIDTHEXPAND", "WIDTHXZEXPAND", "WIDTHCONCAT", "ZERODLY", + "VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", "WIDTHEXPAND", "WIDTHTRUNC", "WIDTHXZEXPAND", "ZERODLY", " MAX" }; // clang-format on @@ -233,9 +234,9 @@ public: 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 == PINMISSING || m_e == REALCVT || m_e == STATICVAR - || m_e == UNSIGNED || m_e == WIDTH || m_e == WIDTHTRUNC || m_e == WIDTHEXPAND - || m_e == WIDTHXZEXPAND); + || m_e == LATCH || m_e == NEWERSTD || m_e == PINMISSING || m_e == REALCVT + || m_e == STATICVAR || m_e == UNSIGNED || m_e == WIDTH || m_e == WIDTHTRUNC + || m_e == WIDTHEXPAND || m_e == WIDTHXZEXPAND); } // Warnings that are style only bool styleError() const VL_MT_SAFE { @@ -666,12 +667,10 @@ inline void v3errorEndFatal(std::ostringstream& sstr) static_assert(true, "") // Takes an optional "name" (as __VA_ARGS__) -#define VL_DEFINE_DUMP(...) \ - VL_ATTR_UNUSED static int dump##__VA_ARGS__() { \ +#define VL_DEFINE_DUMP(func, tag) \ + VL_ATTR_UNUSED static int dump##func() { \ static int level = -1; \ if (VL_UNLIKELY(level < 0)) { \ - std::string tag{VL_STRINGIFY(__VA_ARGS__)}; \ - tag[0] = std::tolower(tag[0]); \ const unsigned dumpTag = v3Global.opt.dumpLevel(tag); \ const unsigned dumpSrc = v3Global.opt.dumpSrcLevel(__FILE__); \ const unsigned dumpLevel = dumpTag >= dumpSrc ? dumpTag : dumpSrc; \ @@ -685,11 +684,11 @@ inline void v3errorEndFatal(std::ostringstream& sstr) // Define debug*() and dump*() routines. This needs to be added to every compilation unit so that // --debugi- and --dumpi- can be used to control debug prints and dumping #define VL_DEFINE_DEBUG_FUNCTIONS \ - VL_DEFINE_DEBUG(); /* Define 'int debug()' */ \ - VL_DEFINE_DUMP(); /* Define 'int dump()' */ \ - VL_DEFINE_DUMP(Dfg); /* Define 'int dumpDfg()' */ \ - VL_DEFINE_DUMP(Graph); /* Define 'int dumpGraph()' */ \ - VL_DEFINE_DUMP(Tree); /* Define 'int dumpTree()' */ \ + VL_DEFINE_DEBUG(); /* Define 'int debug()' for --debugi */ \ + VL_DEFINE_DUMP(Level, ""); /* Define 'int dumpLevel()' for --dumpi */ \ + VL_DEFINE_DUMP(DfgLevel, "dfg"); /* Define 'int dumpDfgLevel()' for --dumpi-level */ \ + VL_DEFINE_DUMP(GraphLevel, "graph"); /* Define 'int dumpGraphLevel()' for dumpi-graph */ \ + VL_DEFINE_DUMP(TreeLevel, "tree"); /* Define 'int dumpTreeLevel()' for dumpi-tree */ \ static_assert(true, "") //---------------------------------------------------------------------- diff --git a/src/V3Expand.cpp b/src/V3Expand.cpp index a34660a7d..812ac86fd 100644 --- a/src/V3Expand.cpp +++ b/src/V3Expand.cpp @@ -39,6 +39,38 @@ VL_DEFINE_DEBUG_FUNCTIONS; +//###################################################################### +// Find nodes with side effects, to mark as non-expandable + +class ExpandOkVisitor final : public VNVisitor { +private: + // NODE STATE + // AstNode::user2() -> bool. Is pure (along with all children) + const VNUser2InUse m_inuser2; + + // STATE - for current visit position (use VL_RESTORER) + // Tracks similar to AstNode::isTreePureRecurse(), but avoid O(n^2) + // False = pure, as nodes that ExpandVisitor inserts preserve pureness + bool m_isImpure = true; // Currently pure + + void visit(AstNode* nodep) override { + bool selfImpure = !nodep->isPure(); + { + VL_RESTORER(m_isImpure); + m_isImpure = false; + iterateChildren(nodep); + selfImpure |= m_isImpure; + nodep->user2(selfImpure); + } + m_isImpure |= selfImpure; + } + +public: + // CONSTRUCTORS + explicit ExpandOkVisitor(AstNetlist* nodep) { iterate(nodep); } + ~ExpandOkVisitor() = default; +}; + //###################################################################### // Expand state, as a visitor of each AstNode @@ -48,15 +80,24 @@ private: // AstNode::user1() -> bool. Processed const VNUser1InUse m_inuser1; - // STATE + // STATE - for current visit position (use VL_RESTORER) AstNode* m_stmtp = nullptr; // Current statement + + // STATE - across all visitors VDouble0 m_statWides; // Statistic tracking VDouble0 m_statWideWords; // Statistic tracking VDouble0 m_statWideLimited; // Statistic tracking // METHODS + // Use state that ExpandOkVisitor calculated + bool isImpure(AstNode* nodep) { + const bool impure = nodep->user2(); + if (impure) UINFO(9, " impure " << nodep << endl); + return impure; + } - bool doExpand(AstNode* nodep) { + bool doExpandWide(AstNode* nodep) { + if (isImpure(nodep)) return false; ++m_statWides; if (nodep->widthWords() <= v3Global.opt.expandLimit()) { m_statWideWords += nodep->widthWords(); @@ -225,7 +266,7 @@ private: bool expandWide(AstNodeAssign* nodep, AstConst* rhsp) { UINFO(8, " Wordize ASSIGN(CONST) " << nodep << endl); - if (!doExpand(nodep)) return false; + if (!doExpandWide(nodep)) return false; // -> {for each_word{ ASSIGN(WORDSEL(wide,#),WORDSEL(CONST,#))}} if (rhsp->num().isFourState()) { rhsp->v3warn(E_UNSUPPORTED, // LCOV_EXCL_LINE // impossible? @@ -241,7 +282,7 @@ private: //-------- Uniops bool expandWide(AstNodeAssign* nodep, AstVarRef* rhsp) { UINFO(8, " Wordize ASSIGN(VARREF) " << nodep << endl); - if (!doExpand(nodep)) return false; + if (!doExpandWide(nodep)) return false; for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, newAstWordSelClone(rhsp, w)); } @@ -251,7 +292,7 @@ private: UINFO(8, " Wordize ASSIGN(ARRAYSEL) " << nodep << endl); UASSERT_OBJ(!VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType), nodep, "ArraySel with unpacked arrays should have been removed in V3Slice"); - if (!doExpand(nodep)) return false; + if (!doExpandWide(nodep)) return false; for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, newAstWordSelClone(rhsp, w)); } @@ -260,7 +301,7 @@ private: bool expandWide(AstNodeAssign* nodep, AstNot* rhsp) { UINFO(8, " Wordize ASSIGN(NOT) " << nodep << endl); // -> {for each_word{ ASSIGN(WORDSEL(wide,#),NOT(WORDSEL(lhs,#))) }} - if (!doExpand(nodep)) return false; + if (!doExpandWide(nodep)) return false; FileLine* const fl = rhsp->fileline(); for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, new AstNot{fl, newAstWordSelClone(rhsp->lhsp(), w)}); @@ -270,7 +311,7 @@ private: //-------- Biops bool expandWide(AstNodeAssign* nodep, AstAnd* rhsp) { UINFO(8, " Wordize ASSIGN(AND) " << nodep << endl); - if (!doExpand(nodep)) return false; + if (!doExpandWide(nodep)) return false; FileLine* const fl = nodep->fileline(); for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, @@ -281,7 +322,7 @@ private: } bool expandWide(AstNodeAssign* nodep, AstOr* rhsp) { UINFO(8, " Wordize ASSIGN(OR) " << nodep << endl); - if (!doExpand(nodep)) return false; + if (!doExpandWide(nodep)) return false; FileLine* const fl = nodep->fileline(); for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, @@ -292,7 +333,7 @@ private: } bool expandWide(AstNodeAssign* nodep, AstXor* rhsp) { UINFO(8, " Wordize ASSIGN(XOR) " << nodep << endl); - if (!doExpand(nodep)) return false; + if (!doExpandWide(nodep)) return false; FileLine* const fl = nodep->fileline(); for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, @@ -304,7 +345,7 @@ private: //-------- Triops bool expandWide(AstNodeAssign* nodep, AstNodeCond* rhsp) { UINFO(8, " Wordize ASSIGN(COND) " << nodep << endl); - if (!doExpand(nodep)) return false; + if (!doExpandWide(nodep)) return false; FileLine* const fl = nodep->fileline(); for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, @@ -322,6 +363,7 @@ private: if (nodep->isWide()) { // See under ASSIGN(EXTEND) } else { + if (isImpure(nodep)) return; AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack(); AstNodeExpr* newp = lhsp; if (nodep->isQuad()) { @@ -343,7 +385,7 @@ private: } bool expandWide(AstNodeAssign* nodep, AstExtend* rhsp) { UINFO(8, " Wordize ASSIGN(EXTEND) " << nodep << endl); - if (!doExpand(nodep)) return false; + if (!doExpandWide(nodep)) return false; AstNodeExpr* const rlhsp = rhsp->lhsp(); for (int w = 0; w < rlhsp->widthWords(); ++w) { addWordAssign(nodep, w, newAstWordSelClone(rlhsp, w)); @@ -365,6 +407,7 @@ private: } else if (nodep->isWide()) { // See under ASSIGN(WIDE) } else if (nodep->fromp()->isWide()) { + if (isImpure(nodep)) return; UINFO(8, " SEL(wide) " << nodep << endl); UASSERT_OBJ(nodep->widthConst() <= 64, nodep, "Inconsistent width"); // Selection amounts @@ -386,7 +429,7 @@ private: const uint32_t midMsbOffset = std::min(nodep->widthConst(), VL_EDATASIZE) - 1; AstNodeExpr* const midMsbp = new AstAdd{lfl, new AstConst{lfl, midMsbOffset}, - nodep->lsbp()->cloneTree(false)}; + nodep->lsbp()->cloneTree(true)}; AstNodeExpr* midwordp = // SEL(from,[midwordnum]) newWordSel(ffl, nodep->fromp()->cloneTree(true), midMsbp, 0); // newWordSel clones the index, so delete it @@ -414,7 +457,7 @@ private: if (nodep->widthConst() > VL_EDATASIZE) { const uint32_t hiMsbOffset = nodep->widthConst() - 1; AstNodeExpr* const hiMsbp = new AstAdd{lfl, new AstConst{lfl, hiMsbOffset}, - nodep->lsbp()->cloneTree(false)}; + nodep->lsbp()->cloneTree(true)}; AstNodeExpr* hiwordp = // SEL(from,[hiwordnum]) newWordSel(ffl, nodep->fromp()->cloneTree(true), hiMsbp); // newWordSel clones the index, so delete it @@ -439,15 +482,16 @@ private: newp->dtypeFrom(nodep); VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } else { // Long/Quad from Long/Quad + // No isImpure() check - can handle side effects in below UINFO(8, " SEL->SHIFT " << nodep << endl); FileLine* const fl = nodep->fileline(); AstNodeExpr* fromp = nodep->fromp()->unlinkFrBack(); AstNodeExpr* const lsbp = nodep->lsbp()->unlinkFrBack(); - if (nodep->isQuad() && !fromp->isQuad()) { fromp = new AstCCast{fl, fromp, nodep}; } + if (nodep->isQuad() && !fromp->isQuad()) fromp = new AstCCast{fl, fromp, nodep}; // {large}>>32 requires 64-bit shift operation; then cast AstNodeExpr* newp = new AstShiftR{fl, fromp, dropCondBound(lsbp), fromp->width()}; newp->dtypeFrom(fromp); - if (!nodep->isQuad() && fromp->isQuad()) { newp = new AstCCast{fl, newp, nodep}; } + if (!nodep->isQuad() && fromp->isQuad()) newp = new AstCCast{fl, newp, nodep}; newp->dtypeFrom(nodep); VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } @@ -455,7 +499,7 @@ private: bool expandWide(AstNodeAssign* nodep, AstSel* rhsp) { UASSERT_OBJ(nodep->widthMin() == rhsp->widthConst(), nodep, "Width mismatch"); - if (!doExpand(nodep)) return false; + if (!doExpandWide(nodep)) return false; if (VN_IS(rhsp->lsbp(), Const) && VL_BITBIT_E(rhsp->lsbConst()) == 0) { const int lsb = rhsp->lsbConst(); UINFO(8, " Wordize ASSIGN(SEL,align) " << nodep << endl); @@ -499,6 +543,7 @@ private: // rhsp: may be allones and can remove AND NOT gate // lsbp: constant or variable // Yuk. + if (isImpure(nodep)) return false; FileLine* const nfl = nodep->fileline(); FileLine* const lfl = lhsp->fileline(); const bool destwide = lhsp->fromp()->isWide(); @@ -649,13 +694,14 @@ private: if (nodep->isWide()) { // See under ASSIGN(WIDE) } else { + // No isImpure() check - can handle side effects in below UINFO(8, " CONCAT " << nodep << endl); FileLine* const fl = nodep->fileline(); AstNodeExpr* lhsp = nodep->lhsp()->unlinkFrBack(); AstNodeExpr* rhsp = nodep->rhsp()->unlinkFrBack(); const uint32_t rhsshift = rhsp->widthMin(); - if (nodep->isQuad() && !lhsp->isQuad()) { lhsp = new AstCCast{fl, lhsp, nodep}; } - if (nodep->isQuad() && !rhsp->isQuad()) { rhsp = new AstCCast{fl, rhsp, nodep}; } + if (nodep->isQuad() && !lhsp->isQuad()) lhsp = new AstCCast{fl, lhsp, nodep}; + if (nodep->isQuad() && !rhsp->isQuad()) rhsp = new AstCCast{fl, rhsp, nodep}; AstNodeExpr* const newp = new AstOr{ fl, new AstShiftL{fl, lhsp, new AstConst{fl, rhsshift}, nodep->width()}, rhsp}; newp->dtypeFrom(nodep); // Unsigned @@ -664,7 +710,7 @@ private: } bool expandWide(AstNodeAssign* nodep, AstConcat* rhsp) { UINFO(8, " Wordize ASSIGN(CONCAT) " << nodep << endl); - if (!doExpand(rhsp)) return false; + if (!doExpandWide(rhsp)) return false; FileLine* const fl = rhsp->fileline(); // Lhs or Rhs may be word, long, or quad. // newAstWordSelClone nicely abstracts the difference. @@ -686,6 +732,7 @@ private: if (nodep->isWide()) { // See under ASSIGN(WIDE) } else { + if (isImpure(nodep)) return; FileLine* const fl = nodep->fileline(); AstNodeExpr* lhsp = nodep->lhsp()->unlinkFrBack(); AstNodeExpr* newp; @@ -719,7 +766,7 @@ private: } bool expandWide(AstNodeAssign* nodep, AstReplicate* rhsp) { UINFO(8, " Wordize ASSIGN(REPLICATE) " << nodep << endl); - if (!doExpand(rhsp)) return false; + if (!doExpandWide(rhsp)) return false; FileLine* const fl = nodep->fileline(); AstNodeExpr* const lhsp = rhsp->lhsp(); const int lhswidth = lhsp->widthMin(); @@ -748,6 +795,7 @@ private: if (nodep->user1SetOnce()) return; // Process once iterateChildren(nodep); if (nodep->lhsp()->isWide()) { + if (isImpure(nodep)) return; UINFO(8, " Wordize EQ/NEQ " << nodep << endl); // -> (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}} FileLine* const fl = nodep->fileline(); @@ -773,6 +821,7 @@ private: iterateChildren(nodep); FileLine* const fl = nodep->fileline(); if (nodep->lhsp()->isWide()) { + if (isImpure(nodep)) return; UINFO(8, " Wordize REDOR " << nodep << endl); // -> (0!={or{for each_word{WORDSEL(lhs,#)}}} AstNodeExpr* newp = nullptr; @@ -783,6 +832,7 @@ private: newp = new AstNeq{fl, new AstConst{fl, AstConst::SizedEData{}, 0}, newp}; VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } else { + // No isImpure() check - can handle side effects in below UINFO(8, " REDOR->EQ " << nodep << endl); AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack(); AstNodeExpr* const newp = new AstNeq{ @@ -795,6 +845,7 @@ private: iterateChildren(nodep); FileLine* const fl = nodep->fileline(); if (nodep->lhsp()->isWide()) { + if (isImpure(nodep)) return; UINFO(8, " Wordize REDAND " << nodep << endl); // -> (0!={and{for each_word{WORDSEL(lhs,#)}}} AstNodeExpr* newp = nullptr; @@ -814,6 +865,7 @@ private: newp}; VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } else { + // No isImpure() check - can handle side effects in below UINFO(8, " REDAND->EQ " << nodep << endl); AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack(); AstNodeExpr* const newp = new AstEq{fl, new AstConst{fl, wordMask(lhsp)}, lhsp}; @@ -824,6 +876,7 @@ private: if (nodep->user1SetOnce()) return; // Process once iterateChildren(nodep); if (nodep->lhsp()->isWide()) { + if (isImpure(nodep)) return; UINFO(8, " Wordize REDXOR " << nodep << endl); // -> (0!={redxor{for each_word{XOR(WORDSEL(lhs,#))}}} FileLine* const fl = nodep->fileline(); @@ -906,14 +959,14 @@ public: } }; -//---------------------------------------------------------------------- -// Top loop - //###################################################################### // Expand class functions void V3Expand::expandAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { ExpandVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("expand", 0, dumpTree() >= 3); + { + ExpandOkVisitor okVisitor{nodep}; + ExpandVisitor{nodep}; + } // Destruct before checking + V3Global::dumpCheckGlobalTree("expand", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Force.cpp b/src/V3Force.cpp index 1073f1c8c..6eab5f0d4 100644 --- a/src/V3Force.cpp +++ b/src/V3Force.cpp @@ -253,16 +253,33 @@ class ForceConvertVisitor final : public VNVisitor { resetRdp->rhsp()->foreach([this](AstNodeVarRef* refp) { if (refp->access() != VAccess::WRITE) return; AstVarScope* const vscp = refp->varScopep(); - AstVarScope* const newVscp - = vscp->varp()->isContinuously() ? vscp : getForceComponents(vscp).m_valVscp; - AstVarRef* const newpRefp = new AstVarRef{refp->fileline(), newVscp, VAccess::READ}; + FileLine* const flp = new FileLine{refp->fileline()}; + AstVarRef* const newpRefp = new AstVarRef{refp->fileline(), vscp, VAccess::READ}; newpRefp->user2(1); // Don't replace this read ref with the read signal - refp->replaceWith(newpRefp); + if (vscp->varp()->isContinuously()) { + refp->replaceWith(newpRefp); + } else if (isRangedDType(vscp)) { + refp->replaceWith(new AstOr{ + flp, + new AstAnd{ + flp, new AstVarRef{flp, getForceComponents(vscp).m_enVscp, VAccess::READ}, + new AstVarRef{flp, getForceComponents(vscp).m_valVscp, VAccess::READ}}, + new AstAnd{ + flp, + new AstNot{flp, new AstVarRef{flp, getForceComponents(vscp).m_enVscp, + VAccess::READ}}, + newpRefp}}); + } else { + refp->replaceWith(new AstCond{ + flp, new AstVarRef{flp, getForceComponents(vscp).m_enVscp, VAccess::READ}, + new AstVarRef{flp, getForceComponents(vscp).m_valVscp, VAccess::READ}, + newpRefp}); + } VL_DO_DANGLING(refp->deleteTree(), refp); }); - resetEnp->addNext(resetRdp); - relinker.relink(resetEnp); + resetRdp->addNext(resetEnp); + relinker.relink(resetRdp); } void visit(AstVarScope* nodep) override { @@ -314,5 +331,5 @@ void V3Force::forceAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); if (!v3Global.hasForceableSignals()) return; ForceConvertVisitor::apply(nodep); - V3Global::dumpCheckGlobalTree("force", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("force", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp index 640cade21..252a68ce4 100644 --- a/src/V3Gate.cpp +++ b/src/V3Gate.cpp @@ -53,7 +53,7 @@ class GateLogicVertex; class GateVarVertex; class GateGraphBaseVisitor VL_NOT_FINAL { public: - V3Graph* m_graphp; // Graph this class is visiting + V3Graph* const m_graphp; // Graph this class is visiting explicit GateGraphBaseVisitor(V3Graph* graphp) : m_graphp{graphp} {} virtual ~GateGraphBaseVisitor() = default; @@ -199,10 +199,11 @@ private: GateVarRefList m_rhsVarRefs; // VarRefs on rhs of assignment AstNode* m_substTreep = nullptr; // What to replace the variable with // STATE - bool m_buffersOnly; // Set when we only allow simple buffering, no equations (for clocks) + const bool + m_buffersOnly; // Set when we only allow simple buffering, no equations (for clocks) const AstNodeVarRef* m_lhsVarRef = nullptr; // VarRef on lhs of assignment (what we're replacing) - bool m_dedupe; // Set when we use isGateDedupable instead of isGateOptimizable + const bool m_dedupe; // Set when we use isGateDedupable instead of isGateOptimizable int m_ops = 0; // Operation count // METHODS @@ -277,9 +278,9 @@ private: public: // CONSTRUCTORS - GateOkVisitor(AstNode* nodep, bool buffersOnly, bool dedupe) { - m_buffersOnly = buffersOnly; - m_dedupe = dedupe; + GateOkVisitor(AstNode* nodep, bool buffersOnly, bool dedupe) + : m_buffersOnly{buffersOnly} + , m_dedupe{dedupe} { // Iterate iterateConst(nodep); // Check results @@ -332,6 +333,7 @@ private: AstActive* m_activep = nullptr; // Current active bool m_activeReducible = true; // Is activation block reducible? bool m_inSenItem = false; // Underneath AstSenItem; any varrefs are clocks + bool m_inExprStmt = false; // Underneath ExprStmt; don't optimize LHS vars bool m_inSlow = false; // Inside a slow structure std::vector m_optimized; // Logic blocks optimized @@ -421,12 +423,12 @@ private: // VISITORS void visit(AstNetlist* nodep) override { iterateChildren(nodep); - if (dumpGraph() >= 3) m_graph.dumpDotFilePrefixed("gate_pre"); + if (dumpGraphLevel() >= 3) m_graph.dumpDotFilePrefixed("gate_pre"); warnSignals(); // Before loss of sync/async pointers // Decompose clock vectors -- need to do this before removing redundant edges decomposeClkVectors(); m_graph.removeRedundantEdgesSum(&V3GraphEdge::followAlwaysTrue); - if (dumpGraph() >= 6) m_graph.dumpDotFilePrefixed("gate_simp"); + if (dumpGraphLevel() >= 6) m_graph.dumpDotFilePrefixed("gate_simp"); // Find gate interconnect and optimize m_graph.userClearVertices(); // vertex->user(): bool. Indicates we've set it as consumed // Get rid of buffers first, @@ -438,43 +440,42 @@ private: // Remove redundant logic if (v3Global.opt.fDedupe()) { dedupe(); - if (dumpGraph() >= 6) m_graph.dumpDotFilePrefixed("gate_dedup"); + if (dumpGraphLevel() >= 6) m_graph.dumpDotFilePrefixed("gate_dedup"); } if (v3Global.opt.fAssemble()) { mergeAssigns(); - if (dumpGraph() >= 6) m_graph.dumpDotFilePrefixed("gate_assm"); + if (dumpGraphLevel() >= 6) m_graph.dumpDotFilePrefixed("gate_assm"); } // Consumption warnings consumedMark(); - if (dumpGraph() >= 3) m_graph.dumpDotFilePrefixed("gate_opt"); + if (dumpGraphLevel() >= 3) m_graph.dumpDotFilePrefixed("gate_opt"); // Rewrite assignments consumedMove(); } void visit(AstNodeModule* nodep) override { VL_RESTORER(m_modp); - { - m_modp = nodep; - m_activeReducible = true; - iterateChildren(nodep); - } + VL_RESTORER(m_activeReducible); + m_modp = nodep; + m_activeReducible = true; + iterateChildren(nodep); } void visit(AstScope* nodep) override { UINFO(4, " SCOPE " << nodep << endl); + VL_RESTORER(m_scopep); m_scopep = nodep; m_logicVertexp = nullptr; iterateChildren(nodep); - m_scopep = nullptr; } void visit(AstActive* nodep) override { // Create required blocks and add to module UINFO(4, " BLOCK " << nodep << endl); + VL_RESTORER(m_activep); + VL_RESTORER(m_activeReducible); m_activeReducible = !(nodep->hasClocked()); // Seq logic outputs aren't reducible m_activep = nodep; AstNode::user2ClearTree(); iterateChildren(nodep); AstNode::user2ClearTree(); - m_activep = nullptr; - m_activeReducible = true; } void visit(AstNodeVarRef* nodep) override { if (m_scopep) { @@ -498,6 +499,10 @@ private: // the weight will increase if (nodep->access().isWriteOrRW()) { new V3GraphEdge{&m_graph, m_logicVertexp, vvertexp, 1}; + if (m_inExprStmt) { + m_logicVertexp->clearReducibleAndDedupable("LHS var in ExprStmt"); + m_logicVertexp->setConsumed("LHS var in ExprStmt"); + } } if (nodep->access().isReadOrRW()) { new V3GraphEdge{&m_graph, vvertexp, m_logicVertexp, 1}; @@ -506,31 +511,31 @@ private: } void visit(AstAlwaysPublic* nodep) override { VL_RESTORER(m_inSlow); - { - m_inSlow = true; - iterateNewStmt(nodep, "AlwaysPublic", nullptr); - } + m_inSlow = true; + iterateNewStmt(nodep, "AlwaysPublic", nullptr); } void visit(AstCFunc* nodep) override { iterateNewStmt(nodep, "User C Function", "User C Function"); } void visit(AstClocking* nodep) override { iterateNewStmt(nodep, nullptr, nullptr); } + void visit(AstExprStmt* nodep) override { + VL_RESTORER(m_inExprStmt); + m_inExprStmt = true; + iterateChildren(nodep); + } void visit(AstSenItem* nodep) override { + VL_RESTORER(m_inSenItem); m_inSenItem = true; if (m_logicVertexp) { // Already under logic; presumably a SenGate iterateChildren(nodep); } else { // Standalone item, probably right under a SenTree iterateNewStmt(nodep, nullptr, nullptr); } - m_inSenItem = false; } void visit(AstNodeProcedure* nodep) override { VL_RESTORER(m_inSlow); - { - m_inSlow = VN_IS(nodep, Initial) || VN_IS(nodep, Final); - iterateNewStmt(nodep, (nodep->isJustOneBodyStmt() ? nullptr : "Multiple Stmts"), - nullptr); - } + m_inSlow = VN_IS(nodep, Initial) || VN_IS(nodep, Final); + iterateNewStmt(nodep, (nodep->isJustOneBodyStmt() ? nullptr : "Multiple Stmts"), nullptr); } void visit(AstAssignAlias* nodep) override { // iterateNewStmt(nodep, nullptr, nullptr); @@ -543,10 +548,8 @@ private: } void visit(AstTraceDecl* nodep) override { VL_RESTORER(m_inSlow); - { - m_inSlow = true; - iterateNewStmt(nodep, "Tracing", "Tracing"); - } + m_inSlow = true; + iterateNewStmt(nodep, "Tracing", "Tracing"); } void visit(AstConcat* nodep) override { UASSERT_OBJ(!(VN_IS(nodep->backp(), NodeAssign) @@ -1269,9 +1272,7 @@ private: public: explicit GateMergeAssignsGraphVisitor(V3Graph* graphp) - : GateGraphBaseVisitor{graphp} { - m_graphp = graphp; // In base - } + : GateGraphBaseVisitor{graphp} {} void mergeAssignsTree(GateVarVertex* vvertexp) { vvertexp->accept(*this); } VDouble0 numMergedAssigns() { return m_numMergedAssigns; } }; @@ -1370,8 +1371,8 @@ private: if (vsp->user2SetOnce()) return VNUser{0}; UINFO(9, "CLK DECOMP Var - " << vvertexp << " : " << vsp << endl); if (vsp->varp()->width() > 1) { - m_seen_clk_vectors++; - m_total_seen_clk_vectors++; + ++m_seen_clk_vectors; + ++m_total_seen_clk_vectors; } const GateClkDecompState* const currState = reinterpret_cast(vu.c()); GateClkDecompState nextState{currState->m_offset, vsp}; @@ -1488,5 +1489,5 @@ void GateVisitor::decomposeClkVectors() { void V3Gate::gateAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { const GateVisitor visitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("gate", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("gate", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3GraphAcyc.cpp b/src/V3GraphAcyc.cpp index c31485d26..ddae8c48b 100644 --- a/src/V3GraphAcyc.cpp +++ b/src/V3GraphAcyc.cpp @@ -544,28 +544,28 @@ void GraphAcyc::main() { // edges (and thus can't represent loops - if we did the unbreakable // marking right, anyways) buildGraph(m_origGraphp); - if (dumpGraph() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_pre"); + if (dumpGraphLevel() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_pre"); // Perform simple optimizations before any cuttings simplify(false); - if (dumpGraph() >= 5) m_breakGraph.dumpDotFilePrefixed("acyc_simp"); + if (dumpGraphLevel() >= 5) m_breakGraph.dumpDotFilePrefixed("acyc_simp"); UINFO(4, " Cutting trivial loops\n"); simplify(true); - if (dumpGraph() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_mid"); + if (dumpGraphLevel() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_mid"); UINFO(4, " Ranking\n"); m_breakGraph.rank(&V3GraphEdge::followNotCutable); - if (dumpGraph() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_rank"); + if (dumpGraphLevel() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_rank"); UINFO(4, " Placement\n"); place(); - if (dumpGraph() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_place"); + if (dumpGraphLevel() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_place"); UINFO(4, " Final Ranking\n"); // Only needed to assert there are no loops in completed graph m_breakGraph.rank(&V3GraphEdge::followAlwaysTrue); - if (dumpGraph() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_done"); + if (dumpGraphLevel() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_done"); } void V3Graph::acyclic(V3EdgeFuncP edgeFuncp) { diff --git a/src/V3GraphTest.cpp b/src/V3GraphTest.cpp index 8a9637cdf..e90251a58 100644 --- a/src/V3GraphTest.cpp +++ b/src/V3GraphTest.cpp @@ -37,7 +37,7 @@ protected: // Utilities void dumpSelf() { - if (dumpGraph() >= 9) m_graph.dumpDotFilePrefixed("v3graphtest_" + name()); + if (dumpGraphLevel() >= 9) m_graph.dumpDotFilePrefixed("v3graphtest_" + name()); } public: diff --git a/src/V3Hasher.cpp b/src/V3Hasher.cpp index c68f1167e..432f62b17 100644 --- a/src/V3Hasher.cpp +++ b/src/V3Hasher.cpp @@ -307,7 +307,7 @@ private: } void visit(AstNodeModule* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, false, [=]() { // - m_hash += nodep->origName(); + m_hash += nodep->name(); }); } void visit(AstNodePreSel* nodep) override { diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index b2774b60e..43aac0fa4 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -727,5 +727,5 @@ void V3Inline::inlineAll(AstNetlist* nodep) { } { InlineIntfRefVisitor{nodep}; } - V3Global::dumpCheckGlobalTree("inline", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("inline", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index 061e5caeb..0b96463e5 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -517,10 +517,10 @@ public: // otherwise done if (pinVarp->direction() == VDirection::INPUT && cellp->modp()->unconnectedDrive().isSetTrue()) { - pinp->exprp(new AstConst{pinp->fileline(), AstConst::StringToParse{}, "'1"}); + pinp->exprp(new AstConst{pinp->fileline(), AstConst::All1{}}); } else if (pinVarp->direction() == VDirection::INPUT && cellp->modp()->unconnectedDrive().isSetFalse()) { - pinp->exprp(new AstConst{pinp->fileline(), AstConst::StringToParse{}, "'0"}); + pinp->exprp(new AstConst{pinp->fileline(), AstConst::All0{}}); } else { return nullptr; } @@ -616,11 +616,11 @@ void V3Inst::checkOutputShort(AstPin* nodep) { void V3Inst::instAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { InstVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("inst", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("inst", 0, dumpTreeLevel() >= 3); } void V3Inst::dearrayAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { InstDeVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("dearray", 0, dumpTree() >= 6); + V3Global::dumpCheckGlobalTree("dearray", 0, dumpTreeLevel() >= 6); } diff --git a/src/V3Life.cpp b/src/V3Life.cpp index 7ba66e97b..c3ff35eba 100644 --- a/src/V3Life.cpp +++ b/src/V3Life.cpp @@ -515,5 +515,5 @@ void V3Life::lifeAll(AstNetlist* nodep) { LifeState state; LifeTopVisitor{nodep, &state}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("life", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("life", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3LifePost.cpp b/src/V3LifePost.cpp index b3bd4234c..56121b68e 100644 --- a/src/V3LifePost.cpp +++ b/src/V3LifePost.cpp @@ -369,5 +369,5 @@ void V3LifePost::lifepostAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); // Mark redundant AssignPost { LifePostDlyVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("life_post", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("life_post", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index f30bb9601..d44ae8361 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -116,6 +116,7 @@ private: VSymGraph m_mods; // Symbol table of all module names LinkCellsGraph m_graph; // Linked graph of all cell interconnects LibraryVertex* m_libVertexp = nullptr; // Vertex at root of all libraries + int m_dedupNum = 0; // Package dedup number const V3GraphVertex* m_topVertexp = nullptr; // Vertex of top module std::unordered_set m_declfnWarned; // Files we issued DECLFILENAME on string m_origTopModuleName; // original name of the top module @@ -167,7 +168,7 @@ private: iterateChildren(nodep); // Find levels in graph m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); - if (dumpGraph()) m_graph.dumpDotFilePrefixed("linkcells"); + if (dumpGraphLevel()) m_graph.dumpDotFilePrefixed("linkcells"); m_graph.rank(); for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { if (const LinkCellsVertex* const vvertexp = dynamic_cast(itp)) { @@ -504,8 +505,13 @@ private: << "... Location of original declaration\n" << foundp->warnContextSecondary()); } - nodep->unlinkFrBack(); - VL_DO_DANGLING(pushDeletep(nodep), nodep); + if (VN_IS(nodep, Package)) { + // Packages may be imported, we instead rename to be unique + nodep->name(nodep->name() + "__Vdedup" + cvtToStr(m_dedupNum++)); + } else { + nodep->unlinkFrBack(); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } } else if (!foundp) { m_mods.rootp()->insert(nodep->name(), new VSymEnt{&m_mods, nodep}); } diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 498763fac..378942655 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -158,15 +158,13 @@ private: std::array m_scopeAliasMap; // Map of aliases std::vector m_ifaceVarSyms; // List of AstIfaceRefDType's to be imported IfaceModSyms m_ifaceModSyms; // List of AstIface+Symbols to be processed - bool m_forPrimary; // First link - bool m_forPrearray; // Compress cell__[array] refs - bool m_forScopeCreation; // Remove VarXRefs for V3Scope + const VLinkDotStep m_step; // Operational step public: // METHODS void dumpSelf(const string& nameComment = "linkdot", bool force = false) { - if (dump() >= 6 || force) { + if (dumpLevel() >= 6 || force) { const string filename = v3Global.debugFilename(nameComment) + ".txt"; const std::unique_ptr logp{V3File::new_ofstream(filename)}; if (logp->fail()) v3fatal("Can't write " << filename); @@ -193,7 +191,7 @@ public: } void preErrorDump() { static bool diddump = false; - if (!diddump && dumpTree()) { + if (!diddump && dumpTreeLevel()) { diddump = true; dumpSelf("linkdot-preerr", true); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linkdot-preerr.tree")); @@ -202,11 +200,9 @@ public: // CONSTRUCTORS LinkDotState(AstNetlist* rootp, VLinkDotStep step) - : m_syms{rootp} { + : m_syms{rootp} + , m_step(step) { UINFO(4, __FUNCTION__ << ": " << endl); - m_forPrimary = (step == LDS_PRIMARY); - m_forPrearray = (step == LDS_PARAMED || step == LDS_PRIMARY); - m_forScopeCreation = (step == LDS_SCOPED); s_errorThisp = this; V3Error::errorExitCb(preErrorDumpHandler); // If get error, dump self } @@ -217,9 +213,10 @@ public: // ACCESSORS VSymGraph* symsp() { return &m_syms; } - bool forPrimary() const { return m_forPrimary; } - bool forPrearray() const { return m_forPrearray; } - bool forScopeCreation() const { return m_forScopeCreation; } + int stepNumber() const { return int(m_step); } + bool forPrimary() const { return m_step == LDS_PRIMARY; } + bool forPrearray() const { return m_step == LDS_PARAMED || m_step == LDS_PRIMARY; } + bool forScopeCreation() const { return m_step == LDS_SCOPED; } // METHODS static string nodeTextType(AstNode* nodep) { @@ -590,7 +587,7 @@ public: baddot = ident; // So user can see where they botched it okSymp = lookupSymp; string altIdent; - if (m_forPrearray) { + if (forPrearray()) { // GENFOR Begin is foo__BRA__##__KET__ after we've genloop unrolled, // but presently should be just "foo". // Likewise cell foo__[array] before we've expanded arrays is just foo @@ -620,7 +617,7 @@ public: else if (ident == "$root") { lookupSymp = rootEntp(); // We've added the '$root' module, now everything else is one lower - if (!m_forPrearray) { + if (!forPrearray()) { lookupSymp = lookupSymp->findIdFlat(ident); UASSERT(lookupSymp, "Cannot find $root module under netlist"); } @@ -1017,9 +1014,12 @@ class LinkDotFindVisitor final : public VNVisitor { for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { if (VN_IS(stmtp, Var) || VN_IS(stmtp, Foreach)) { std::string name; + const std::string stepStr = m_statep->forPrimary() + ? "" + : std::to_string(m_statep->stepNumber()) + "_"; do { ++m_modBlockNum; - name = "unnamedblk" + cvtToStr(m_modBlockNum); + name = "unnamedblk" + stepStr + cvtToStr(m_modBlockNum); // Increment again if earlier pass of V3LinkDot claimed this name } while (m_curSymp->findIdFlat(name)); nodep->name(name); @@ -1174,6 +1174,7 @@ class LinkDotFindVisitor final : public VNVisitor { } AstVar* const newvarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, varname, VFlagChildDType{}, dtypep}; + newvarp->lifetime(VLifetime::STATIC); nodep->varp(newvarp); iterate(nodep->exprp()); } @@ -1211,7 +1212,8 @@ class LinkDotFindVisitor final : public VNVisitor { return; } const bool nansiBad - = ((findvarp->isDeclTyped() && nodep->isDeclTyped()) + = (((findvarp->isDeclTyped() || findvarp->isNet()) + && (nodep->isDeclTyped() || nodep->isNet())) || (findvarp->isIO() && nodep->isIO())); // e.g. !(output && output) const bool ansiBad = findvarp->isAnsi() || nodep->isAnsi(); // dup illegal with ANSI @@ -2019,9 +2021,10 @@ 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. + std::set m_extendsParam; // Classes that have a parameterized super class + // (except the default instances) + // They are added to the set only in linkDotPrimary. + bool m_insideClassExtParam = false; // Inside a class from m_extendsParam struct DotStates { DotPosition m_dotPos; // Scope part of dotted resolution @@ -2770,49 +2773,59 @@ private: } } } - // 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); - const bool err = !(checkImplicit && m_statep->implicitOk(m_modp, nodep->name())); - if (err) { - if (foundp) { - nodep->v3error("Found definition of '" - << m_ds.m_dotText << (m_ds.m_dotText == "" ? "" : ".") - << nodep->prettyName() << "'" - << " as a " << foundp->nodep()->typeName() - << " but expected a " << expectWhat); - } else if (m_ds.m_dotText == "") { - UINFO(7, " ErrParseRef curSymp=se" << cvtToHex(m_curSymp) - << " ds=" << m_ds.ascii() << endl); - const string suggest = m_statep->suggestSymFallback( - m_ds.m_dotSymp, nodep->name(), VNodeMatcher{}); - nodep->v3error("Can't find definition of " - << expectWhat << ": " << nodep->prettyNameQ() << '\n' - << (suggest.empty() ? "" : nodep->warnMore() + suggest)); - } else { - nodep->v3error("Can't find definition of " - << (!baddot.empty() ? AstNode::prettyNameQ(baddot) - : nodep->prettyNameQ()) - << " in dotted " << expectWhat << ": '" - << m_ds.m_dotText + "." + nodep->prettyName() << "'"); - if (okSymp) { - okSymp->cellErrorScopes(nodep, AstNode::prettyName(m_ds.m_dotText)); + if (!ok) { + if (m_insideClassExtParam) { + // Don't throw error if the 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. + // Mark that the Dot statement can't be resolved. + m_ds.m_unresolvedClass = true; + // If the symbol was a scope name, it would be resolved. + if (m_ds.m_dotPos == DP_SCOPE) m_ds.m_dotPos = DP_MEMBER; + } else { + // 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); + const bool err + = !(checkImplicit && m_statep->implicitOk(m_modp, nodep->name())); + if (err) { + if (foundp) { + nodep->v3error("Found definition of '" + << m_ds.m_dotText << (m_ds.m_dotText == "" ? "" : ".") + << nodep->prettyName() << "'" + << " as a " << foundp->nodep()->typeName() + << " but expected a " << expectWhat); + } else if (m_ds.m_dotText == "") { + UINFO(7, " ErrParseRef curSymp=se" + << cvtToHex(m_curSymp) << " ds=" << m_ds.ascii() << endl); + const string suggest = m_statep->suggestSymFallback( + m_ds.m_dotSymp, nodep->name(), VNodeMatcher{}); + nodep->v3error( + "Can't find definition of " + << expectWhat << ": " << nodep->prettyNameQ() << '\n' + << (suggest.empty() ? "" : nodep->warnMore() + suggest)); + } else { + nodep->v3error("Can't find definition of " + << (!baddot.empty() ? AstNode::prettyNameQ(baddot) + : nodep->prettyNameQ()) + << " in dotted " << expectWhat << ": '" + << m_ds.m_dotText + "." + nodep->prettyName() << "'"); + if (okSymp) { + okSymp->cellErrorScopes(nodep, + AstNode::prettyName(m_ds.m_dotText)); + } } + m_ds.m_dotErr = true; + } + if (checkImplicit) { + // Create if implicit, and also if error (so only complain once) + // Else if a scope is allowed, making a signal won't help error cascade + AstVarRef* const newp + = new AstVarRef{nodep->fileline(), nodep->name(), VAccess::READ}; + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + createImplicitVar(m_curSymp, newp, m_modp, m_modSymp, err); } - m_ds.m_dotErr = true; - } - if (checkImplicit) { - // Create if implicit, and also if error (so only complain once) - // Else if a scope is allowed, making a signal won't help error cascade - AstVarRef* const newp - = new AstVarRef{nodep->fileline(), nodep->name(), VAccess::READ}; - nodep->replaceWith(newp); - VL_DO_DANGLING(pushDeletep(nodep), nodep); - createImplicitVar(m_curSymp, newp, m_modp, m_modSymp, err); } } } @@ -3028,6 +3041,10 @@ private: nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); return; + } else if (m_ds.m_dotp && m_ds.m_dotPos == DP_SCOPE) { + // HERE function() . method_called_on_function_return_value() + m_ds.m_dotPos = DP_MEMBER; + m_ds.m_dotText = ""; } else { checkNoDot(nodep); } @@ -3109,15 +3126,16 @@ private: } } else if (VN_IS(nodep, New) && m_statep->forPrearray()) { // Resolved in V3Width - } else if (nodep->name() == "randomize" || nodep->name() == "srandom") { + } else if (nodep->name() == "randomize" || nodep->name() == "srandom" + || nodep->name() == "get_randstate" + || nodep->name() == "set_randstate") { // Resolved in V3Width } else if (nodep->dotted() == "") { if (nodep->pli()) { if (v3Global.opt.bboxSys()) { AstNode* newp; if (VN_IS(nodep, FuncRef)) { - newp = new AstConst{nodep->fileline(), AstConst::StringToParse{}, - "'0"}; + newp = new AstConst{nodep->fileline(), AstConst::All0{}}; } else { AstNode* outp = nullptr; while (nodep->pinsp()) { @@ -3162,6 +3180,11 @@ private: void visit(AstSelBit* nodep) override { if (nodep->user3SetOnce()) return; iterateAndNextNull(nodep->fromp()); + if (m_ds.m_unresolvedClass) { + UASSERT_OBJ(m_ds.m_dotPos != DP_SCOPE, nodep, + "Object of unresolved class on scope position in dotted reference"); + return; + } if (m_ds.m_dotPos == DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart} UINFO(9, " deferring until after a V3Param pass: " << nodep << endl); @@ -3228,7 +3251,16 @@ private: UINFO(5, " " << nodep << endl); checkNoDot(nodep); if (nodep->isExternDef()) { - if (!m_curSymp->findIdFallback("extern " + nodep->name())) { + if (const VSymEnt* const foundp + = m_curSymp->findIdFallback("extern " + nodep->name())) { + const AstNodeFTask* const funcProtop = VN_AS(foundp->nodep(), NodeFTask); + // Copy specifiers. + // External definition cannot have any specifiers, so no value will be overwritten. + nodep->isHideLocal(funcProtop->isHideLocal()); + nodep->isHideProtected(funcProtop->isHideProtected()); + nodep->isVirtual(funcProtop->isVirtual()); + nodep->lifetime(funcProtop->lifetime()); + } else { nodep->v3error("extern not found that declares " + nodep->prettyNameQ()); } } @@ -3271,6 +3303,86 @@ private: // No checknodot(nodep), visit(AstScope) will check for LambdaArgRef iterateChildren(nodep); } + void visit(AstClassExtends* nodep) override { + // Resolve the symbol and get the class. + // If it is a parameterized case, the class will be resolved after V3Param.cpp + if (nodep->user3SetOnce()) return; + // If the class is resolved, there is nothing more to do + if (nodep->classOrNullp()) return; + if (m_statep->forPrimary()) { + AstNode* cprp = nodep->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 { + 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 + nodep->v3error("Attempting to extend using non-class"); // LCOV_EXCL_LINE + return; + } + VSymEnt* const foundp = lookSymp->findIdFallback(cpackagerefp->name()); + if (foundp) { + if (AstClass* const classp = VN_CAST(foundp->nodep(), Class)) { + AstPin* paramsp = cpackagerefp->paramsp(); + if (paramsp) { + paramsp = paramsp->cloneTree(true); + nodep->parameterized(true); + } + nodep->childDTypep(new AstClassRefDType{nodep->fileline(), classp, paramsp}); + // Link pins + iterate(nodep->childDTypep()); + } else if (AstParamTypeDType* const paramp + = VN_CAST(foundp->nodep(), ParamTypeDType)) { + AstRefDType* const refParamp + = new AstRefDType{nodep->fileline(), paramp->name()}; + refParamp->refDTypep(paramp); + nodep->childDTypep(refParamp); + nodep->parameterized(true); + } else { + nodep->v3warn(E_UNSUPPORTED, + "Unsupported: " << foundp->nodep()->prettyTypeName() + << " in AstClassExtends"); + return; + } + } else { + const string suggest = m_statep->suggestSymFallback( + m_curSymp, cpackagerefp->name(), LinkNodeMatcherClass{}); + cpackagerefp->v3error( + "Class for '" << nodep->verilogKwd() // extends/implements + << "' not found: " << cpackagerefp->prettyNameQ() << '\n' + << (suggest.empty() ? "" : cpackagerefp->warnMore() + suggest)); + return; + } + if (!nodep->childDTypep()) nodep->v3error("Attempting to extend using non-class"); + nodep->classOrPkgsp()->unlinkFrBack()->deleteTree(); + } else { + // Probably a parameter + if (AstRefDType* const refp = VN_CAST(nodep->childDTypep(), RefDType)) { + if (AstClassRefDType* classRefp = VN_CAST(refp->skipRefp(), ClassRefDType)) { + // Resolved to a class reference. + refp->replaceWith(classRefp->cloneTree(false)); + } else { + // Unable to resolve the ref type to a class reference. + // Get the value of type parameter passed to the class instance, + // to print the helpful error message. + const AstNodeDType* typep = refp->refDTypep(); + if (const AstParamTypeDType* const paramp = VN_CAST(typep, ParamTypeDType)) { + typep = paramp->subDTypep(); + } + typep->v3error("Attempting to extend using non-class"); + } + } + } + } void visit(AstClass* nodep) override { if (nodep->user3SetOnce()) return; UINFO(5, " " << nodep << endl); @@ -3293,110 +3405,50 @@ private: cextp->v3error("Multiple inheritance illegal on non-interface classes" " (IEEE 1800-2017 8.13)"); } - if (cextp->childDTypep() || cextp->dtypep()) { + iterate(cextp); + if (m_statep->forPrimary()) { + if (cextp->parameterized()) { + // Parameters in extends statement. + // The class can't be resolved in the current pass. + m_extendsParam.insert(nodep); + m_insideClassExtParam = true; + } + if (AstClassRefDType* const classRefp + = VN_CAST(cextp->childDTypep(), ClassRefDType)) { + AstClass* const classp = classRefp->classp(); + if (classp != nodep) iterate(classp); + if (m_extendsParam.find(classp) != m_extendsParam.end()) { + // One of its super classes has parameters in extends statement. + // Some links may not be resolved in the first pass. + m_extendsParam.insert(nodep); + m_insideClassExtParam = true; + } + } + } + + if (AstClass* const classp = cextp->classOrNullp()) { // 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()); + 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'"); + } + classp->isExtended(true); + nodep->isExtended(true); + iterate(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 { - 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 (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 { - classp = classRefDtypep->classp(); - } - } - } else { - cextp->v3warn(E_UNSUPPORTED, - "Unsupported: " << foundp->nodep()->prettyTypeName() - << " in AstClassExtends"); - } - - if (classp) { - UINFO(8, "Import to " << nodep << " from export class " << classp - << endl); - if (classp == nodep) { - cextp->v3error("Attempting to extend class " - << nodep->prettyNameQ() << " from itself"); - } else if (cextp->isImplements() && !classp->isInterfaceClass()) { - cextp->v3error("Attempting to implement from non-interface class " - << classp->prettyNameQ() << '\n' - << "... Suggest use 'extends'"); - } else if (!cextp->isImplements() && !nodep->isInterfaceClass() - && classp->isInterfaceClass()) { - cextp->v3error("Attempting to extend from interface class " - << classp->prettyNameQ() << '\n' - << "... Suggest use 'implements'"); - } else { - 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; @@ -3424,6 +3476,12 @@ private: if (nodep->user3SetOnce()) return; if (AstNode* const cpackagep = nodep->classOrPackageOpp()) { if (AstClassOrPackageRef* const cpackagerefp = VN_CAST(cpackagep, ClassOrPackageRef)) { + if (cpackagerefp->paramsp()) { + // Unable to link before the instantiation of parameter classes. + // The class reference node has to be visited to properly link parameters. + iterate(cpackagep); + return; + } nodep->classOrPackagep(cpackagerefp->classOrPackagep()); if (!VN_IS(nodep->classOrPackagep(), Class) && !VN_IS(nodep->classOrPackagep(), Package)) { @@ -3483,6 +3541,8 @@ private: nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); return; + } else if (m_insideClassExtParam) { + return; } else { if (foundp) UINFO(1, "Found sym node: " << foundp->nodep() << endl); nodep->v3error("Can't find typedef: " << nodep->prettyNameQ()); @@ -3555,18 +3615,18 @@ public: // Link class functions void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) { - if (debug() >= 5 || dumpTree() >= 9) { + if (debug() >= 5 || dumpTreeLevel() >= 9) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot.tree")); } LinkDotState state(rootp, step); const LinkDotFindVisitor visitor{rootp, &state}; - if (debug() >= 5 || dumpTree() >= 9) { + if (debug() >= 5 || dumpTreeLevel() >= 9) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-find.tree")); } if (step == LDS_PRIMARY || step == LDS_PARAMED) { // Initial link stage, resolve parameters const LinkDotParamVisitor visitors{rootp, &state}; - if (debug() >= 5 || dumpTree() >= 9) { + if (debug() >= 5 || dumpTreeLevel() >= 9) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-param.tree")); } } else if (step == LDS_ARRAYED) { @@ -3575,7 +3635,7 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) { // process AstScope's. This needs to be separate pass after whole hierarchy graph created. const LinkDotScopeVisitor visitors{rootp, &state}; v3Global.assertScoped(true); - if (debug() >= 5 || dumpTree() >= 9) { + if (debug() >= 5 || dumpTreeLevel() >= 9) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-scoped.tree")); } } else { @@ -3592,23 +3652,23 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) { void V3LinkDot::linkDotPrimary(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); linkDotGuts(nodep, LDS_PRIMARY); - V3Global::dumpCheckGlobalTree("linkdot", 0, dumpTree() >= 6); + V3Global::dumpCheckGlobalTree("linkdot", 0, dumpTreeLevel() >= 6); } void V3LinkDot::linkDotParamed(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); linkDotGuts(nodep, LDS_PARAMED); - V3Global::dumpCheckGlobalTree("linkdotparam", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("linkdotparam", 0, dumpTreeLevel() >= 3); } void V3LinkDot::linkDotArrayed(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); linkDotGuts(nodep, LDS_ARRAYED); - V3Global::dumpCheckGlobalTree("linkdot", 0, dumpTree() >= 6); + V3Global::dumpCheckGlobalTree("linkdot", 0, dumpTreeLevel() >= 6); } void V3LinkDot::linkDotScope(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); linkDotGuts(nodep, LDS_SCOPED); - V3Global::dumpCheckGlobalTree("linkdot", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("linkdot", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3LinkInc.cpp b/src/V3LinkInc.cpp index e420d1276..904fd423f 100644 --- a/src/V3LinkInc.cpp +++ b/src/V3LinkInc.cpp @@ -69,7 +69,6 @@ private: // METHODS void insertBeforeStmt(AstNode* nodep, AstNode* newp) { // Return node that must be visited, if any - // See also AstNode::addBeforeStmt; this predates that function if (debug() >= 9) newp->dumpTree("- newstmt: "); UASSERT_OBJ(m_insStmtp, nodep, "Function not underneath a statement"); if (m_insMode == IM_BEFORE) { @@ -310,5 +309,5 @@ public: void V3LinkInc::linkIncrements(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { LinkIncVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("linkinc", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("linkinc", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index 7ac37ade3..de4c1230e 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -351,5 +351,5 @@ public: void V3LinkJump::linkJump(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { LinkJumpVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("linkjump", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("linkjump", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index ca0f07574..e8648fd6a 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -319,7 +319,7 @@ public: void V3LinkLValue::linkLValue(AstNetlist* nodep) { UINFO(4, __FUNCTION__ << ": " << endl); { LinkLValueVisitor{nodep, VAccess::NOCHANGE}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("linklvalue", 0, dumpTree() >= 6); + V3Global::dumpCheckGlobalTree("linklvalue", 0, dumpTreeLevel() >= 6); } void V3LinkLValue::linkLValueSet(AstNode* nodep) { // Called by later link functions when it is known a node needs diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index 11fffed29..609563b99 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -88,7 +88,7 @@ void V3LinkLevel::modSortByLevel() { UASSERT_OBJ(!v3Global.rootp()->modulesp(), v3Global.rootp(), "Unlink didn't work"); for (AstNodeModule* nodep : mods) v3Global.rootp()->addModulesp(nodep); UINFO(9, "modSortByLevel() done\n"); // Comment required for gcc4.6.3 / bug666 - V3Global::dumpCheckGlobalTree("cells", false, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("cells", false, dumpTreeLevel() >= 3); } void V3LinkLevel::timescaling(const ModVec& mods) { @@ -184,7 +184,7 @@ void V3LinkLevel::wrapTop(AstNetlist* rootp) { } } - V3Global::dumpCheckGlobalTree("wraptop", 0, dumpTree() >= 6); + V3Global::dumpCheckGlobalTree("wraptop", 0, dumpTreeLevel() >= 6); } void V3LinkLevel::wrapTopCell(AstNetlist* rootp) { diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 3b7d8eb0a..9da829f30 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -124,13 +124,6 @@ private: && (VN_IS(nodep->stmtsp(), GenIf)) // Begin has if underneath && !nodep->stmtsp()->nextp()); // Has only one item } - bool hasStaticDeclAssignments(AstNodeFTask* nodep) { - for (const AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) { - const AstVar* const varp = VN_CAST(itemp, Var); - if (varp && varp->valuep() && !varp->lifetime().isAutomatic()) return true; - } - return false; - } // VISITs void visit(AstNodeFTask* nodep) override { @@ -156,12 +149,30 @@ private: // DPI-imported functions and properties don't have lifetime specifiers m_lifetime = VLifetime::NONE; } - if (m_lifetime.isStatic() && hasStaticDeclAssignments(nodep)) { - nodep->v3warn( - IMPLICITSTATIC, - "Function/task's lifetime implicitly set to static\n" - << nodep->warnMore() - << "... Suggest use 'function automatic' or 'function static'"); + for (AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) { + AstVar* const varp = VN_CAST(itemp, Var); + if (varp && varp->valuep() && varp->lifetime().isNone() + && m_lifetime.isStatic() && !varp->isIO()) { + if (VN_IS(m_modp, Module)) { + nodep->v3warn(IMPLICITSTATIC, + "Function/task's lifetime implicitly set to static\n" + << nodep->warnMore() + << "... Suggest use 'function automatic' or " + "'function static'\n" + << nodep->warnContextPrimary() << '\n' + << varp->warnOther() + << "... Location of implicit static variable\n" + << varp->warnContextSecondary() << '\n' + << "... Suggest use 'function automatic' or " + "'function static'"); + } else { + varp->v3warn(IMPLICITSTATIC, + "Variable's lifetime implicitly set to static\n" + << nodep->warnMore() + << "... Suggest use 'static' before " + "variable declaration'"); + } + } } nodep->lifetime(m_lifetime); } @@ -240,7 +251,8 @@ private: if (nodep->isGParam() && m_modp) m_modp->hasGParam(true); if (nodep->isParam() && !nodep->valuep() && nodep->fileline()->language() < V3LangCode::L1800_2009) { - nodep->v3error("Parameter requires default value, or use IEEE 1800-2009 or later."); + nodep->v3warn(NEWERSTD, + "Parameter requires default value, or use IEEE 1800-2009 or later."); } if (VN_IS(nodep->subDTypep(), ParseTypeDType)) { // It's a parameter type. Use a different node type for this. @@ -329,6 +341,11 @@ private: } } } + void visit(AstConst* nodep) override { + if (nodep->num().autoExtend() && nodep->fileline()->language() < V3LangCode::L1800_2005) { + nodep->v3warn(NEWERSTD, "Unbased unsized literals require IEEE 1800-2005 or later."); + } + } void visit(AstAttrOf* nodep) override { cleanFileline(nodep); @@ -775,5 +792,5 @@ public: void V3LinkParse::linkParse(AstNetlist* rootp) { UINFO(4, __FUNCTION__ << ": " << endl); { LinkParseVisitor{rootp}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("linkparse", 0, dumpTree() >= 6); + V3Global::dumpCheckGlobalTree("linkparse", 0, dumpTreeLevel() >= 6); } diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index df1872d19..3a917eb02 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -484,5 +484,5 @@ void V3LinkResolve::linkResolve(AstNetlist* rootp) { const LinkResolveVisitor visitor{rootp}; LinkBotupVisitor{rootp}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("linkresolve", 0, dumpTree() >= 6); + V3Global::dumpCheckGlobalTree("linkresolve", 0, dumpTreeLevel() >= 6); } diff --git a/src/V3Localize.cpp b/src/V3Localize.cpp index 1cf60339d..586e8940e 100644 --- a/src/V3Localize.cpp +++ b/src/V3Localize.cpp @@ -57,11 +57,13 @@ private: AstUser4Allocator> m_references; - // STATE + // STATE - across all visitors + std::vector m_varScopeps; // List of variables to consider for localization VDouble0 m_statLocVars; // Statistic tracking + + // STATE - for current visit position (use VL_RESTORER) AstCFunc* m_cfuncp = nullptr; // Current active function uint32_t m_nodeDepth = 0; // Node depth under m_cfuncp - std::vector m_varScopeps; // List of variables to consider for localization // METHODS bool isOptimizable(AstVarScope* nodep) { @@ -133,12 +135,10 @@ private: UINFO(4, " CFUNC " << nodep << endl); VL_RESTORER(m_cfuncp); VL_RESTORER(m_nodeDepth); - { - m_cfuncp = nodep; - m_nodeDepth = 0; - const VNUser2InUse user2InUse; - iterateChildrenConst(nodep); - } + m_cfuncp = nodep; + m_nodeDepth = 0; + const VNUser2InUse user2InUse; + iterateChildrenConst(nodep); } void visit(AstCCall* nodep) override { @@ -204,9 +204,9 @@ private: } void visit(AstNode* nodep) override { + VL_RESTORER(m_nodeDepth); ++m_nodeDepth; iterateChildrenConst(nodep); - --m_nodeDepth; } public: @@ -223,5 +223,5 @@ public: void V3Localize::localizeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { LocalizeVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("localize", 0, dumpTree() >= 6); + V3Global::dumpCheckGlobalTree("localize", 0, dumpTreeLevel() >= 6); } diff --git a/src/V3MergeCond.cpp b/src/V3MergeCond.cpp index d5756cc67..5f57e82c5 100644 --- a/src/V3MergeCond.cpp +++ b/src/V3MergeCond.cpp @@ -888,5 +888,5 @@ public: void V3MergeCond::mergeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { MergeCondVisitor{nodep}; } - V3Global::dumpCheckGlobalTree("merge_cond", 0, dumpTree() >= 6); + V3Global::dumpCheckGlobalTree("merge_cond", 0, dumpTreeLevel() >= 6); } diff --git a/src/V3Mutex.h b/src/V3Mutex.h index b1ee4b021..c5f3883a7 100644 --- a/src/V3Mutex.h +++ b/src/V3Mutex.h @@ -85,6 +85,7 @@ public: /// Construct mutex (without locking it) V3MutexImp() = default; ~V3MutexImp() = default; + VL_UNCOPYABLE(V3MutexImp); const V3MutexImp& operator!() const { return *this; } // For -fthread_safety /// Acquire/lock mutex void lock() VL_ACQUIRE() VL_MT_SAFE { @@ -137,23 +138,10 @@ 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(); + 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; diff --git a/src/V3Name.cpp b/src/V3Name.cpp index bec3dd9cc..cf4bec4f0 100644 --- a/src/V3Name.cpp +++ b/src/V3Name.cpp @@ -42,8 +42,8 @@ private: // AstVar::user1() -> bool. Set true if already processed const VNUser1InUse m_inuser1; - // STATE - const AstNodeModule* m_modp = nullptr; + // STATE - for current visit position (use VL_RESTORER) + const AstNodeModule* m_modp = nullptr; // Current module // METHODS void rename(AstNode* nodep, bool addPvt) { @@ -150,5 +150,5 @@ public: void V3Name::nameAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { NameVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("name", 0, dumpTree() >= 6); + V3Global::dumpCheckGlobalTree("name", 0, dumpTreeLevel() >= 6); } diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 3b8a26040..219c8e30c 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -23,6 +23,7 @@ #include "V3Error.h" #include "V3File.h" #include "V3Global.h" +#include "V3Mutex.h" #include "V3OptionParser.h" #include "V3Os.h" #include "V3PreShell.h" @@ -547,6 +548,11 @@ string V3Options::filePath(FileLine* fl, const string& modname, const string& la // Find a filename to read the specified module name, // using the incdir and libext's. // Return "" if not found. + if (modname[0] == '/') { + // If leading /, obey existing absolute path, so can find getStdPackagePath() + string exists = filePathCheckOneDir(modname, ""); + if (exists != "") return exists; + } for (const string& dir : m_impp->m_incDirUsers) { string exists = filePathCheckOneDir(modname, dir); if (exists != "") return exists; @@ -1273,6 +1279,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char DECL_OPTION("-l2-name", Set, &m_l2Name); DECL_OPTION("-no-l2name", CbCall, [this]() { m_l2Name = ""; }).undocumented(); // Historical DECL_OPTION("-l2name", CbCall, [this]() { m_l2Name = "v"; }).undocumented(); // Historical + DECL_OPTION("-main-top-name", Set, &m_mainTopName); DECL_OPTION("-MAKEFLAGS", CbVal, callStrSetter(&V3Options::addMakeFlags)); DECL_OPTION("-MMD", OnOff, &m_makeDepend); diff --git a/src/V3Options.h b/src/V3Options.h index 360c28b53..fea00aaf8 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -22,7 +22,6 @@ #include "V3Error.h" #include "V3LangCode.h" -#include "V3Mutex.h" #include #include @@ -331,6 +330,7 @@ private: string m_flags; // main switch: -f {name} string m_l2Name; // main switch: --l2name; "" for top-module's name string m_libCreate; // main switch: --lib-create {lib_name} + string m_mainTopName; // main switch: --main-top-name string m_makeDir; // main switch: -Mdir string m_modPrefix; // main switch: --mod-prefix string m_pipeFilter; // main switch: --pipe-filter @@ -568,6 +568,7 @@ public: } return libName; } + string mainTopName() const { return m_mainTopName; } string makeDir() const VL_MT_SAFE { return m_makeDir; } string modPrefix() const VL_MT_SAFE { return m_modPrefix; } string pipeFilter() const { return m_pipeFilter; } diff --git a/src/V3Order.cpp b/src/V3Order.cpp index dc1905f97..ae0365019 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -1214,9 +1214,11 @@ AstActive* OrderProcess::processMoveOneLogic(const OrderLogicVertex* lvertexp, // Process procedures per statement (unless profCFuncs), so we can split CFuncs within // procedures. Everything else is handled in one go bool suspendable = false; + bool needProcess = false; bool slow = m_slow; if (AstNodeProcedure* const procp = VN_CAST(nodep, NodeProcedure)) { suspendable = procp->isSuspendable(); + needProcess = procp->needProcess(); if (suspendable) slow = slow && !VN_IS(procp, Always); nodep = procp->stmtsp(); pushDeletep(procp); @@ -1241,6 +1243,7 @@ AstActive* OrderProcess::processMoveOneLogic(const OrderLogicVertex* lvertexp, const string name = cfuncName(modp, domainp, scopep, nodep); newFuncpr = new AstCFunc{nodep->fileline(), name, scopep, suspendable ? "VlCoroutine" : ""}; + if (needProcess) newFuncpr->setNeedProcess(); newFuncpr->isStatic(false); newFuncpr->isLoose(true); newFuncpr->slow(slow); @@ -1416,7 +1419,7 @@ void OrderProcess::processMTasks() { void OrderProcess::process(bool multiThreaded) { // Dump data - if (dumpGraph()) m_graph.dumpDotFilePrefixed(m_tag + "_orderg_pre"); + if (dumpGraphLevel()) m_graph.dumpDotFilePrefixed(m_tag + "_orderg_pre"); // Break cycles. Each strongly connected subgraph (including cutable // edges) will have its own color, and corresponds to a loop in the @@ -1424,27 +1427,27 @@ void OrderProcess::process(bool multiThreaded) { // edges are actually still there, just with weight 0). UINFO(2, " Acyclic & Order...\n"); m_graph.acyclic(&V3GraphEdge::followAlwaysTrue); - if (dumpGraph()) m_graph.dumpDotFilePrefixed(m_tag + "_orderg_acyc"); + if (dumpGraphLevel()) m_graph.dumpDotFilePrefixed(m_tag + "_orderg_acyc"); // Assign ranks so we know what to follow // Then, sort vertices and edges by that ordering m_graph.order(); - if (dumpGraph()) m_graph.dumpDotFilePrefixed(m_tag + "_orderg_order"); + if (dumpGraphLevel()) m_graph.dumpDotFilePrefixed(m_tag + "_orderg_order"); // Assign logic vertices to new domains UINFO(2, " Domains...\n"); processDomains(); - if (dumpGraph()) m_graph.dumpDotFilePrefixed(m_tag + "_orderg_domain"); + if (dumpGraphLevel()) m_graph.dumpDotFilePrefixed(m_tag + "_orderg_domain"); - if (dump()) processEdgeReport(); + if (dumpLevel()) processEdgeReport(); if (!multiThreaded) { UINFO(2, " Construct Move Graph...\n"); processMoveBuildGraph(); // Different prefix (ordermv) as it's not the same graph - if (dumpGraph() >= 4) m_pomGraph.dumpDotFilePrefixed(m_tag + "_ordermv_start"); + if (dumpGraphLevel() >= 4) m_pomGraph.dumpDotFilePrefixed(m_tag + "_ordermv_start"); m_pomGraph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); - if (dumpGraph() >= 4) m_pomGraph.dumpDotFilePrefixed(m_tag + "_ordermv_simpl"); + if (dumpGraphLevel() >= 4) m_pomGraph.dumpDotFilePrefixed(m_tag + "_ordermv_simpl"); UINFO(2, " Move...\n"); processMove(); @@ -1454,7 +1457,7 @@ void OrderProcess::process(bool multiThreaded) { } // Dump data - if (dumpGraph()) m_graph.dumpDotFilePrefixed(m_tag + "_orderg_done"); + if (dumpGraphLevel()) m_graph.dumpDotFilePrefixed(m_tag + "_orderg_done"); } //###################################################################### diff --git a/src/V3Os.cpp b/src/V3Os.cpp index 4a80f98ac..3fb7ec5ee 100644 --- a/src/V3Os.cpp +++ b/src/V3Os.cpp @@ -126,7 +126,7 @@ void V3Os::setenvStr(const string& envvar, const string& value, const string& wh string V3Os::filenameFromDirBase(const string& dir, const string& basename) { // Don't return ./{filename} because if filename was absolute, that makes it relative - if (dir == ".") { + if (dir.empty() || dir == ".") { return basename; } else { return dir + "/" + basename; diff --git a/src/V3PairingHeap.h b/src/V3PairingHeap.h index 494b6da99..9e6bc041a 100644 --- a/src/V3PairingHeap.h +++ b/src/V3PairingHeap.h @@ -26,7 +26,7 @@ // Pairing heap (max-heap) with increase key and delete. // // While this is written as a generic data structure, it's interface and -// implementation is finely tuned for it's use by V3Parm_tition, and is critical +// implementation is finely tuned for use by V3Partition, and is critical // to Verilation performance, so be very careful changing anything or adding any // new operations that would impact either memory usage, or performance of the // existing operations. This data structure is fully deterministic, meaning diff --git a/src/V3Param.cpp b/src/V3Param.cpp index e15d38834..ba39ef6e6 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -221,7 +221,7 @@ public: //###################################################################### // Remove parameters from cells and build new modules -class ParamProcessor final { +class ParamProcessor final : public VNDeleter { // NODE STATE - Local // AstVar::user4() // int Global parameter number (for naming new module) // // (0=not processed, 1=iterated, but no number, @@ -257,8 +257,10 @@ class ParamProcessor final { // Generated modules by this visitor is not included V3StringSet m_allModuleNames; - using ValueMapValue = std::pair; - std::map m_valueMap; // Hash of node hash to (param value, name) + CloneMap m_originalParams; // Map between parameters of copied parameteized classes and their + // original nodes + + std::map m_valueMap; // Hash of node hash to param value int m_nextValue = 1; // Next value to use in m_valueMap const AstNodeModule* m_modp = nullptr; // Current module being processed @@ -304,8 +306,10 @@ class ParamProcessor final { return st; } - static string paramValueKey(const AstNode* nodep) { - if (const AstRefDType* const refp = VN_CAST(nodep, RefDType)) { nodep = refp->skipRefp(); } + static string paramValueString(const AstNode* nodep) { + if (const AstRefDType* const refp = VN_CAST(nodep, RefDType)) { + nodep = refp->skipRefToEnump(); + } string key = nodep->name(); if (const AstIfaceRefDType* const ifrtp = VN_CAST(nodep, IfaceRefDType)) { if (ifrtp->cellp() && ifrtp->cellp()->modp()) { @@ -322,13 +326,13 @@ class ParamProcessor final { key += " {"; for (const AstNode* memberp = dtypep->membersp(); memberp; memberp = memberp->nextp()) { - key += paramValueKey(memberp); + key += paramValueString(memberp); key += ";"; } key += "}"; } else if (const AstMemberDType* const dtypep = VN_CAST(nodep, MemberDType)) { key += " "; - key += paramValueKey(dtypep->subDTypep()); + key += paramValueString(dtypep->subDTypep()); } else if (const AstBasicDType* const dtypep = VN_CAST(nodep, BasicDType)) { if (dtypep->isSigned()) { key += " signed"; } if (dtypep->isRanged()) { @@ -342,20 +346,22 @@ 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); + if (const AstRefDType* const refp = VN_CAST(nodep, RefDType)) { + nodep = refp->skipRefToEnump(); + } + const string paramStr = paramValueString(nodep); // cppcheck-has-bug-suppress unreadVariable - V3Hash hash = V3Hasher::uncachedHash(nodep); + V3Hash hash = V3Hasher::uncachedHash(nodep) + paramStr; // Force hash collisions -- for testing only // cppcheck-has-bug-suppress unreadVariable - if (VL_UNLIKELY(v3Global.opt.debugCollision())) hash = V3Hash{}; + if (VL_UNLIKELY(v3Global.opt.debugCollision())) hash = V3Hash{paramStr}; int num; const auto it = m_valueMap.find(hash); - if (it != m_valueMap.end() && it->second.second == key) { - num = it->second.first; + if (it != m_valueMap.end()) { + num = it->second; } else { num = m_nextValue++; - m_valueMap[hash] = std::make_pair(num, key); + m_valueMap[hash] = num; } return std::string{"z"} + cvtToStr(num); } @@ -388,23 +394,20 @@ class ParamProcessor final { } return nullptr; } - void collectPins(CloneMap* clonemapp, AstNodeModule* modp) { + void collectPins(CloneMap* clonemapp, AstNodeModule* modp, bool originalIsCopy) { // Grab all I/O so we can remap our pins later for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + const AstNode* originalParamp = nullptr; if (AstVar* const varp = VN_CAST(stmtp, Var)) { if (varp->isIO() || varp->isGParam() || varp->isIfaceRef()) { // Cloning saved a pointer to the new node for us, so just follow that link. - const AstVar* const oldvarp = varp->clonep(); - // UINFO(8,"Clone list 0x"< 0x"<<(uint32_t)varp<emplace(oldvarp, varp); + originalParamp = varp->clonep(); } } else if (AstParamTypeDType* const ptp = VN_CAST(stmtp, ParamTypeDType)) { - if (ptp->isGParam()) { - const AstParamTypeDType* const oldptp = ptp->clonep(); - clonemapp->emplace(oldptp, ptp); - } + if (ptp->isGParam()) originalParamp = ptp->clonep(); } + if (originalIsCopy) originalParamp = m_originalParams[originalParamp]; + clonemapp->emplace(originalParamp, stmtp); } } void relinkPins(const CloneMap* clonemapp, AstPin* startpinp) { @@ -546,9 +549,8 @@ 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); + // Self references linked in the first pass of V3LinkDot.cpp should point to the default + // instance. if (AstClassRefDType* const classRefp = VN_CAST(nodep, ClassRefDType)) { if (classRefp->classp() == oldClassp) classRefp->classp(newClassp); } else if (AstClassOrPackageRef* const classRefp = VN_CAST(nodep, ClassOrPackageRef)) { @@ -566,7 +568,13 @@ class ParamProcessor final { // Deep clone of new module // 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); + AstNodeModule* newmodp; + if (srcModp->user2p()) { + newmodp = VN_CAST(srcModp->user2p()->cloneTree(false), NodeModule); + } else { + newmodp = srcModp->cloneTree(false); + } + if (AstClass* const newClassp = VN_CAST(newmodp, Class)) { newClassp->isParameterized(false); replaceRefsRecurse(newmodp->stmtsp(), newClassp, VN_AS(srcModp, Class)); @@ -604,7 +612,7 @@ class ParamProcessor final { // Grab all I/O so we can remap our pins later // Note we allow multiple users of a parameterized model, // thus we need to stash this info. - collectPins(clonemapp, newmodp); + collectPins(clonemapp, newmodp, srcModp->user2p()); // Relink parameter vars to the new module relinkPins(clonemapp, paramsp); // Fix any interface references @@ -788,6 +796,18 @@ class ParamProcessor final { } } + void storeOriginalParams(AstClass* const classp) { + for (AstNode* stmtp = classp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + AstNode* originalParamp = nullptr; + if (AstVar* const varp = VN_CAST(stmtp, Var)) { + if (varp->isGParam()) originalParamp = varp->clonep(); + } else if (AstParamTypeDType* const ptp = VN_CAST(stmtp, ParamTypeDType)) { + if (ptp->isGParam()) originalParamp = ptp->clonep(); + } + if (originalParamp) m_originalParams[stmtp] = originalParamp; + } + } + bool nodeDeparamCommon(AstNode* nodep, AstNodeModule*& srcModpr, AstPin* paramsp, AstPin* pinsp, bool any_overrides) { // Make sure constification worked @@ -810,9 +830,16 @@ 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); + // If it's the first use of the default instance, create a copy and store it in user2p. + // user2p will also be used to check if the default instance is used. + if (!srcModpr->user2p() && VN_IS(srcModpr, Class)) { + AstClass* classCopyp = VN_AS(srcModpr, Class)->cloneTree(false); + // It is a temporary copy of the original class node, stored in order to create + // another instances. It is needed only during class instantiation. + pushDeletep(classCopyp); + srcModpr->user2p(classCopyp); + storeOriginalParams(classCopyp); + } } else if (AstNodeModule* const paramedModp = m_hierBlocks.findByParams(srcModpr->name(), paramsp, m_modp)) { paramedModp->dead(false); @@ -920,7 +947,11 @@ public: class ParamVisitor final : public VNVisitor { // NODE STATE // AstNodeModule::user1 -> bool: already fixed level - // AstClass::user2 -> bool: Referenced (value read only in parameterized classes) + // AstClass::user2p -> AstClass*: Unchanged copy of the parameterized class node. + // The class node may be modified according to parameter + // values and an unchanged copy is needed to instantiate + // classes with different parameters. + // STATE ParamProcessor m_processor; // De-parameterize a cell, build modules UnrollStateful m_unroller; // Loop unroller @@ -1378,8 +1409,8 @@ public: for (AstNodeModule* const modp : modps) netlistp->addModulesp(modp); for (AstClass* const classp : m_paramClasses) { - if (!classp->user2()) { - // Unreferenced, so it can be removed + if (!classp->user2p()) { + // The default value isn't referenced, so it can be removed VL_DO_DANGLING(pushDeletep(classp->unlinkFrBack()), classp); } else { // Referenced. classp became a specialized class with the default @@ -1399,5 +1430,5 @@ public: void V3Param::param(AstNetlist* rootp) { UINFO(2, __FUNCTION__ << ": " << endl); { ParamVisitor{rootp}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("param", 0, dumpTree() >= 6); + V3Global::dumpCheckGlobalTree("param", 0, dumpTreeLevel() >= 6); } diff --git a/src/V3ParseGrammar.cpp b/src/V3ParseGrammar.cpp index b60e6bb07..f2f7f7949 100644 --- a/src/V3ParseGrammar.cpp +++ b/src/V3ParseGrammar.cpp @@ -85,9 +85,9 @@ AstArg* V3ParseGrammar::argWrapList(AstNodeExpr* nodep) { } AstNode* V3ParseGrammar::createSupplyExpr(FileLine* fileline, const string& name, int value) { - AstAssignW* assignp - = new AstAssignW{fileline, new AstVarRef{fileline, name, VAccess::WRITE}, - new AstConst{fileline, AstConst::StringToParse{}, (value ? "'1" : "'0")}}; + AstAssignW* assignp = new AstAssignW{fileline, new AstVarRef{fileline, name, VAccess::WRITE}, + value ? new AstConst{fileline, AstConst::All1{}} + : new AstConst{fileline, AstConst::All0{}}}; AstStrengthSpec* strengthSpecp = new AstStrengthSpec{fileline, VStrength::SUPPLY, VStrength::SUPPLY}; assignp->strengthSpecp(strengthSpecp); diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index cbf1808d5..c7e2e62b4 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -35,6 +35,7 @@ #include "V3Os.h" #include "V3ParseBison.h" // Generated by bison #include "V3PreShell.h" +#include "V3Stats.h" #include @@ -247,24 +248,22 @@ size_t V3ParseImp::ppInputToLex(char* buf, size_t max_size) { return got; } -void V3ParseImp::preprocDumps(std::ostream& os) { - if (v3Global.opt.dumpDefines()) { - V3PreShell::dumpDefines(os); - } else { - const bool noblanks = v3Global.opt.preprocOnly() && v3Global.opt.preprocNoLine(); - for (auto& buf : m_ppBuffers) { - if (noblanks) { - bool blank = true; - for (string::iterator its = buf.begin(); its != buf.end(); ++its) { - if (!std::isspace(*its) && *its != '\n') { - blank = false; - break; - } +void V3ParseImp::preprocDumps(std::ostream& os, bool forInputs) { + bool noblanks = forInputs || (v3Global.opt.preprocOnly() && v3Global.opt.preprocNoLine()); + bool nolines = forInputs; + for (auto& buf : m_ppBuffers) { + if (noblanks) { + bool blank = true; + for (string::iterator its = buf.begin(); its != buf.end(); ++its) { + if (!std::isspace(*its) && *its != '\n') { + blank = false; + break; } - if (blank) continue; } - os << buf; + if (blank) continue; + if (nolines && buf.rfind("`line ", 0) == 0) continue; } + os << buf; } } @@ -292,7 +291,7 @@ void V3ParseImp::parseFile(FileLine* fileline, const string& modfilename, bool i if (v3Global.opt.preprocOnly() || v3Global.opt.keepTempFiles()) { // Create output file with all the preprocessor output we buffered up const string vppfilename = v3Global.opt.hierTopDataDir() + "/" + v3Global.opt.prefix() - + "_" + nondirname + ".vpp"; + + "__" + nondirname + ".vpp"; std::ofstream* ofp = nullptr; std::ostream* osp; if (v3Global.opt.preprocOnly()) { @@ -303,15 +302,20 @@ void V3ParseImp::parseFile(FileLine* fileline, const string& modfilename, bool i if (osp->fail()) { fileline->v3error("Cannot write preprocessor output: " + vppfilename); return; + } + if (v3Global.opt.dumpDefines()) { + V3PreShell::dumpDefines(*osp); } else { - preprocDumps(*osp); - if (ofp) { - ofp->close(); - VL_DO_DANGLING(delete ofp, ofp); - } + preprocDumps(*osp, false); + } + if (ofp) { + ofp->close(); + VL_DO_DANGLING(delete ofp, ofp); } } + if (debug() && modfilename != V3Options::getStdPackagePath()) dumpInputsFile(); + // Parse it if (!v3Global.opt.preprocOnly()) { lexFile(modfilename); @@ -320,6 +324,31 @@ void V3ParseImp::parseFile(FileLine* fileline, const string& modfilename, bool i } } +void V3ParseImp::dumpInputsFile() { + // Create output file with joined preprocessor output we buffered up, + // Useful for debug to feed back into Verilator + static bool append = false; + const string vppfilename + = v3Global.opt.hierTopDataDir() + "/" + v3Global.opt.prefix() + "__inputs.vpp"; + std::ofstream* ofp = V3File::new_ofstream(vppfilename, append); + if (ofp->fail()) { + v3error("Cannot write preprocessor output: " + vppfilename); + return; + } + if (!append) { + append = true; + UINFO(1, "Writing all preprocessed output to " << vppfilename << endl); + *ofp << "// Dump of all post-preprocessor input\n"; + *ofp << "// Blank lines and `line directives have been removed\n"; + *ofp << "//\n"; + V3Stats::infoHeader(*ofp, "// "); + } + *ofp << "\n"; + preprocDumps(*ofp, true); + ofp->close(); + VL_DO_DANGLING(delete ofp, ofp); +} + void V3ParseImp::lexFile(const string& modname) { // Prepare for lexing UINFO(3, "Lexing " << modname << endl); diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index e1037b130..bba341102 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -295,6 +295,7 @@ public: void parseFile(FileLine* fileline, const string& modfilename, bool inLibrary, const string& errmsg); + void dumpInputsFile(); private: void lexFile(const string& modname); @@ -305,7 +306,7 @@ private: size_t tokenPipeScanParam(size_t depth); size_t tokenPipeScanType(size_t depth); const V3ParseBisonYYSType* tokenPeekp(size_t depth); - void preprocDumps(std::ostream& os); + void preprocDumps(std::ostream& os, bool forInputs); }; #endif // Guard diff --git a/src/V3Partition.cpp b/src/V3Partition.cpp index a10ffec1c..41b41ba51 100644 --- a/src/V3Partition.cpp +++ b/src/V3Partition.cpp @@ -2463,7 +2463,7 @@ public: } } - if (dumpGraph() >= 4) schedule.dumpDotFilePrefixedAlways(mtaskGraph, "schedule"); + if (dumpGraphLevel() >= 4) schedule.dumpDotFilePrefixedAlways(mtaskGraph, "schedule"); return schedule; } @@ -2528,7 +2528,7 @@ private: // V3Partition implementation void V3Partition::debugMTaskGraphStats(const V3Graph* graphp, const string& stage) { - if (!debug() && !dump() && !dumpGraph()) return; + if (!debug() && !dumpLevel() && !dumpGraphLevel()) return; UINFO(4, "\n"); UINFO(4, " Stats for " << stage << endl); @@ -2565,7 +2565,7 @@ void V3Partition::debugMTaskGraphStats(const V3Graph* graphp, const string& stag if (mtaskCount < 1000) { string filePrefix("ordermv_"); filePrefix += stage; - if (dumpGraph() >= 4) graphp->dumpDotFilePrefixedAlways(filePrefix); + if (dumpGraphLevel() >= 4) graphp->dumpDotFilePrefixedAlways(filePrefix); } // Look only at the cost of each mtask, neglect communication cost. @@ -2740,7 +2740,7 @@ void V3Partition::go(V3Graph* mtasksp) { // For debug: print out the longest critical path. This allows us to // verify that the costs look reasonable, that we aren't combining // nodes that should probably be split, etc. - if (dump() >= 3) LogicMTask::dumpCpFilePrefixed(mtasksp, "cp"); + if (dumpLevel() >= 3) LogicMTask::dumpCpFilePrefixed(mtasksp, "cp"); // Merge nodes that could present data hazards; see comment within. { @@ -3227,6 +3227,7 @@ void V3Partition::finalize(AstNetlist* netlistp) { } void V3Partition::selfTest() { + UINFO(2, __FUNCTION__ << ": " << endl); PartPropagateCpSelfTest::selfTest(); PartPackMTasks::selfTest(); PartContraction::selfTest(); diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index 5cc257f7e..2a3d09db1 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -53,16 +53,17 @@ private: const VNUser1InUse m_inuser1; const VNUser2InUse m_inuser2; - // STATE + // STATE - across all visitors + V3UniqueNames m_tempNames; // For generating unique temporary variable names + VDouble0 m_extractedToConstPool; // Statistic tracking + + // STATE - for current visit position (use VL_RESTORER) AstCFunc* m_cfuncp = nullptr; // Current block AstNode* m_stmtp = nullptr; // Current statement AstCCall* m_callp = nullptr; // Current AstCCall AstWhile* m_inWhilep = nullptr; // Inside while loop, special statement additions AstTraceInc* m_inTracep = nullptr; // Inside while loop, special statement additions bool m_assignLhs = false; // Inside assignment lhs, don't breakup extracts - V3UniqueNames m_tempNames; // For generating unique temporary variable names - - VDouble0 m_extractedToConstPool; // Statistic tracking // METHODS bool assignNoTemp(AstNodeAssign* nodep) { @@ -157,30 +158,38 @@ private: } void visit(AstCFunc* nodep) override { VL_RESTORER(m_cfuncp); - { - m_cfuncp = nodep; - m_tempNames.reset(); - iterateChildren(nodep); - } + m_cfuncp = nodep; + m_tempNames.reset(); + iterateChildren(nodep); } + +#define RESTORER_START_STATEMENT() \ + VL_RESTORER(m_assignLhs); \ + VL_RESTORER(m_stmtp); + + // Must use RESTORER_START_STATEMENT() in visitors using this void startStatement(AstNode* nodep) { m_assignLhs = false; if (m_cfuncp) m_stmtp = nodep; } + void visit(AstWhile* nodep) override { UINFO(4, " WHILE " << nodep << endl); + RESTORER_START_STATEMENT(); startStatement(nodep); iterateAndNextNull(nodep->precondsp()); startStatement(nodep); - m_inWhilep = nodep; - iterateAndNextNull(nodep->condp()); - m_inWhilep = nullptr; + { + VL_RESTORER(m_inWhilep); + m_inWhilep = nodep; + iterateAndNextNull(nodep->condp()); + } startStatement(nodep); iterateAndNextNull(nodep->stmtsp()); iterateAndNextNull(nodep->incsp()); - m_stmtp = nullptr; } void visit(AstNodeAssign* nodep) override { + RESTORER_START_STATEMENT(); startStatement(nodep); { bool noopt = false; @@ -202,23 +211,24 @@ private: } } iterateAndNextNull(nodep->rhsp()); - m_assignLhs = true; - iterateAndNextNull(nodep->lhsp()); - m_assignLhs = false; - m_stmtp = nullptr; + { + VL_RESTORER(m_assignLhs); + m_assignLhs = true; + iterateAndNextNull(nodep->lhsp()); + } } void visit(AstNodeStmt* nodep) override { UINFO(4, " STMT " << nodep << endl); + RESTORER_START_STATEMENT(); startStatement(nodep); iterateChildren(nodep); - m_stmtp = nullptr; } void visit(AstTraceInc* nodep) override { + RESTORER_START_STATEMENT(); startStatement(nodep); + VL_RESTORER(m_inTracep); m_inTracep = nodep; iterateChildren(nodep); - m_inTracep = nullptr; - m_stmtp = nullptr; } void visitShift(AstNodeBiop* nodep) { // Shifts of > 32/64 bits in C++ will wrap-around and generate non-0s @@ -345,9 +355,9 @@ private: // Autoflush void visit(AstDisplay* nodep) override { + RESTORER_START_STATEMENT(); startStatement(nodep); iterateChildren(nodep); - m_stmtp = nullptr; if (v3Global.opt.autoflush()) { const AstNode* searchp = nodep->nextp(); while (searchp && VN_IS(searchp, Comment)) searchp = searchp->nextp(); @@ -400,5 +410,5 @@ public: void V3Premit::premitAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { PremitVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("premit", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("premit", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 34c90bf9a..6f88a2372 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -47,6 +47,7 @@ private: using BaseToDerivedMap = std::unordered_map; BaseToDerivedMap m_baseToDerivedMap; // Mapping from base classes to classes that extend them + AstClass* m_classp = nullptr; // Current class // METHODS void markMembers(AstClass* nodep) { @@ -87,6 +88,8 @@ private: // VISITORS void visit(AstClass* nodep) override { + VL_RESTORER(m_classp); + m_classp = nodep; iterateChildrenConst(nodep); if (nodep->extendsp()) { // Save pointer to derived class @@ -104,6 +107,11 @@ private: markMembers(classp); } } + void visit(AstNodeFTaskRef* nodep) override { + iterateChildrenConst(nodep); + if (nodep->name() != "randomize") return; + if (m_classp) m_classp->user1(true); + } void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } @@ -368,7 +376,7 @@ void V3Randomize::randomizeNetlist(AstNetlist* nodep) { const RandomizeMarkVisitor markVisitor{nodep}; RandomizeVisitor{nodep}; } - V3Global::dumpCheckGlobalTree("randomize", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("randomize", 0, dumpTreeLevel() >= 3); } AstFunc* V3Randomize::newRandomizeFunc(AstClass* nodep) { diff --git a/src/V3Reloop.cpp b/src/V3Reloop.cpp index bd2d110c0..f71d8913b 100644 --- a/src/V3Reloop.cpp +++ b/src/V3Reloop.cpp @@ -273,5 +273,5 @@ public: void V3Reloop::reloopAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { ReloopVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("reloop", 0, dumpTree() >= 6); + V3Global::dumpCheckGlobalTree("reloop", 0, dumpTreeLevel() >= 6); } diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index f9ac9cc8e..78f09d44b 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -247,6 +247,7 @@ void orderSequentially(AstCFunc* funcp, const LogicByScope& lbs) { subFuncp = createNewSubFuncp(scopep); subFuncp->name(subFuncp->name() + "__" + cvtToStr(scopep->user2Inc())); subFuncp->rtnType("VlCoroutine"); + if (procp->needProcess()) subFuncp->setNeedProcess(); if (VN_IS(procp, Always)) { subFuncp->slow(false); FileLine* const flp = procp->fileline(); @@ -1225,7 +1226,7 @@ void schedule(AstNetlist* netlistp) { netlistp->dpiExportTriggerp(nullptr); - V3Global::dumpCheckGlobalTree("sched", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("sched", 0, dumpTreeLevel() >= 3); } } // namespace V3Sched diff --git a/src/V3SchedAcyclic.cpp b/src/V3SchedAcyclic.cpp index ac9bec353..20cfc4c7b 100644 --- a/src/V3SchedAcyclic.cpp +++ b/src/V3SchedAcyclic.cpp @@ -403,7 +403,7 @@ LogicByScope breakCycles(AstNetlist* netlistp, LogicByScope& combinationalLogic) if (graphp->empty()) return LogicByScope{}; // Dump for debug - if (dumpGraph() >= 6) graphp->dumpDotFilePrefixed("sched-comb-cycles"); + if (dumpGraphLevel() >= 6) graphp->dumpDotFilePrefixed("sched-comb-cycles"); // Make graph acyclic by cutting some edges. Note: This also colors strongly connected // components which reportCycles uses to print each SCCs separately. diff --git a/src/V3SchedPartition.cpp b/src/V3SchedPartition.cpp index 849ff16e7..1f3a0075b 100644 --- a/src/V3SchedPartition.cpp +++ b/src/V3SchedPartition.cpp @@ -341,11 +341,11 @@ LogicRegions partition(LogicByScope& clockedLogic, LogicByScope& combinationalLo // Build the graph const std::unique_ptr graphp = SchedGraphBuilder::build(clockedLogic, combinationalLogic, hybridLogic); - if (dumpGraph() >= 6) graphp->dumpDotFilePrefixed("sched"); + if (dumpGraphLevel() >= 6) graphp->dumpDotFilePrefixed("sched"); // Partition into Active and NBA regions colorActiveRegion(*(graphp.get())); - if (dumpGraph() >= 6) graphp->dumpDotFilePrefixed("sched-partitioned", true); + if (dumpGraphLevel() >= 6) graphp->dumpDotFilePrefixed("sched-partitioned", true); LogicRegions result; diff --git a/src/V3SchedReplicate.cpp b/src/V3SchedReplicate.cpp index 7db10cabf..961f0e29f 100644 --- a/src/V3SchedReplicate.cpp +++ b/src/V3SchedReplicate.cpp @@ -261,13 +261,13 @@ LogicReplicas replicateLogic(LogicRegions& logicRegionsRegions) { // Build the dataflow (dependency) graph const std::unique_ptr graphp = buildGraph(logicRegionsRegions); // Dump for debug - if (dumpGraph() >= 6) graphp->dumpDotFilePrefixed("sched-replicate"); + if (dumpGraphLevel() >= 6) graphp->dumpDotFilePrefixed("sched-replicate"); // Propagate driving region flags for (V3GraphVertex* vtxp = graphp->verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) { propagateDrivingRegions(static_cast(vtxp)); } // Dump for debug - if (dumpGraph() >= 6) graphp->dumpDotFilePrefixed("sched-replicate-propagated"); + if (dumpGraphLevel() >= 6) graphp->dumpDotFilePrefixed("sched-replicate-propagated"); // Replicate the necessary logic return replicate(graphp.get()); } diff --git a/src/V3SchedTiming.cpp b/src/V3SchedTiming.cpp index 0b3da2451..ec816bc70 100644 --- a/src/V3SchedTiming.cpp +++ b/src/V3SchedTiming.cpp @@ -155,6 +155,19 @@ TimingKit prepareTiming(AstNetlist* const netlistp) { std::vector m_writtenBySuspendable; // METHODS + // Add arguments to a resume() call based on arguments in the suspending call + void addResumePins(AstCMethodHard* const resumep, AstNodeExpr* pinsp) { + AstCExpr* const exprp = VN_CAST(pinsp, CExpr); + AstText* const textp = VN_CAST(exprp->exprsp(), Text); + if (textp) { + // The first argument, vlProcess, isn't used by any of resume() methods, skip it + if ((pinsp = VN_CAST(pinsp->nextp(), NodeExpr))) { + resumep->addPinsp(pinsp->cloneTree(false)); + } + } else { + resumep->addPinsp(pinsp->cloneTree(false)); + } + } // Create an active with a timing scheduler resume() call void createResumeActive(AstCAwait* const awaitp) { auto* const methodp = VN_AS(awaitp->exprp(), CMethodHard); @@ -166,7 +179,14 @@ TimingKit prepareTiming(AstNetlist* const netlistp) { flp, new AstVarRef{flp, schedulerp, VAccess::READWRITE}, "resume"}; resumep->dtypeSetVoid(); if (schedulerp->dtypep()->basicp()->isTriggerScheduler()) { - if (methodp->pinsp()) resumep->addPinsp(methodp->pinsp()->cloneTree(false)); + UASSERT_OBJ(methodp->pinsp(), methodp, + "Trigger method should have pins from V3Timing"); + // The first pin is the commit boolean, the rest (if any) should be debug info + // See V3Timing for details + if (AstNode* const dbginfop = methodp->pinsp()->nextp()) { + if (methodp->pinsp()) + addResumePins(resumep, static_cast(dbginfop)); + } } else if (schedulerp->dtypep()->basicp()->isDynamicTriggerScheduler()) { auto* const postp = resumep->cloneTree(false); postp->name("doPostUpdates"); @@ -256,6 +276,7 @@ void transformForks(AstNetlist* const netlistp) { // STATE bool m_inClass = false; // Are we in a class? bool m_beginHasAwaits = false; // Does the current begin have awaits? + bool m_beginNeedProcess = false; // Does the current begin have process::self dependency? AstFork* m_forkp = nullptr; // Current fork AstCFunc* m_funcp = nullptr; // Current function @@ -342,8 +363,9 @@ void transformForks(AstNetlist* const netlistp) { UASSERT_OBJ(m_forkp, nodep, "Begin outside of a fork"); // Start with children, so later we only find awaits that are actually in this begin m_beginHasAwaits = false; + m_beginNeedProcess = false; iterateChildrenConst(nodep); - if (m_beginHasAwaits) { + if (m_beginHasAwaits || m_beginNeedProcess) { UASSERT_OBJ(!nodep->name().empty(), nodep, "Begin needs a name"); // Create a function to put this begin's statements in FileLine* const flp = nodep->fileline(); @@ -366,9 +388,19 @@ void transformForks(AstNetlist* const netlistp) { } // Put the begin's statements in the function, delete the begin newfuncp->addStmtsp(nodep->stmtsp()->unlinkFrBackWithNext()); + if (m_beginNeedProcess) { + newfuncp->setNeedProcess(); + newfuncp->addStmtsp(new AstCStmt{nodep->fileline(), + "vlProcess->state(VlProcess::FINISHED);\n"}); + } + if (!m_beginHasAwaits) { + // co_return at the end (either that or a co_await is required in a coroutine + newfuncp->addStmtsp(new AstCStmt{nodep->fileline(), "co_return;\n"}); + } remapLocals(newfuncp, callp); } else { - // No awaits, just inline the forked process + // The begin has neither awaits nor a process::self call, just inline the + // statements nodep->replaceWith(nodep->stmtsp()->unlinkFrBackWithNext()); } VL_DO_DANGLING(nodep->deleteTree(), nodep); @@ -377,6 +409,10 @@ void transformForks(AstNetlist* const netlistp) { m_beginHasAwaits = true; iterateChildrenConst(nodep); } + void visit(AstCCall* nodep) override { + if (nodep->funcp()->needProcess()) m_beginNeedProcess = true; + iterateChildrenConst(nodep); + } //-------------------- void visit(AstNodeExpr*) override {} // Accelerate @@ -388,7 +424,7 @@ void transformForks(AstNetlist* const netlistp) { ~ForkVisitor() override = default; }; ForkVisitor{netlistp}; - V3Global::dumpCheckGlobalTree("sched_forks", 0, dumpTree() >= 6); + V3Global::dumpCheckGlobalTree("sched_forks", 0, dumpTreeLevel() >= 6); } } // namespace V3Sched diff --git a/src/V3Scope.cpp b/src/V3Scope.cpp index 2dd807015..b5a31ea51 100644 --- a/src/V3Scope.cpp +++ b/src/V3Scope.cpp @@ -423,5 +423,5 @@ void V3Scope::scopeAll(AstNetlist* nodep) { const ScopeVisitor visitor{nodep}; ScopeCleanupVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("scope", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("scope", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 05fac3e7a..fa28b67b8 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -880,6 +880,15 @@ private: checkNodeInfo(nodep); iterateChildrenConst(nodep); } + void visit(AstExprStmt* nodep) override { + if (jumpingOver(nodep)) return; + checkNodeInfo(nodep); + iterateAndNextConstNull(nodep->stmtsp()); + if (!optimizable()) return; + iterateAndNextConstNull(nodep->resultp()); + if (!optimizable()) return; + if (!m_checkOnly) newValue(nodep, fetchValue(nodep->resultp())); + } void visit(AstJumpBlock* nodep) override { if (jumpingOver(nodep)) return; diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index 4c5a392cd..e4aa7407f 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -112,7 +112,8 @@ class SliceVisitor final : public VNVisitor { : 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)) { + || VN_IS(nodep, CMethodHard) || VN_IS(nodep, MemberSel) + || VN_IS(nodep, ExprStmt)) { UINFO(9, " cloneSel(" << elements << "," << offset << ") " << nodep << endl); const int leOffset = !arrayp->rangep()->ascending() ? arrayp->rangep()->elementsConst() - 1 - offset @@ -247,5 +248,5 @@ public: void V3Slice::sliceAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { SliceVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("slice", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("slice", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Split.cpp b/src/V3Split.cpp index 88cd94652..c937deb08 100644 --- a/src/V3Split.cpp +++ b/src/V3Split.cpp @@ -456,7 +456,7 @@ protected: UINFO(5, "ReorderBlock " << nodep << endl); m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); - if (dumpGraph() >= 9) m_graph.dumpDotFilePrefixed("reorderg_nodup", false); + if (dumpGraphLevel() >= 9) m_graph.dumpDotFilePrefixed("reorderg_nodup", false); // Mark all the logic for this step // Vertex::m_user begin: true indicates logic for this step @@ -513,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("splitg_preo", false); + if (dumpGraphLevel() >= 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("splitg_opt", false); + if (dumpGraphLevel() >= 9) m_graph.dumpDotFilePrefixed("splitg_opt", false); } void reorderBlock(AstNode* nodep) { @@ -942,14 +942,14 @@ protected: } } - if (dumpGraph() >= 9) m_graph.dumpDotFilePrefixed("splitg_nodup", false); + if (dumpGraphLevel() >= 9) m_graph.dumpDotFilePrefixed("splitg_nodup", false); // Weak coloring to determine what needs to remain grouped // in a single always. This follows all edges excluding: // - those we pruned above // - PostEdges, which are done later m_graph.weaklyConnected(&SplitEdge::followScoreboard); - if (dumpGraph() >= 9) m_graph.dumpDotFilePrefixed("splitg_colored", false); + if (dumpGraphLevel() >= 9) m_graph.dumpDotFilePrefixed("splitg_colored", false); } void visit(AstAlways* nodep) override { @@ -1003,10 +1003,10 @@ private: void V3Split::splitReorderAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { ReorderVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("reorder", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("reorder", 0, dumpTreeLevel() >= 3); } void V3Split::splitAlwaysAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { SplitVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("split", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("split", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3SplitAs.cpp b/src/V3SplitAs.cpp index b8c82f80d..24bf6f831 100644 --- a/src/V3SplitAs.cpp +++ b/src/V3SplitAs.cpp @@ -34,19 +34,12 @@ VL_DEFINE_DEBUG_FUNCTIONS; -//###################################################################### - -class SplitAsBaseVisitor VL_NOT_FINAL : public VNVisitor { -public: - // METHODS -}; - //###################################################################### // Find all split variables in a block -class SplitAsFindVisitor final : public SplitAsBaseVisitor { +class SplitAsFindVisitor final : public VNVisitor { private: - // STATE + // STATE - across all visitors AstVarScope* m_splitVscp = nullptr; // Variable we want to split // METHODS @@ -55,6 +48,12 @@ private: m_splitVscp = nodep->varScopep(); } } + void visit(AstExprStmt* nodep) override { + // A function call inside the splitting assignment + // We need to presume the whole call is preserved (if the upper statement is) + // This will break if the m_splitVscp is a "ref" argument to the function, + // but little we can do. + } void visit(AstNode* nodep) override { iterateChildren(nodep); } public: @@ -68,11 +67,12 @@ public: //###################################################################### // Remove nodes not containing proper references -class SplitAsCleanVisitor final : public SplitAsBaseVisitor { +class SplitAsCleanVisitor final : public VNVisitor { private: - // STATE - AstVarScope* const m_splitVscp; // Variable we want to split + // STATE - across all visitors + const AstVarScope* const m_splitVscp; // Variable we want to split const bool m_modeMatch; // Remove matching Vscp, else non-matching + // STATE - for current visit position (use VL_RESTORER) bool m_keepStmt = false; // Current Statement must be preserved bool m_matches = false; // Statement below has matching lvalue reference @@ -89,6 +89,7 @@ private: UINFO(6, " CL STMT " << nodep << endl); const bool oldKeep = m_keepStmt; { + VL_RESTORER(m_matches); m_matches = false; m_keepStmt = false; @@ -107,6 +108,12 @@ private: m_keepStmt = oldKeep || m_keepStmt; UINFO(9, " upKeep=" << m_keepStmt << " STMT " << nodep << endl); } + void visit(AstExprStmt* nodep) override { + // A function call inside the splitting assignment + // We need to presume the whole call is preserved (if the upper statement is) + // This will break if the m_splitVscp is a "ref" argument to the function, + // but little we can do. + } void visit(AstNode* nodep) override { iterateChildren(nodep); } public: @@ -122,31 +129,28 @@ public: //###################################################################### // SplitAs class functions -class SplitAsVisitor final : public SplitAsBaseVisitor { +class SplitAsVisitor final : public VNVisitor { private: // NODE STATE // AstAlways::user() -> bool. True if already processed const VNUser1InUse m_inuser1; - // STATE + // STATE - across all visitors VDouble0 m_statSplits; // Statistic tracking - AstVarScope* m_splitVscp = nullptr; // Variable we want to split // METHODS - void splitAlways(AstAlways* nodep) { - UINFO(3, "Split " << nodep << endl); - UINFO(3, " For " << m_splitVscp << endl); + void splitAlways(AstAlways* nodep, AstVarScope* splitVscp) { if (debug() >= 9) nodep->dumpTree("- in: "); // Duplicate it and link in AstAlways* const newp = nodep->cloneTree(false); newp->user1(true); // So we don't clone it again nodep->addNextHere(newp); { // Delete stuff we don't want in old - const SplitAsCleanVisitor visitor{nodep, m_splitVscp, false}; + const SplitAsCleanVisitor visitor{nodep, splitVscp, false}; if (debug() >= 9) nodep->dumpTree("- out0: "); } { // Delete stuff we don't want in new - const SplitAsCleanVisitor visitor{newp, m_splitVscp, true}; + const SplitAsCleanVisitor visitor{newp, splitVscp, true}; if (debug() >= 9) newp->dumpTree("- out1: "); } } @@ -158,16 +162,18 @@ private: while (!nodep->user1()) { // Find any splittable variables const SplitAsFindVisitor visitor{nodep}; - m_splitVscp = visitor.splitVscp(); - if (m_splitVscp && m_splitVscp == lastSplitVscp) { - // We did this last time! Something's stuck! - nodep->v3fatalSrc("Infinite loop in isolate_assignments removal for: " - << m_splitVscp->prettyNameQ()); - } - lastSplitVscp = m_splitVscp; + AstVarScope* const splitVscp = visitor.splitVscp(); // Now isolate the always - if (m_splitVscp) { - splitAlways(nodep); + if (splitVscp) { + UINFO(3, "Split " << nodep << endl); + UINFO(3, " For " << splitVscp << endl); + if (splitVscp == lastSplitVscp) { + // We did this last time! Something's stuck! + nodep->v3fatalSrc("Infinite loop in isolate_assignments removal for: " + << splitVscp->prettyNameQ()); + } + lastSplitVscp = splitVscp; + splitAlways(nodep, splitVscp); ++m_statSplits; } else { nodep->user1(true); @@ -192,5 +198,5 @@ public: void V3SplitAs::splitAsAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { SplitAsVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("splitas", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("splitas", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3SplitVar.cpp b/src/V3SplitVar.cpp index 4550ee310..dc62dc823 100644 --- a/src/V3SplitVar.cpp +++ b/src/V3SplitVar.cpp @@ -1249,9 +1249,9 @@ void V3SplitVar::splitVariable(AstNetlist* nodep) { const SplitUnpackedVarVisitor visitor{nodep}; refs = visitor.getPackedVarRefs(); } - V3Global::dumpCheckGlobalTree("split_var", 0, dumpTree() >= 9); + V3Global::dumpCheckGlobalTree("split_var", 0, dumpTreeLevel() >= 9); { SplitPackedVarVisitor{nodep, refs}; } - V3Global::dumpCheckGlobalTree("split_var", 0, dumpTree() >= 9); + V3Global::dumpCheckGlobalTree("split_var", 0, dumpTreeLevel() >= 9); } bool V3SplitVar::canSplitVar(const AstVar* varp) { diff --git a/src/V3Stats.h b/src/V3Stats.h index fb56fbfe8..97c61050b 100644 --- a/src/V3Stats.h +++ b/src/V3Stats.h @@ -121,6 +121,8 @@ public: static void statsFinalAll(AstNetlist* nodep); /// Called by the top level to dump the statistics static void statsReport(); + /// Called by debug dumps + static void infoHeader(std::ofstream& os, const string& prefix); }; #endif // Guard diff --git a/src/V3StatsReport.cpp b/src/V3StatsReport.cpp index d72f13c1c..c725a40f5 100644 --- a/src/V3StatsReport.cpp +++ b/src/V3StatsReport.cpp @@ -40,18 +40,8 @@ class StatsReport final { std::ofstream& os; ///< Output stream static StatColl s_allStats; ///< All statistics - void header() { - os << "Verilator Statistics Report\n\n"; - - os << "Information:\n"; - os << " " << V3Options::version() << '\n'; - os << " Arguments: " << v3Global.opt.allArgsString() << '\n'; - os << " Build jobs: " << v3Global.opt.buildJobs() << '\n'; - os << " Verilate jobs: " << v3Global.opt.verilateJobs() << '\n'; - os << '\n'; - } - void sumit() { + os << '\n'; // If sumit is set on a statistic, combine with others of same name std::multimap byName; // * is always first @@ -179,7 +169,8 @@ public: // CONSTRUCTORS explicit StatsReport(std::ofstream* aofp) : os(*aofp) { // Need () or GCC 4.8 false warning - header(); + os << "Verilator Statistics Report\n\n"; + V3Stats::infoHeader(os, ""); sumit(); stars(); stages(); @@ -222,6 +213,14 @@ void V3Stats::statsStage(const string& name) { V3Stats::addStatPerf("Stage, Memory (MB), " + digitName, memory); } +void V3Stats::infoHeader(std::ofstream& os, const string& prefix) { + os << prefix << "Information:\n"; + os << prefix << " " << V3Options::version() << '\n'; + os << prefix << " Arguments: " << v3Global.opt.allArgsString() << '\n'; + os << prefix << " Build jobs: " << v3Global.opt.buildJobs() << '\n'; + os << prefix << " Verilate jobs: " << v3Global.opt.verilateJobs() << '\n'; +} + void V3Stats::statsReport() { UINFO(2, __FUNCTION__ << ": " << endl); diff --git a/src/V3Subst.cpp b/src/V3Subst.cpp index 05dec8dba..a83a4cf93 100644 --- a/src/V3Subst.cpp +++ b/src/V3Subst.cpp @@ -384,5 +384,5 @@ public: void V3Subst::substituteAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { SubstVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("subst", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("subst", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3SymTable.h b/src/V3SymTable.h index 9d723a15e..482753a5c 100644 --- a/src/V3SymTable.h +++ b/src/V3SymTable.h @@ -314,7 +314,7 @@ public: } } void dumpFilePrefixed(const string& nameComment) { - if (dumpTree()) { + if (dumpTreeLevel()) { const string filename = v3Global.debugFilename(nameComment) + ".txt"; UINFO(2, "Dumping " << filename << endl); const std::unique_ptr logp{V3File::new_ofstream(filename)}; diff --git a/src/V3TSP.cpp b/src/V3TSP.cpp index 2ee4a27ca..f8961e5eb 100644 --- a/src/V3TSP.cpp +++ b/src/V3TSP.cpp @@ -396,7 +396,7 @@ public: } } void dumpGraphFilePrefixed(const string& nameComment) const { - if (::dump()) { + if (dumpLevel()) { const string filename = v3Global.debugFilename(nameComment) + ".txt"; const std::unique_ptr logp{V3File::new_ofstream(filename)}; if (logp->fail()) v3fatal("Can't write " << filename); @@ -406,7 +406,7 @@ public: void findEulerTour(std::vector* sortedOutp) { UASSERT(sortedOutp->empty(), "Output graph must start empty"); - if (::dumpGraph() >= 6) dumpDotFilePrefixed("findEulerTour"); + if (::dumpGraphLevel() >= 6) dumpDotFilePrefixed("findEulerTour"); std::unordered_set markedEdges; // Pick a start node Vertex* const start_vertexp = castVertexp(verticesBeginp()); @@ -464,12 +464,12 @@ void V3TSP::tspSort(const V3TSP::StateVec& states, V3TSP::StateVec* resultp) { // Create the minimum spanning tree Graph minGraph; graph.makeMinSpanningTree(&minGraph); - if (dumpGraph() >= 6) minGraph.dumpGraphFilePrefixed("minGraph"); + if (dumpGraphLevel() >= 6) minGraph.dumpGraphFilePrefixed("minGraph"); const std::vector oddDegree = minGraph.getOddDegreeKeys(); Graph matching; graph.perfectMatching(oddDegree, &matching); - if (dumpGraph() >= 6) matching.dumpGraphFilePrefixed("matching"); + if (dumpGraphLevel() >= 6) matching.dumpGraphFilePrefixed("matching"); // Adds edges to minGraph, the resulting graph will have even number of // edge counts at every vertex: @@ -670,12 +670,12 @@ void V3TSP::selfTestString() { Graph minGraph; graph.makeMinSpanningTree(&minGraph); - if (dumpGraph() >= 6) minGraph.dumpGraphFilePrefixed("minGraph"); + if (dumpGraphLevel() >= 6) minGraph.dumpGraphFilePrefixed("minGraph"); const std::vector oddDegree = minGraph.getOddDegreeKeys(); Graph matching; graph.perfectMatching(oddDegree, &matching); - if (dumpGraph() >= 6) matching.dumpGraphFilePrefixed("matching"); + if (dumpGraphLevel() >= 6) matching.dumpGraphFilePrefixed("matching"); minGraph.combineGraph(matching); diff --git a/src/V3Table.cpp b/src/V3Table.cpp index 56b754b04..26f37f970 100644 --- a/src/V3Table.cpp +++ b/src/V3Table.cpp @@ -431,5 +431,5 @@ void TableSimulateVisitor::varRefCb(AstVarRef* nodep) { void V3Table::tableAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { TableVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("table", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("table", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 94c45b035..5f1e195ce 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -283,7 +283,7 @@ public: iterate(nodep); // m_callGraph.removeRedundantEdgesSum(&TaskEdge::followAlwaysTrue); - if (dumpGraph()) m_callGraph.dumpDotFilePrefixed("task_call"); + if (dumpGraphLevel()) m_callGraph.dumpDotFilePrefixed("task_call"); } ~TaskStateVisitor() override = default; VL_UNCOPYABLE(TaskStateVisitor); @@ -1316,6 +1316,9 @@ private: } } + // Mark the fact that this function allocates std::process + if (nodep->isFromStd() && nodep->name() == "self") cfuncp->setNeedProcess(); + // Delete rest of cloned task and return new func VL_DO_DANGLING(pushDeletep(nodep), nodep); if (debug() >= 9) cfuncp->dumpTree("- userFunc: "); @@ -1335,7 +1338,6 @@ private: } AstNode* insertBeforeStmt(AstNode* nodep, AstNode* newp) { // Return node that must be visited, if any - // See also AstNode::addBeforeStmt; this predates that function if (debug() >= 9) nodep->dumpTree("- newstmt: "); UASSERT_OBJ(m_insStmtp, nodep, "Function not underneath a statement"); AstNode* visitp = nullptr; @@ -1372,6 +1374,15 @@ private: iterateChildren(nodep); } } + void visit(AstWith* nodep) override { + if (nodep->user1SetOnce()) { + // Make sure that the return expression is converted only once + return; + } + AstNodeExpr* const withExprp = VN_AS(nodep->exprp()->unlinkFrBack(), NodeExpr); + nodep->addExprp(new AstCReturn{withExprp->fileline(), withExprp}); + iterateChildren(nodep); + } void visit(AstScope* nodep) override { m_scopep = nodep; m_insStmtp = nullptr; @@ -1820,5 +1831,5 @@ void V3Task::taskAll(AstNetlist* nodep) { TaskStateVisitor visitors{nodep}; const TaskVisitor visitor{nodep, &visitors}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("task", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("task", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3ThreadPool.cpp b/src/V3ThreadPool.cpp index 47750a093..63469681a 100644 --- a/src/V3ThreadPool.cpp +++ b/src/V3ThreadPool.cpp @@ -23,29 +23,33 @@ // c++11 requires definition of static constexpr as well as declaration constexpr unsigned int V3ThreadPool::FUTUREWAITFOR_MS; -void V3ThreadPool::resize(unsigned n) VL_MT_UNSAFE { +void V3ThreadPool::resize(unsigned n) VL_MT_UNSAFE VL_EXCLUDES(m_mutex) + VL_EXCLUDES(m_stoppedJobsMutex) { // This function is not thread-safe and can result in race between threads 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; - m_stoppedJobs = 0; - m_cv.notify_all(); - m_stoppedJobsCV.notify_all(); - stoppedJobsLock.unlock(); - lock.unlock(); + { + V3LockGuard lock{m_mutex}; + V3LockGuard stoppedJobsLock{m_stoppedJobsMutex}; + + UASSERT(m_queue.empty(), "Resizing busy thread pool"); + // Shut down old threads + m_shutdown = true; + m_stoppedJobs = 0; + m_cv.notify_all(); + m_stoppedJobsCV.notify_all(); + } while (!m_workers.empty()) { m_workers.front().join(); m_workers.pop_front(); } - lock.lock(); - // Start new threads - m_shutdown = false; - for (unsigned int i = 1; i < n; ++i) { - m_workers.emplace_back(&V3ThreadPool::startWorker, this, i); + if (n > 1) { + V3LockGuard lock{m_mutex}; + // Start new threads + m_shutdown = false; + for (unsigned int i = 1; i < n; ++i) { + m_workers.emplace_back(&V3ThreadPool::startWorker, this, i); + } } } @@ -60,11 +64,11 @@ void V3ThreadPool::workerJobLoop(int id) VL_MT_SAFE { job_t job; { V3LockGuard lock(m_mutex); - m_cv.wait(lock, [&]() VL_REQUIRES(m_mutex) { + m_cv.wait(m_mutex, [&]() VL_REQUIRES(m_mutex) { return !m_queue.empty() || m_shutdown || m_stopRequested; }); if (m_shutdown) return; // Terminate if requested - if (stopRequestedStandalone()) { continue; } + if (stopRequested()) { continue; } // Get the job UASSERT(!m_queue.empty(), "Job should be available"); @@ -100,9 +104,9 @@ void V3ThreadPool::requestExclusiveAccess(const V3ThreadPool::job_t&& exclusiveA V3LockGuard stoppedJobLock{m_stoppedJobsMutex}; // if some other job already requested exclusive access // wait until it stops - if (stopRequested()) { waitStopRequested(stoppedJobLock); } + if (stopRequested()) { waitStopRequested(); } m_stopRequested = true; - waitOtherThreads(stoppedJobLock); + waitOtherThreads(); m_exclusiveAccess = true; exclusiveAccessJob(); m_exclusiveAccess = false; @@ -111,28 +115,29 @@ void V3ThreadPool::requestExclusiveAccess(const V3ThreadPool::job_t&& exclusiveA } } -bool V3ThreadPool::waitIfStopRequested() VL_MT_SAFE { - V3LockGuard stoppedJobLock(m_stoppedJobsMutex); +bool V3ThreadPool::waitIfStopRequested() VL_MT_SAFE VL_EXCLUDES(m_stoppedJobsMutex) { if (!stopRequested()) return false; - waitStopRequested(stoppedJobLock); + V3LockGuard stoppedJobLock(m_stoppedJobsMutex); + waitStopRequested(); return true; } -void V3ThreadPool::waitStopRequested(V3LockGuard& stoppedJobLock) VL_REQUIRES(m_stoppedJobsMutex) { +void V3ThreadPool::waitStopRequested() VL_REQUIRES(m_stoppedJobsMutex) { ++m_stoppedJobs; m_stoppedJobsCV.notify_all(); - m_stoppedJobsCV.wait( - stoppedJobLock, [&]() VL_REQUIRES(m_stoppedJobsMutex) { return !m_stopRequested.load(); }); + m_stoppedJobsCV.wait(m_stoppedJobsMutex, [&]() VL_REQUIRES(m_stoppedJobsMutex) { + return !m_stopRequested.load(); + }); --m_stoppedJobs; m_stoppedJobsCV.notify_all(); } -void V3ThreadPool::waitOtherThreads(V3LockGuard& stoppedJobLock) VL_MT_SAFE_EXCLUDES(m_mutex) +void V3ThreadPool::waitOtherThreads() VL_MT_SAFE_EXCLUDES(m_mutex) VL_REQUIRES(m_stoppedJobsMutex) { ++m_stoppedJobs; m_stoppedJobsCV.notify_all(); m_cv.notify_all(); - m_stoppedJobsCV.wait(stoppedJobLock, [&]() VL_REQUIRES(m_stoppedJobsMutex) { + m_stoppedJobsCV.wait(m_stoppedJobsMutex, [&]() VL_REQUIRES(m_stoppedJobsMutex) { // count also the main thread return m_stoppedJobs == (m_workers.size() + 1); }); @@ -152,19 +157,20 @@ void V3ThreadPool::selfTest() { }); }; auto secondJob = [&](int sleep) -> void { - V3LockGuard lock{commonMutex}; - lock.unlock(); + commonMutex.lock(); + commonMutex.unlock(); s().waitIfStopRequested(); - lock.lock(); + V3LockGuard lock{commonMutex}; std::this_thread::sleep_for(std::chrono::milliseconds{sleep}); commonValue = 1000; }; auto thirdJob = [&](int sleep) -> void { - V3LockGuard lock{commonMutex}; - std::this_thread::sleep_for(std::chrono::milliseconds{sleep}); - lock.unlock(); + { + V3LockGuard lock{commonMutex}; + std::this_thread::sleep_for(std::chrono::milliseconds{sleep}); + } s().requestExclusiveAccess([&]() { firstJob(sleep); }); - lock.lock(); + V3LockGuard lock{commonMutex}; commonValue = 1000; }; std::list> futures; @@ -183,8 +189,10 @@ void V3ThreadPool::selfTest() { futures.push_back(s().enqueue(std::bind(thirdJob, 100))); futures.push_back(s().enqueue(std::bind(thirdJob, 100))); 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)); diff --git a/src/V3ThreadPool.h b/src/V3ThreadPool.h index 69e093fcc..d87d5be17 100644 --- a/src/V3ThreadPool.h +++ b/src/V3ThreadPool.h @@ -27,19 +27,6 @@ #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 { @@ -67,9 +54,10 @@ class V3ThreadPool final { // CONSTRUCTORS V3ThreadPool() = default; ~V3ThreadPool() { - V3LockGuard lock{m_mutex}; - m_queue = {}; // make sure queue is empty - lock.unlock(); + { + V3LockGuard lock{m_mutex}; + m_queue = {}; // make sure queue is empty + } resize(0); } @@ -82,7 +70,7 @@ public: } // Resize thread pool to n workers (queue must be empty) - void resize(unsigned n) VL_MT_UNSAFE; + void resize(unsigned n) VL_MT_UNSAFE VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_stoppedJobsMutex); // Enqueue a job for asynchronous execution // Due to missing support for lambda annotations in c++11, @@ -105,7 +93,7 @@ public: // Check if other thread requested exclusive access to processing, // if so, it waits for it to complete. Afterwards it is resumed. // Returns true if request was send and we waited, otherwise false - bool waitIfStopRequested() VL_MT_SAFE; + bool waitIfStopRequested() VL_MT_SAFE VL_EXCLUDES(m_stoppedJobsMutex); // Waits for future. // This function can be interupted by exclusive access request. @@ -123,8 +111,7 @@ public: // 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) { + static auto waitForFutures(std::list>& futures) { return waitForFuturesImp(futures); } @@ -132,9 +119,8 @@ public: private: template - static typename future_type::return_type::type - waitForFuturesImp(std::list>& futures) { - typename future_type::return_type::type results; + static std::list waitForFuturesImp(std::list>& futures) { + std::list results; while (!futures.empty()) { results.push_back(V3ThreadPool::waitForFuture(futures.front())); futures.pop_front(); @@ -153,23 +139,17 @@ private: } // True when any thread requested exclusive access - bool stopRequested() const VL_REQUIRES(m_stoppedJobsMutex) { + bool stopRequested() const VL_MT_SAFE { // don't wait if shutdown already requested if (m_shutdown) return false; return m_stopRequested; } - bool stopRequestedStandalone() VL_MT_SAFE_EXCLUDES(m_stoppedJobsMutex) { - const V3LockGuard lock{m_stoppedJobsMutex}; - return stopRequested(); - } - // Waits until exclusive access job completes its job - void waitStopRequested(V3LockGuard& stoppedJobLock) VL_REQUIRES(m_stoppedJobsMutex); + void waitStopRequested() VL_REQUIRES(m_stoppedJobsMutex); // Waits until all other jobs are stopped - void waitOtherThreads(V3LockGuard& stoppedJobLock) VL_MT_SAFE_EXCLUDES(m_mutex) - VL_REQUIRES(m_stoppedJobsMutex); + void waitOtherThreads() VL_MT_SAFE_EXCLUDES(m_mutex) VL_REQUIRES(m_stoppedJobsMutex); template void pushJob(std::shared_ptr>& prom, std::function&& f) VL_MT_SAFE; diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index a71ad592e..fc2d06029 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -12,7 +12,13 @@ // Version 2.0. // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // -// TimingVisitor transformations: +// TimingSuspendableVisitor locates all C++ functions and processes that contain timing controls, +// and marks them as suspendable. If a process calls a suspendable function, then it is also marked +// as suspendable. If a function calls or overrides a suspendable function, it is also marked as +// suspendable. TimingSuspendableVisitor creates a dependency graph to propagate this property. It +// does not perform any AST transformations. +// +// TimingControlVisitor is the one that actually performs transformations: // - for each intra-assignment timing control: // - if it's a continuous assignment, transform it into an always // - introduce an intermediate variable @@ -33,9 +39,6 @@ // - if it's not a fork..join_none: // - create a join sync variable // - create statements that sync the main process with its children -// - for each process or C++ function, if it has CAwait statements, mark it as suspendable -// - if we mark a virtual function as suspendable, mark all overriding and overridden functions -// as suspendable, as well as calling processes // // See the internals documentation docs/internals.rst for more details. // @@ -54,18 +57,31 @@ #include "V3SenTree.h" #include "V3UniqueNames.h" +#include + VL_DEFINE_DEBUG_FUNCTIONS; // ###################################################################### -// Transform nodes affected by timing -class TimingVisitor final : public VNVisitor { +enum TimingFlag : uint8_t { + // Properties of flags with higher numbers include properties of flags with + // lower numbers + T_NORM = 0, // Normal non-suspendable process + T_SUSP = 1, // Suspendable + T_PROC = 2 // Suspendable with process metadata +}; + +// ###################################################################### +// Detect nodes affected by timing + +class TimingSuspendableVisitor final : public VNVisitor { private: // TYPES // Vertex of a dependency graph of suspendable nodes, e.g. if a node (process or task) is // suspendable, all its dependents should also be suspendable - class DependencyVertex final : public V3GraphVertex { + class TimingDependencyVertex final : public V3GraphVertex { AstNode* const m_nodep; // AST node represented by this graph vertex + // ACCESSORS string name() const override VL_MT_STABLE { return cvtToHex(nodep()) + ' ' + nodep()->prettyTypeName(); @@ -75,29 +91,183 @@ private: public: // CONSTRUCTORS - DependencyVertex(V3Graph* graphp, AstNode* nodep) + TimingDependencyVertex(V3Graph* graphp, AstNode* nodep) : V3GraphVertex{graphp} , m_nodep{nodep} {} - ~DependencyVertex() override = default; + ~TimingDependencyVertex() override = default; // ACCESSORS virtual AstNode* nodep() const VL_MT_STABLE { return m_nodep; } }; // NODE STATE - // AstNode::user1() -> bool. Set true if the node has been - // processed. - // AstSenTree::user1() -> AstVarScope*. Trigger scheduler assigned - // to this sentree - // Ast{NodeProcedure,CFunc,Begin}::user2() -> bool. Set true if process/task is - // suspendable - // AstSenTree::user2() -> AstCExpr*. Debug info passed to the - // timing schedulers + // AstClass::user1() -> bool. Set true if the class + // member cache has been + // refreshed. + // Ast{NodeProcedure,CFunc,Begin}::user2() -> int. Set to >= T_SUSP if + // process/task suspendable + // and to T_PROC if it + // needs process metadata. // Ast{NodeProcedure,CFunc,Begin}::user3() -> DependencyVertex*. Vertex in m_depGraph const VNUser1InUse m_user1InUse; const VNUser2InUse m_user2InUse; const VNUser3InUse m_user3InUse; + // STATE + AstClass* m_classp = nullptr; // Current class + AstNode* m_procp = nullptr; // NodeProcedure/CFunc/Begin we're under + V3Graph m_depGraph; // Dependency graph where a node is a dependency of another if it being + // suspendable makes the other node suspendable + + // METHODS + // Get or create the dependency vertex for the given node + TimingDependencyVertex* getDependencyVertex(AstNode* const nodep) { + if (!nodep->user3p()) nodep->user3p(new TimingDependencyVertex{&m_depGraph, nodep}); + return nodep->user3u().to(); + } + // Set timing flag of a node + bool setTimingFlag(AstNode* nodep, int flag) { + // Properties of flags with higher numbers include properties of flags with lower + // numbers, so modify nodep->user2() only if it will increase. + if (nodep->user2() < flag) { + nodep->user2(flag); + return true; + } + return false; + } + // Propagate suspendable/needProcess flag to all nodes that depend on the given one + void propagateTimingFlags(TimingDependencyVertex* const vxp) { + auto* const parentp = vxp->nodep(); + for (V3GraphEdge* edgep = vxp->inBeginp(); edgep; edgep = edgep->inNextp()) { + auto* const depVxp = static_cast(edgep->fromp()); + AstNode* const depp = depVxp->nodep(); + if (setTimingFlag(depp, parentp->user2())) propagateTimingFlags(depVxp); + } + } + + // VISITORS + void visit(AstClass* nodep) override { + UASSERT(!m_classp, "Class under class"); + VL_RESTORER(m_classp); + m_classp = nodep; + iterateChildren(nodep); + } + void visit(AstNodeProcedure* nodep) override { + VL_RESTORER(m_procp); + m_procp = nodep; + iterateChildren(nodep); + } + void visit(AstCFunc* nodep) override { + VL_RESTORER(m_procp); + m_procp = nodep; + iterateChildren(nodep); + TimingDependencyVertex* const vxp = getDependencyVertex(nodep); + if (nodep->needProcess()) nodep->user2(T_PROC); + if (!m_classp) return; + // If class method (possibly overrides another method) + if (!m_classp->user1SetOnce()) m_classp->repairCache(); + + // Go over overridden functions + + std::queue extends; + if (m_classp->extendsp()) extends.push(m_classp->extendsp()); + + while (!extends.empty()) { + AstClassExtends* ext_list = extends.front(); + extends.pop(); + + for (AstClassExtends* cextp = ext_list; cextp; + cextp = VN_AS(cextp->nextp(), ClassExtends)) { + // TODO: It is possible that a methods the same name in the base class is not + // actually overridden by our method. If this causes a problem, traverse to + // the root of the inheritance hierarchy and check if the original method is + // virtual or not. + if (!cextp->classp()->user1SetOnce()) cextp->classp()->repairCache(); + if (auto* const overriddenp + = VN_CAST(cextp->classp()->findMember(nodep->name()), CFunc)) { + setTimingFlag(nodep, overriddenp->user2()); + if (nodep->user2() + < T_PROC) { // Add a vertex only if the flag can still change + // Make a dependency cycle, as being suspendable should propagate both up + // and down the inheritance tree + TimingDependencyVertex* const overriddenVxp + = getDependencyVertex(overriddenp); + new V3GraphEdge{&m_depGraph, vxp, overriddenVxp, 1}; + new V3GraphEdge{&m_depGraph, overriddenVxp, vxp, 1}; + } + } else { + AstClassExtends* more_extends = cextp->classp()->extendsp(); + if (more_extends) extends.push(more_extends); + } + } + } + } + void visit(AstNodeCCall* nodep) override { + setTimingFlag(m_procp, nodep->funcp()->user2()); + if (m_procp->user2() < T_PROC) { // Add a vertex only if the flag can still change + TimingDependencyVertex* const procVxp = getDependencyVertex(m_procp); + TimingDependencyVertex* const funcVxp = getDependencyVertex(nodep->funcp()); + new V3GraphEdge{&m_depGraph, procVxp, funcVxp, 1}; + iterateChildren(nodep); + } + } + void visit(AstBegin* nodep) override { + VL_RESTORER(m_procp); + m_procp = nodep; + iterateChildren(nodep); + } + void visit(AstFork* nodep) override { + v3Global.setUsesTiming(); // Even if there are no event controls, we have to set this flag + // so that transformForks() in V3SchedTiming gets called and + // removes all forks and begins + if (nodep->isTimingControl() && m_procp) m_procp->user2(T_SUSP); + iterateChildren(nodep); + } + void visit(AstNode* nodep) override { + if (nodep->isTimingControl()) { + v3Global.setUsesTiming(); + if (m_procp) m_procp->user2(T_SUSP); + } + iterateChildren(nodep); + } + + //-------------------- + void visit(AstVar*) override {} // Accelerate + +public: + // CONSTRUCTORS + explicit TimingSuspendableVisitor(AstNetlist* nodep) { + iterate(nodep); + m_depGraph.removeTransitiveEdges(); + for (V3GraphVertex* vxp = m_depGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { + TimingDependencyVertex* const depVxp = static_cast(vxp); + if (depVxp->nodep()->user2()) propagateTimingFlags(depVxp); + } + if (dumpGraphLevel() >= 6) m_depGraph.dumpDotFilePrefixed("timing_deps"); + } + ~TimingSuspendableVisitor() override = default; +}; + +// ###################################################################### +// Transform nodes affected by timing + +class TimingControlVisitor final : public VNVisitor { +private: + // NODE STATE + // Ast{Always,NodeCCall,Fork,NodeAssign}::user1() -> bool. Set true if the node has + // been processed. + // AstSenTree::user1() -> AstVarScope*. Trigger scheduler assigned + // to this sentree + // Ast{NodeProcedure,CFunc,Begin}::user2() -> bool. Set true if process/task + // is suspendable + // Ast{EventControl}::user2() -> bool. Set true if event control + // should immediately be + // committed + // AstSenTree::user2() -> AstCExpr*. Debug info passed to the + // timing schedulers + // const VNUser1InUse m_user1InUse; (Allocated for use in SuspendableVisitor) + // const VNUser2InUse m_user2InUse; (Allocated for use in SuspendableVisitor) + // STATE // Current context AstNetlist* const m_netlistp; // Root node @@ -105,7 +275,7 @@ private: AstClass* m_classp = nullptr; // Current class AstScope* m_scopep = nullptr; // Current scope AstActive* m_activep = nullptr; // Current active - AstNode* m_procp = nullptr; // NodeProcedure/CFunc/Fork we're under + AstNode* m_procp = nullptr; // NodeProcedure/CFunc/Begin we're under double m_timescaleFactor = 1.0; // Factor to scale delays by // Unique names @@ -128,16 +298,9 @@ private: AstSenTree* m_dynamicSensesp = nullptr; // Domain to trigger if a dynamic trigger is set // Other - V3Graph m_depGraph; // Dependency graph where a node is a dependency of another if it being - // suspendable makes the other node suspendable SenTreeFinder m_finder{m_netlistp}; // Sentree finder and uniquifier // METHODS - // Get or create the dependency vertex for the given node - DependencyVertex* getDependencyVertex(AstNode* const nodep) { - if (!nodep->user3p()) nodep->user3p(new DependencyVertex{&m_depGraph, nodep}); - return nodep->user3u().to(); - } // Find net delay on the LHS of an assignment AstDelay* getLhsNetDelay(AstNodeAssign* nodep) const { bool foundWrite = false; @@ -321,6 +484,14 @@ private: methodp->addPinsp(createEventDescription(sensesp)); addDebugInfo(methodp); } + // Adds process pointer to a hardcoded method call + void addProcessInfo(AstCMethodHard* const methodp) const { + FileLine* const flp = methodp->fileline(); + AstCExpr* const ap = new AstCExpr{ + flp, m_procp && m_procp->user2() == T_PROC ? "vlProcess" : "nullptr", 0}; + ap->dtypeSetVoid(); + methodp->addPinsp(ap); + } // Creates the fork handle type and returns it AstBasicDType* getCreateForkSyncDTypep() { if (m_forkDtp) return m_forkDtp; @@ -359,7 +530,7 @@ private: // Create a fork sync var FileLine* const flp = forkp->fileline(); // If we're in a function, insert the sync var directly before the fork - AstNode* const insertBeforep = VN_IS(m_procp, CFunc) ? forkp : nullptr; + AstNode* const insertBeforep = m_classp ? forkp : nullptr; AstVarScope* forkVscp = createTemp(flp, forkp->name() + "__sync", getCreateForkSyncDTypep(), insertBeforep); unsigned joinCount = 0; // Needed for join counter @@ -373,11 +544,13 @@ private: auto* const initp = new AstCMethodHard{flp, new AstVarRef{flp, forkVscp, VAccess::WRITE}, "init", new AstConst{flp, joinCount}}; initp->dtypeSetVoid(); + addProcessInfo(initp); forkp->addHereThisAsNext(initp->makeStmt()); // Await the join at the end auto* const joinp = new AstCMethodHard{flp, new AstVarRef{flp, forkVscp, VAccess::WRITE}, "join"}; joinp->dtypeSetVoid(); + addProcessInfo(joinp); addDebugInfo(joinp); AstCAwait* const awaitp = new AstCAwait{flp, joinp}; awaitp->dtypeSetVoid(); @@ -407,98 +580,73 @@ private: VL_RESTORER(m_procp); m_procp = nodep; iterateChildren(nodep); - if (nodep->user2()) nodep->setSuspendable(); + if (nodep->user2() >= T_SUSP) nodep->setSuspendable(); + if (nodep->user2() >= T_PROC) nodep->setNeedProcess(); + } + void visit(AstInitial* nodep) override { + visit(static_cast(nodep)); + if (nodep->needProcess() && !nodep->user1SetOnce()) { + nodep->addStmtsp( + new AstCStmt{nodep->fileline(), "vlProcess->state(VlProcess::FINISHED);\n"}); + } } void visit(AstAlways* nodep) override { - visit(static_cast(nodep)); - if (nodep->isSuspendable() && !nodep->user1SetOnce()) { - FileLine* const flp = nodep->fileline(); - AstSenTree* const sensesp = m_activep->sensesp(); - if (sensesp->hasClocked()) { - AstNode* bodysp = nodep->stmtsp()->unlinkFrBackWithNext(); - auto* const controlp = new AstEventControl{flp, sensesp->cloneTree(false), bodysp}; - nodep->addStmtsp(controlp); - iterate(controlp); - } - // Note: The 'while (true)' outer loop will be added in V3Sched - auto* const activep = new AstActive{ - flp, "", new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Initial{}}}}; - activep->sensesStorep(activep->sensesp()); - activep->addStmtsp(nodep->unlinkFrBack()); - m_activep->addNextHere(activep); + if (nodep->user1SetOnce()) return; + iterateChildren(nodep); + if (!nodep->user2()) return; + if (nodep->user2() == T_PROC) nodep->setNeedProcess(); + nodep->setSuspendable(); + FileLine* const flp = nodep->fileline(); + AstSenTree* const sensesp = m_activep->sensesp(); + if (sensesp->hasClocked()) { + AstNode* const bodysp = nodep->stmtsp()->unlinkFrBackWithNext(); + auto* const controlp = new AstEventControl{flp, sensesp->cloneTree(false), bodysp}; + nodep->addStmtsp(controlp); + iterate(controlp); } + // Note: The 'while (true)' outer loop will be added in V3Sched + auto* const activep = new AstActive{ + flp, "", new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Initial{}}}}; + activep->sensesStorep(activep->sensesp()); + activep->addStmtsp(nodep->unlinkFrBack()); + m_activep->addNextHere(activep); } void visit(AstCFunc* nodep) override { VL_RESTORER(m_procp); m_procp = nodep; iterateChildren(nodep); - DependencyVertex* const vxp = getDependencyVertex(nodep); - if (m_classp && nodep->isVirtual() - && !nodep->user1SetOnce()) { // If virtual (only visit once) - // Go over overridden functions - m_classp->repairCache(); - for (auto* cextp = m_classp->extendsp(); cextp; - cextp = VN_AS(cextp->nextp(), ClassExtends)) { - if (auto* const overriddenp - = VN_CAST(cextp->classp()->findMember(nodep->name()), CFunc)) { - if (overriddenp->user2()) { // If suspendable - if (!nodep->user2()) { - // It should be a coroutine but it has no awaits. Add a co_return at - // the end (either that or a co_await is required in a coroutine) - nodep->addStmtsp(new AstCStmt{nodep->fileline(), "co_return;\n"}); - } - nodep->user2(true); - // If it's suspendable already, no need to add it as our dependency or - // self to its dependencies - } else { - DependencyVertex* const overriddenVxp = getDependencyVertex(overriddenp); - new V3GraphEdge{&m_depGraph, vxp, overriddenVxp, 1}; - new V3GraphEdge{&m_depGraph, overriddenVxp, vxp, 1}; - } - } - } + if (!nodep->user2()) return; + nodep->rtnType("VlCoroutine"); + // If in a class, create a shared pointer to 'this' + if (m_classp) nodep->addInitsp(new AstCStmt{nodep->fileline(), "VL_KEEP_THIS;\n"}); + AstNode* firstCoStmtp = nullptr; // First co_* statement in the function + nodep->exists([&](AstCAwait* const awaitp) -> bool { return (firstCoStmtp = awaitp); }); + if (!firstCoStmtp) { + // It's a coroutine but has no awaits (a class method that overrides/is + // overridden by a suspendable, but doesn't have any awaits itself). Add a + // co_return at the end (either that or a co_await is required in a + // coroutine) + firstCoStmtp = new AstCStmt{nodep->fileline(), "co_return;\n"}; + nodep->addStmtsp(firstCoStmtp); } - if (nodep->user2() && !nodep->isCoroutine()) { // If first marked as suspendable - nodep->rtnType("VlCoroutine"); - // If in a class, create a shared pointer to 'this' - if (m_classp) nodep->addInitsp(new AstCStmt{nodep->fileline(), "VL_KEEP_THIS;\n"}); - // Revisit dependent nodes if needed - for (V3GraphEdge* edgep = vxp->inBeginp(); edgep; edgep = edgep->inNextp()) { - auto* const depVxp = static_cast(edgep->fromp()); - AstNode* const depp = depVxp->nodep(); - if (!depp->user2()) { // If dependent not suspendable - depp->user2(true); - if (auto* const funcp = VN_CAST(depp, CFunc)) { - // It's a coroutine but has no awaits (a class method that overrides/is - // overridden by a suspendable, but doesn't have any awaits itself). Add a - // co_return at the end (either that or a co_await is required in a - // coroutine) - funcp->addStmtsp(new AstCStmt{funcp->fileline(), "co_return;\n"}); - } - } - iterate(depp); - } + if (nodep->dpiExportImpl()) { + // A DPI-exported coroutine won't be able to block the calling code + // Error on the await node; fall back to the function node + firstCoStmtp->v3warn(E_UNSUPPORTED, + "Unsupported: Timing controls inside DPI-exported tasks"); } + if (nodep->user2() == T_PROC) nodep->setNeedProcess(); } void visit(AstNodeCCall* nodep) override { - if (nodep->funcp()->user2()) { // If suspendable + if (nodep->funcp()->user2() && !nodep->user1SetOnce()) { // If suspendable VNRelinker relinker; nodep->unlinkFrBack(&relinker); AstCAwait* const awaitp = new AstCAwait{nodep->fileline(), nodep}; awaitp->dtypeSetVoid(); relinker.relink(awaitp); - } else { - // Add our process/func as the CFunc's dependency as we might have to put an await here - DependencyVertex* const procVxp = getDependencyVertex(m_procp); - DependencyVertex* const funcVxp = getDependencyVertex(nodep->funcp()); - new V3GraphEdge{&m_depGraph, procVxp, funcVxp, 1}; } iterateChildren(nodep); } - void visit(AstCAwait* nodep) override { - v3Global.setUsesTiming(); - m_procp->user2(true); - } void visit(AstDelay* nodep) override { UASSERT_OBJ(!nodep->isCycleDelay(), nodep, "Cycle delays should have been handled in V3AssertPre"); @@ -527,6 +675,7 @@ private: auto* const delayMethodp = new AstCMethodHard{ flp, new AstVarRef{flp, getCreateDelayScheduler(), VAccess::WRITE}, "delay", valuep}; delayMethodp->dtypeSetVoid(); + addProcessInfo(delayMethodp); addDebugInfo(delayMethodp); // Create the co_await AstCAwait* const awaitp = new AstCAwait{flp, delayMethodp, getCreateDelaySenTree()}; @@ -566,6 +715,7 @@ private: flp, new AstVarRef{flp, getCreateDynamicTriggerScheduler(), VAccess::WRITE}, "evaluation"}; evalMethodp->dtypeSetVoid(); + addProcessInfo(evalMethodp); auto* const sensesp = nodep->sensesp(); addEventDebugInfo(evalMethodp, sensesp); // Create the co_await @@ -620,6 +770,10 @@ private: flp, new AstVarRef{flp, getCreateTriggerSchedulerp(sensesp), VAccess::WRITE}, "trigger"}; triggerMethodp->dtypeSetVoid(); + // If it should be committed immediately, pass true, otherwise false + triggerMethodp->addPinsp(nodep->user2() ? new AstConst{flp, AstConst::BitTrue{}} + : new AstConst{flp, AstConst::BitFalse{}}); + addProcessInfo(triggerMethodp); addEventDebugInfo(triggerMethodp, sensesp); // Create the co_await AstCAwait* const awaitp = new AstCAwait{flp, triggerMethodp, sensesp}; @@ -644,7 +798,7 @@ private: } // Insert new vars before the timing control if we're in a function; in a process we can't // do that. These intra-assignment vars will later be passed to forked processes by value. - AstNode* const insertBeforep = VN_IS(m_procp, CFunc) ? controlp : nullptr; + AstNode* const insertBeforep = m_classp ? controlp : nullptr; // Function for replacing values with intermediate variables const auto replaceWithIntermediate = [&](AstNodeExpr* const valuep, const std::string& name) { @@ -746,14 +900,20 @@ private: AstSenItem* const senItemsp = varRefpsToSenItemsp(condp); UASSERT_OBJ(senItemsp, nodep, "No varrefs in wait statement condition"); // Put the event control in a while loop with the wait expression as condition - auto* const loopp - = new AstWhile{flp, new AstLogNot{flp, condp}, - new AstEventControl{flp, new AstSenTree{flp, senItemsp}, nullptr}}; + AstEventControl* const controlp + = new AstEventControl{flp, new AstSenTree{flp, senItemsp}, nullptr}; + controlp->user2(true); // Commit immediately + AstWhile* const loopp = new AstWhile{flp, new AstLogNot{flp, condp}, controlp}; if (stmtsp) AstNode::addNext(loopp, stmtsp); nodep->replaceWith(loopp); } VL_DO_DANGLING(nodep->deleteTree(), nodep); } + void visit(AstBegin* nodep) override { + VL_RESTORER(m_procp); + m_procp = nodep; + iterateChildren(nodep); + } void visit(AstFork* nodep) override { if (nodep->user1SetOnce()) return; v3Global.setUsesTiming(); @@ -771,8 +931,6 @@ private: } auto* const beginp = VN_AS(stmtp, Begin); stmtp = beginp->nextp(); - VL_RESTORER(m_procp); - m_procp = beginp; iterate(beginp); // Even if we do not find any awaits, we cannot simply inline the process here, as new // awaits could be added later. @@ -783,18 +941,16 @@ private: } //-------------------- - void visit(AstNodeExpr*) override {} // Accelerate - void visit(AstVar*) override {} + void visit(AstVar*) override {} // Accelerate void visit(AstNode* nodep) override { iterateChildren(nodep); } public: // CONSTRUCTORS - explicit TimingVisitor(AstNetlist* nodep) + explicit TimingControlVisitor(AstNetlist* nodep) : m_netlistp{nodep} { iterate(nodep); - if (dumpGraph() >= 6) m_depGraph.dumpDotFilePrefixed("timing_deps"); } - ~TimingVisitor() override = default; + ~TimingControlVisitor() override = default; }; //###################################################################### @@ -802,6 +958,7 @@ public: void V3Timing::timingAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - TimingVisitor{nodep}; - V3Global::dumpCheckGlobalTree("timing", 0, dumpTree() >= 3); + TimingSuspendableVisitor susVisitor{nodep}; + if (v3Global.usesTiming()) TimingControlVisitor{nodep}; + V3Global::dumpCheckGlobalTree("timing", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index 44a12ba59..ced3fc9fd 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -734,11 +734,11 @@ private: detectDuplicates(); // Simplify & optimize the graph - if (dumpGraph() >= 6) m_graph.dumpDotFilePrefixed("trace_pre"); + if (dumpGraphLevel() >= 6) m_graph.dumpDotFilePrefixed("trace_pre"); graphSimplify(true); - if (dumpGraph() >= 6) m_graph.dumpDotFilePrefixed("trace_simplified"); + if (dumpGraphLevel() >= 6) m_graph.dumpDotFilePrefixed("trace_simplified"); graphOptimize(); - if (dumpGraph() >= 6) m_graph.dumpDotFilePrefixed("trace_optimized"); + if (dumpGraphLevel() >= 6) m_graph.dumpDotFilePrefixed("trace_optimized"); // Create the fine grained activity flags createActivityFlags(); @@ -924,5 +924,5 @@ public: void V3Trace::traceAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { TraceVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("trace", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("trace", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index 704d1d4b4..e06a7f02b 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -196,7 +196,11 @@ private: } std::string getScopeChar(VltTraceScope sct) { - return std::string(1, static_cast(0x80 + sct)); + if (v3Global.opt.traceFormat().fst()) { + return std::string(1, static_cast(0x80 + sct)); + } else { + return std::string(); + } } std::string addAboveInterface(const std::string& scopeName) { @@ -550,5 +554,5 @@ public: void V3TraceDecl::traceDeclAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { TraceDeclVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("tracedecl", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("tracedecl", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 61720c2a4..ee0eeb271 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -294,7 +294,7 @@ public: for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { graphWalkRecurseBack(static_cast(itp), 0); } - if (dumpGraph() >= 9) m_graph.dumpDotFilePrefixed("tri_pos__" + nodep->name()); + if (dumpGraphLevel() >= 9) m_graph.dumpDotFilePrefixed("tri_pos__" + nodep->name()); } void associate(AstNode* fromp, AstNode* top) { new V3GraphEdge{&m_graph, makeVertex(fromp), makeVertex(top), 1}; @@ -1818,5 +1818,5 @@ public: void V3Tristate::tristateAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { TristateVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("tristate", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("tristate", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Unknown.cpp b/src/V3Unknown.cpp index 2f0eabfb0..bf597e4e3 100644 --- a/src/V3Unknown.cpp +++ b/src/V3Unknown.cpp @@ -519,5 +519,5 @@ public: void V3Unknown::unknownAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { UnknownVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("unknown", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("unknown", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index 5e94255a3..6655001d1 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -518,5 +518,5 @@ void V3Unroll::unrollAll(AstNetlist* nodep) { UnrollStateful unroller; unroller.unrollAll(nodep); } // Destruct before checking - V3Global::dumpCheckGlobalTree("unroll", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("unroll", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3VariableOrder.cpp b/src/V3VariableOrder.cpp index 6f19fe113..e108e1605 100644 --- a/src/V3VariableOrder.cpp +++ b/src/V3VariableOrder.cpp @@ -207,5 +207,5 @@ void V3VariableOrder::orderAll() { modp = VN_AS(modp->nextp(), NodeModule)) { VariableOrder::processModule(modp); } - V3Global::dumpCheckGlobalTree("variableorder", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("variableorder", 0, dumpTreeLevel() >= 3); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index d1583e1c4..334987da8 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -232,7 +232,7 @@ private: const AstWith* m_withp = nullptr; // Current 'with' statement const AstFunc* m_funcp = nullptr; // Current function const AstAttrOf* m_attrp = nullptr; // Current attribute - AstNodeModule* m_modp = nullptr; // Current module + AstPackage* m_pkgp = nullptr; // Current package const bool m_paramsOnly; // Computing parameter value; limit operation const bool m_doGenerate; // Do errors later inside generate statement int m_dtTables = 0; // Number of created data type tables @@ -503,9 +503,26 @@ private: // the size of this subexpression only. // Second call (final()) m_vup->width() is probably the expression size, so // the expression includes the size of the output too. - if (nodep->thenp()->dtypep()->skipRefp() == nodep->elsep()->dtypep()->skipRefp()) { + const AstNodeDType* const thenDTypep = nodep->thenp()->dtypep(); + const AstNodeDType* const elseDTypep = nodep->elsep()->dtypep(); + if (thenDTypep->skipRefp() == elseDTypep->skipRefp()) { // TODO might need a broader equation, use the Castable function? - nodep->dtypeFrom(nodep->thenp()->dtypep()); + nodep->dtypeFrom(thenDTypep); + } else if (nodep->thenp()->isClassHandleValue() + || nodep->elsep()->isClassHandleValue()) { + AstNodeDType* commonClassTypep = nullptr; + if (nodep->thenp()->isClassHandleValue() && nodep->elsep()->isClassHandleValue()) { + // Get the most-deriving class type that both arguments can be casted to. + commonClassTypep = getCommonClassTypep(nodep->thenp(), nodep->elsep()); + } + if (commonClassTypep) { + nodep->dtypep(commonClassTypep); + } else { + nodep->v3error("Incompatible types of operands of condition operator: " + << thenDTypep->prettyTypeName() << " and " + << elseDTypep->prettyTypeName()); + nodep->dtypeFrom(thenDTypep); + } } else if (nodep->thenp()->isDouble() || nodep->elsep()->isDouble()) { nodep->dtypeSetDouble(); } else if (nodep->thenp()->isString() || nodep->elsep()->isString()) { @@ -1658,12 +1675,16 @@ private: } else if (AstNodeDType* const keyp = VN_CAST(elementsp, NodeDType)) { newp = new AstAssocArrayDType{nodep->fileline(), VFlagChildDType{}, childp, keyp}; } else { + // The subtract in the range may confuse users; as the array + // size is self determined there's no reason to warn about widths + FileLine* const elementsNewFl = elementsp->fileline(); + elementsNewFl->warnOff(V3ErrorCode::WIDTHEXPAND, true); // Must be expression that is constant, but we'll determine that later newp = new AstUnpackArrayDType{ nodep->fileline(), VFlagChildDType{}, childp, new AstRange{nodep->fileline(), new AstConst(elementsp->fileline(), 0), - new AstSub{elementsp->fileline(), VN_AS(elementsp, NodeExpr), - new AstConst(elementsp->fileline(), 1)}}}; + new AstSub{elementsNewFl, VN_AS(elementsp, NodeExpr), + new AstConst(elementsNewFl, 1)}}}; } nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); @@ -2469,23 +2490,35 @@ private: nextip = itemp->nextp(); // iterate may cause the node to get replaced VL_DO_DANGLING(userIterate(itemp, WidthVP{CONTEXT_DET, PRELIM}.p()), itemp); } - // Take width as maximum across all items - int width = nodep->exprp()->width(); - int mwidth = nodep->exprp()->widthMin(); - for (const AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) { - width = std::max(width, itemp->width()); - mwidth = std::max(mwidth, itemp->widthMin()); + + AstBasicDType* dtype = VN_CAST(nodep->exprp()->dtypep(), BasicDType); + AstNodeDType* subDTypep = nullptr; + + if (dtype && dtype->isString()) { + nodep->dtypeSetString(); + subDTypep = nodep->findStringDType(); + } else if (dtype && dtype->isDouble()) { + nodep->dtypeSetDouble(); + subDTypep = nodep->findDoubleDType(); + } else { + // Take width as maximum across all items + int width = nodep->exprp()->width(); + int mwidth = nodep->exprp()->widthMin(); + for (const AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) { + width = std::max(width, itemp->width()); + mwidth = std::max(mwidth, itemp->widthMin()); + } + nodep->dtypeSetBit(); + subDTypep = nodep->findLogicDType(width, mwidth, nodep->exprp()->dtypep()->numeric()); } - // Apply width - AstNodeDType* const subDTypep - = nodep->findLogicDType(width, mwidth, nodep->exprp()->dtypep()->numeric()); + iterateCheck(nodep, "Inside expression", nodep->exprp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP); for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { nextip = itemp->nextp(); // iterate may cause the node to get replaced iterateCheck(nodep, "Inside Item", itemp, CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP); } - nodep->dtypeSetBit(); + if (debug() >= 9) nodep->dumpTree("- inside-in: "); // Now rip out the inside and replace with simple math AstNodeExpr* newp = nullptr; @@ -2509,8 +2542,8 @@ private: "Inside operator not legal on non-unpacked arrays (IEEE 1800-2017 11.4.13)"); continue; } else { - inewp = new AstEqWild{itemp->fileline(), nodep->exprp()->cloneTree(true), - itemp->unlinkFrBack()}; + inewp = AstEqWild::newTyped(itemp->fileline(), nodep->exprp()->cloneTree(true), + itemp->unlinkFrBack()); } if (newp) { newp = new AstOr{nodep->fileline(), newp, inewp}; @@ -2595,6 +2628,16 @@ private: } void visit(AstClass* nodep) override { if (nodep->didWidthAndSet()) return; + // If the package is std::process, set m_process type to VlProcessRef + if (m_pkgp && m_pkgp->name() == "std" && nodep->name() == "process") { + if (AstVar* const varp = VN_CAST(nodep->findMember("m_process"), Var)) { + AstBasicDType* const dtypep = new AstBasicDType{ + nodep->fileline(), VBasicDTypeKwd::PROCESS_REFERENCE, VSigning::UNSIGNED}; + v3Global.rootp()->typeTablep()->addTypesp(dtypep); + varp->getChildDTypep()->unlinkFrBack(); + varp->dtypep(dtypep); + } + } // 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); @@ -2603,6 +2646,11 @@ private: userIterateChildren(nodep, nullptr); // First size all members nodep->repairCache(); } + void visit(AstPackage* nodep) override { + VL_RESTORER(m_pkgp); + m_pkgp = nodep; + userIterateChildren(nodep, nullptr); + } void visit(AstThisRef* nodep) override { if (nodep->didWidthAndSet()) return; nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->childDTypep())); @@ -2613,12 +2661,6 @@ private: // though causes problems with t_class_forward.v, so for now avoided // 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); - } void visit(AstClassOrPackageRef* nodep) override { if (nodep->didWidthAndSet()) return; userIterateChildren(nodep, nullptr); @@ -2808,6 +2850,10 @@ private: return false; } + void visit(AstCExpr* nodep) override { + // Inserted by V3Width only so we know has been resolved + userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); + } void visit(AstCMethodHard* nodep) override { // Never created before V3Width, so no need to redo it UASSERT_OBJ(nodep->dtypep(), nodep, "CMETHODCALLs should have already been sized"); @@ -2874,7 +2920,7 @@ private: } return nullptr; } - void methodOkArguments(AstMethodCall* nodep, int minArg, int maxArg) { + void methodOkArguments(AstNodeFTaskRef* nodep, int minArg, int maxArg) { int narg = 0; for (AstNode* argp = nodep->pinsp(); argp; argp = argp->nextp()) { if (VN_IS(argp, With)) { @@ -3227,36 +3273,11 @@ private: << nodep->prettyName() << "'"); } } - void methodCallDyn(AstMethodCall* nodep, AstDynArrayDType* adtypep) { + AstCMethodHard* methodCallArray(AstMethodCall* nodep, AstNodeDType* adtypep) { AstCMethodHard* newp = nullptr; - if (nodep->name() == "at") { // Created internally for [] - methodOkArguments(nodep, 1, 1); - methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); - newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "at"}; - newp->dtypeFrom(adtypep->subDTypep()); - } else if (nodep->name() == "size") { - methodOkArguments(nodep, 0, 0); - newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "size"}; - newp->dtypeSetSigned32(); - } else if (nodep->name() == "delete") { // function void delete() - methodOkArguments(nodep, 0, 0); - methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); - newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "clear"}; - newp->dtypeSetVoid(); - } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor" - || nodep->name() == "sum" || nodep->name() == "product") { - // All value return - AstWith* const withp - = methodWithArgument(nodep, false, false, adtypep->subDTypep(), - nodep->findUInt32DType(), adtypep->subDTypep()); - methodOkArguments(nodep, 0, 0); - methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); - newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), - "r_" + nodep->name(), withp}; - newp->dtypeFrom(adtypep->subDTypep()); - if (!nodep->firstAbovep()) newp->dtypeSetVoid(); - } else if (nodep->name() == "reverse" || nodep->name() == "shuffle" - || nodep->name() == "sort" || nodep->name() == "rsort") { + + if (nodep->name() == "reverse" || nodep->name() == "shuffle" || nodep->name() == "sort" + || nodep->name() == "rsort") { AstWith* withp = nullptr; if (nodep->name() == "sort" || nodep->name() == "rsort") { withp = methodWithArgument(nodep, false, true, nullptr, nodep->findUInt32DType(), @@ -3269,10 +3290,12 @@ private: newp->dtypeSetVoid(); } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique" || nodep->name() == "unique_index") { + AstWith* const withp = methodWithArgument( + nodep, false, true, nullptr, nodep->findUInt32DType(), adtypep->subDTypep()); methodOkArguments(nodep, 0, 0); methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), - nodep->name()}; + nodep->name(), withp}; if (nodep->name() == "unique_index") { newp->dtypep(newp->findQueueIndexDType()); } else { @@ -3301,6 +3324,38 @@ private: nodep->name(), withp}; newp->dtypep(newp->findQueueIndexDType()); if (!nodep->firstAbovep()) newp->dtypeSetVoid(); + } + return newp; + } + void methodCallDyn(AstMethodCall* nodep, AstDynArrayDType* adtypep) { + AstCMethodHard* newp = nullptr; + if (nodep->name() == "at") { // Created internally for [] + methodOkArguments(nodep, 1, 1); + methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); + newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "at"}; + newp->dtypeFrom(adtypep->subDTypep()); + } else if (nodep->name() == "size") { + methodOkArguments(nodep, 0, 0); + newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "size"}; + newp->dtypeSetSigned32(); + } else if (nodep->name() == "delete") { // function void delete() + methodOkArguments(nodep, 0, 0); + methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); + newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "clear"}; + newp->dtypeSetVoid(); + } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor" + || nodep->name() == "sum" || nodep->name() == "product") { + // All value return + AstWith* const withp + = methodWithArgument(nodep, false, false, adtypep->subDTypep(), + nodep->findUInt32DType(), adtypep->subDTypep()); + methodOkArguments(nodep, 0, 0); + methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); + newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), + "r_" + nodep->name(), withp}; + newp->dtypeFrom(adtypep->subDTypep()); + if (!nodep->firstAbovep()) newp->dtypeSetVoid(); + } else if ((newp = methodCallArray(nodep, adtypep))) { } else { nodep->v3warn(E_UNSUPPORTED, "Unsupported/unknown built-in dynamic array method " << nodep->prettyNameQ()); @@ -3388,54 +3443,7 @@ private: "r_" + nodep->name(), withp}; newp->dtypeFrom(withp ? withp->dtypep() : adtypep->subDTypep()); if (!nodep->firstAbovep()) newp->dtypeSetVoid(); - } else if (nodep->name() == "reverse" || nodep->name() == "shuffle" - || nodep->name() == "sort" || nodep->name() == "rsort") { - AstWith* withp = nullptr; - if (nodep->name() == "sort" || nodep->name() == "rsort") { - withp = methodWithArgument(nodep, false, true, nullptr, nodep->findUInt32DType(), - adtypep->subDTypep()); - } - methodOkArguments(nodep, 0, 0); - methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); - newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), - nodep->name(), withp}; - newp->dtypeSetVoid(); - } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique" - || nodep->name() == "unique_index") { - AstWith* const withp = methodWithArgument( - nodep, false, true, nullptr, nodep->findUInt32DType(), adtypep->subDTypep()); - methodOkArguments(nodep, 0, 0); - methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); - newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), - nodep->name(), withp}; - if (nodep->name() == "unique_index") { - newp->dtypep(newp->findQueueIndexDType()); - } else { - newp->dtypeFrom(adtypep); - } - if (!nodep->firstAbovep()) newp->dtypeSetVoid(); - } else if (nodep->name() == "find" || nodep->name() == "find_first" - || nodep->name() == "find_last") { - AstWith* const withp - = methodWithArgument(nodep, true, false, nodep->findBitDType(), - nodep->findUInt32DType(), adtypep->subDTypep()); - methodOkArguments(nodep, 0, 0); - methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); - newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), - nodep->name(), withp}; - newp->dtypeFrom(adtypep); - if (!nodep->firstAbovep()) newp->dtypeSetVoid(); - } else if (nodep->name() == "find_index" || nodep->name() == "find_first_index" - || nodep->name() == "find_last_index") { - AstWith* const withp - = methodWithArgument(nodep, true, false, nodep->findBitDType(), - nodep->findUInt32DType(), adtypep->subDTypep()); - methodOkArguments(nodep, 0, 0); - methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); - newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), - nodep->name(), withp}; - newp->dtypep(newp->findQueueIndexDType()); - if (!nodep->firstAbovep()) newp->dtypeSetVoid(); + } else if ((newp = methodCallArray(nodep, adtypep))) { } else { nodep->v3warn(E_UNSUPPORTED, "Unsupported/unknown built-in queue method " << nodep->prettyNameQ()); @@ -3454,7 +3462,7 @@ private: VL_DANGLING(index_exprp); // May have been edited return VN_AS(nodep->pinsp(), Arg)->exprp(); } - void methodCallWarnTiming(AstMethodCall* const nodep, const std::string& className) { + void methodCallWarnTiming(AstNodeFTaskRef* const nodep, const std::string& className) { if (v3Global.opt.timing().isSetFalse()) { nodep->v3warn(E_NOTIMING, className << "::" << nodep->name() << "() requires --timing"); @@ -3478,14 +3486,12 @@ private: if (classp->name() == "semaphore" || classp->name() == "process" || VString::startsWith(classp->name(), "mailbox")) { // Find the package the class is in - AstNode* pkgItemp = classp; - while (pkgItemp->backp() && pkgItemp->backp()->nextp() == pkgItemp) { - pkgItemp = pkgItemp->backp(); - } - AstPackage* const packagep = VN_CAST(pkgItemp->backp(), Package); + AstPackage* const packagep = getItemPackage(classp); // Check if it's std if (packagep && packagep->name() == "std") { - if (classp->name() == "semaphore" && nodep->name() == "get") { + if (classp->name() == "process") { + methodCallWarnTiming(nodep, "process"); + } else if (classp->name() == "semaphore" && nodep->name() == "get") { methodCallWarnTiming(nodep, "semaphore"); } else if (nodep->name() == "put" || nodep->name() == "get" || nodep->name() == "peek") { @@ -3518,6 +3524,13 @@ private: processFTaskRefArgs(nodep); } return; + } else if (nodep->name() == "get_randstate" || nodep->name() == "set_randstate") { + // See implementations under AstNodeFTaskRef + nodep->v3warn(E_UNSUPPORTED, "Unsupported: 'get_randstate'/'set_randstate' called " + "on object. Suggest call from inside class."); + nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitTrue{}}); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; } classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr; } @@ -3602,6 +3615,11 @@ private: } nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); + } else if (AstCMethodHard* newp = methodCallArray(nodep, adtypep)) { + newp->protect(false); + newp->didWidth(true); + nodep->replaceWith(newp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); } else { nodep->v3error("Unknown built-in array method " << nodep->prettyNameQ()); nodep->dtypeFrom(adtypep->subDTypep()); // Best guess @@ -3740,7 +3758,9 @@ private: void visit(AstNew* nodep) override { if (nodep->didWidth()) return; AstClass* classp = nullptr; + bool assign = false; if (VN_IS(nodep->backp(), Assign)) { // assignment case + assign = true; AstClassRefDType* const refp = m_vup ? VN_CAST(m_vup->dtypeNullSkipRefp(), ClassRefDType) : nullptr; if (!refp) { // e.g. int a = new; @@ -3768,9 +3788,9 @@ private: classp = VN_CAST(nodep->classOrPackagep(), Class); UASSERT_OBJ(classp, nodep, "Unlinked classOrPackagep()"); UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked taskp()"); - nodep->dtypeFrom(nodep->taskp()); } userIterate(nodep->taskp(), nullptr); + if (!assign) nodep->dtypeFrom(nodep->taskp()); processFTaskRefArgs(nodep); } void visit(AstNewCopy* nodep) override { @@ -4627,6 +4647,7 @@ private: FileLine* const fl = varp->fileline(); auto* const whilep = new AstWhile{ fl, condp, bodysp, new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, incp}}; + varp->lifetime(VLifetime::AUTOMATIC); AstNode* const stmtsp = varp; // New statements for under new Begin stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, leftp}); stmtsp->addNext(whilep); @@ -5277,6 +5298,7 @@ private: nodep->didWidth(true); return; } + if (m_pkgp && m_pkgp->name() == "std") nodep->isFromStd(true); if (nodep->classMethod() && nodep->name() == "rand_mode") { nodep->v3error("The 'rand_mode' method is built-in and cannot be overridden" " (IEEE 1800-2017 18.8)"); @@ -5359,9 +5381,25 @@ private: } } + AstPackage* getItemPackage(AstNode* pkgItemp) { + while (pkgItemp->backp() && pkgItemp->backp()->nextp() == pkgItemp) { + pkgItemp = pkgItemp->backp(); + } + return VN_CAST(pkgItemp->backp(), Package); + } void visit(AstFuncRef* nodep) override { visit(static_cast(nodep)); nodep->dtypeFrom(nodep->taskp()); + if (nodep->fileline()->timingOn()) { + AstNodeModule* const classp = nodep->classOrPackagep(); + if (nodep->name() == "self" && classp->name() == "process") { + // Find if package the class is in is std:: + AstPackage* const packagep = getItemPackage(classp); + if (packagep && packagep->name() == "std") { + methodCallWarnTiming(nodep, "process"); + } + } + } // if (debug()) nodep->dumpTree("- FuncOut: "); } // Returns true if dtypep0 and dtypep1 have same dimensions @@ -5533,7 +5571,9 @@ 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") { + if (nodep->name() == "randomize" || nodep->name() == "srandom" + || (!nodep->taskp() + && (nodep->name() == "get_randstate" || nodep->name() == "set_randstate"))) { // TODO perhaps this should move to V3LinkDot if (!m_classp) { nodep->v3error("Calling implicit class method " << nodep->prettyNameQ() @@ -5544,8 +5584,35 @@ private: } if (nodep->name() == "randomize") { nodep->taskp(V3Randomize::newRandomizeFunc(m_classp)); - } else { + } else if (nodep->name() == "srandom") { nodep->taskp(V3Randomize::newSRandomFunc(m_classp)); + } else if (nodep->name() == "get_randstate") { + methodOkArguments(nodep, 0, 0); + m_classp->baseMostClassp()->needRNG(true); + v3Global.useRandomizeMethods(true); + AstCExpr* const newp + = new AstCExpr{nodep->fileline(), "__Vm_rng.get_randstate()", 1, true}; + newp->dtypeSetString(); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } else if (nodep->name() == "set_randstate") { + methodOkArguments(nodep, 1, 1); + AstNodeExpr* const expr1p = VN_AS(nodep->pinsp(), Arg)->exprp(); // May edit + iterateCheckString(nodep, "LHS", expr1p, BOTH); + AstNodeExpr* const exprp = VN_AS(nodep->pinsp(), Arg)->exprp(); + m_classp->baseMostClassp()->needRNG(true); + v3Global.useRandomizeMethods(true); + AstCExpr* const newp + = new AstCExpr{nodep->fileline(), "__Vm_rng.set_randstate(", 1, true}; + newp->addExprsp(exprp->unlinkFrBack()); + newp->addExprsp(new AstText{nodep->fileline(), ")", true}); + newp->dtypeSetString(); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } else { + UASSERT_OBJ(false, nodep, "Bad case"); } } UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked"); @@ -7251,6 +7318,30 @@ private: if (nodep->subDTypep()) return hasOpenArrayIterateDType(nodep->subDTypep()->skipRefp()); return false; } + AstNodeDType* getCommonClassTypep(AstNode* nodep1, AstNode* nodep2) { + // Return the class type that both nodep1 and nodep2 are castable to. + // If both are null, return the type of null constant. + // If one is a class and one is null, return AstClassRefDType that points to that class. + // If no common class type exists, return nullptr. + + // First handle cases with null values and when one class is a super class of the other. + if (VN_IS(nodep1, Const)) std::swap(nodep1, nodep2); + const Castable castable = computeCastable(nodep1->dtypep(), nodep2->dtypep(), nodep2); + if (castable == SAMEISH || castable == COMPATIBLE) { + return nodep1->dtypep()->cloneTree(false); + } else if (castable == DYNAMIC_CLASS) { + return nodep2->dtypep()->cloneTree(false); + } + + AstClassRefDType* classDtypep1 = VN_CAST(nodep1->dtypep(), ClassRefDType); + while (classDtypep1) { + const Castable castable = computeCastable(classDtypep1, nodep2->dtypep(), nodep2); + if (castable == COMPATIBLE) return classDtypep1->cloneTree(false); + AstClassExtends* const extendsp = classDtypep1->classp()->extendsp(); + classDtypep1 = extendsp ? VN_AS(extendsp->dtypep(), ClassRefDType) : nullptr; + } + return nullptr; + } //---------------------------------------------------------------------- // METHODS - casting @@ -7441,7 +7532,7 @@ void V3Width::width(AstNetlist* nodep) { WidthRemoveVisitor rvisitor; (void)rvisitor.mainAcceptEdit(nodep); } // Destruct before checking - V3Global::dumpCheckGlobalTree("width", 0, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("width", 0, dumpTreeLevel() >= 3); } //! Single node parameter propagation @@ -7477,5 +7568,5 @@ AstNode* V3Width::widthGenerateParamsEdit( void V3Width::widthCommit(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { WidthCommitVisitor{nodep}; } // Destruct before checking - V3Global::dumpCheckGlobalTree("widthcommit", 0, dumpTree() >= 6); + V3Global::dumpCheckGlobalTree("widthcommit", 0, dumpTreeLevel() >= 6); } diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 6c6940b0c..9aeb177e1 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -144,6 +144,7 @@ static void process() { if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "Link"); if (v3Global.opt.debugExitUvm()) { V3Error::abortIfErrors(); + if (v3Global.opt.xmlOnly()) V3EmitXml::emitxml(); cout << "--debug-exit-uvm: Exiting after UVM-supported pass\n"; std::exit(0); } @@ -623,6 +624,7 @@ static void verilate(const string& argString) { V3Partition::selfTestNormalizeCosts(); V3Broken::selfTest(); V3ThreadPool::selfTest(); + UINFO(2, "selfTest done\n"); } // Read first filename @@ -635,7 +637,7 @@ static void verilate(const string& argString) { } // Final steps - V3Global::dumpCheckGlobalTree("final", 990, dumpTree() >= 3); + V3Global::dumpCheckGlobalTree("final", 990, dumpTreeLevel() >= 3); V3Error::abortIfErrors(); diff --git a/test_regress/driver.pl b/test_regress/driver.pl index 6bf8240c8..283aaa41e 100755 --- a/test_regress/driver.pl +++ b/test_regress/driver.pl @@ -153,6 +153,9 @@ if ($#opt_tests < 0) { # Run everything @opt_tests = _calc_hashset(@opt_tests) if $opt_hashset; if ($#opt_tests >= 2 && $opt_jobs >= 2) { + # Read supported into master process, so don't call every subprocess + _have_coroutines(); + _have_sc(); # Without this tests such as t_debug_sigsegv_bt_bad.pl will occasionally # block on input and cause a SIGSTOP, then a "fg" was needed to resume testing. if (!$::Have_Forker) { @@ -289,6 +292,35 @@ sub _calc_hashset { return @new; } +####################################################################### +# Verilator utilities + +our %_Verilator_Supported; +sub _verilator_get_supported { + my $feature = shift; + # Returns if given feature is supported + if (!defined $_Verilator_Supported{$feature}) { + my @args = ("perl", "$ENV{VERILATOR_ROOT}/bin/verilator", "-get-supported", $feature); + my $args = join(' ', @args); + my $out = `$args`; + $out or die "couldn't run: $! " . join(' ', @args); + chomp $out; + $_Verilator_Supported{$feature} = ($out =~ /1/ ? 1 : 0); + } + return $_Verilator_Supported{$feature}; +} + +sub _have_coroutines { + return 1 if _verilator_get_supported('COROUTINES'); + return 0; +} + +sub _have_sc { + return 1 if (defined $ENV{SYSTEMC} || defined $ENV{SYSTEMC_INCLUDE} || $ENV{CFG_HAVE_SYSTEMC}); + return 1 if _verilator_get_supported('SYSTEMC'); + return 0; +} + ####################################################################### ####################################################################### ####################################################################### @@ -889,21 +921,24 @@ sub compile_vlt_flags { my %param = (%{$self}, @_); # Default arguments are from $self return 1 if $self->errors || $self->skips; - my $checkflags = join(' ', @{$param{v_flags}}, - @{$param{v_flags2}}, - @{$param{verilator_flags}}, - @{$param{verilator_flags2}}, - @{$param{verilator_flags3}}); + my $checkflags = (' '.join(' ', + @{$param{v_flags}}, + @{$param{v_flags2}}, + @{$param{verilator_flags}}, + @{$param{verilator_flags2}}, + @{$param{verilator_flags3}}) + .' '); die "%Error: specify threads via 'threads =>' argument, not as a command line option" unless ($checkflags !~ /(^|\s)-?-threads\s/); + $self->{coverage} = 1 if ($checkflags =~ /-coverage\b/); + $self->{savable} = 1 if ($checkflags =~ /-savable\b/); $self->{sc} = 1 if ($checkflags =~ /-sc\b/); + $self->{timing} = 1 if ($checkflags =~ / -?-timing\b/ || $checkflags =~ / -?-binary\b/ ); $self->{trace} = ($opt_trace || $checkflags =~ /-trace\b/ || $checkflags =~ /-trace-fst\b/); $self->{trace_format} = (($checkflags =~ /-trace-fst/ && $self->{sc} && 'fst-sc') || ($checkflags =~ /-trace-fst/ && !$self->{sc} && 'fst-c') || ($self->{sc} && 'vcd-sc') || (!$self->{sc} && 'vcd-c')); - $self->{savable} = 1 if ($checkflags =~ /-savable\b/); - $self->{coverage} = 1 if ($checkflags =~ /-coverage\b/); $self->{sanitize} = $opt_sanitize unless exists($self->{sanitize}); $self->{benchmarksim} = 1 if ($param{benchmarksim}); @@ -1113,7 +1148,10 @@ sub compile { $self->skip("Test requires SystemC; ignore error since not installed\n"); return 1; } - + if ($self->{timing} && !$self->have_coroutines) { + $self->skip("Test requires Coroutines; ignore error since not available\n"); + return 1; + } if ($param{verilator_make_cmake} && !$self->have_cmake) { $self->skip("Test requires CMake; ignore error since not available or version too old\n"); return 1; @@ -1464,16 +1502,11 @@ sub sc { } sub have_sc { - my $self = (ref $_[0] ? shift : $Self); - return 1 if (defined $ENV{SYSTEMC} || defined $ENV{SYSTEMC_INCLUDE} || $ENV{CFG_HAVE_SYSTEMC}); - return 1 if $self->verilator_get_supported('SYSTEMC'); - return 0; + return ::_have_sc(); } sub have_coroutines { - my $self = (ref $_[0] ? shift : $Self); - return 1 if $self->verilator_get_supported('COROUTINES'); - return 0; + return ::_have_coroutines(); } sub make_version { @@ -2155,25 +2188,6 @@ sub _read_inputs_vhdl { $fh->close(); } -####################################################################### -# Verilator utilities - -our %_Verilator_Supported; -sub verilator_get_supported { - my $self = (ref $_[0] ? shift : $Self); - my $feature = shift; - # Returns if given feature is supported - if (!defined $_Verilator_Supported{$feature}) { - my @args = ("perl", "$ENV{VERILATOR_ROOT}/bin/verilator", "-get-supported", $feature); - my $args = join(' ', @args); - my $out = `$args`; - $out or die "couldn't run: $! " . join(' ', @args); - chomp $out; - $_Verilator_Supported{$feature} = ($out =~ /1/ ? 1 : 0); - } - return $_Verilator_Supported{$feature}; -} - ####################################################################### # File utilities diff --git a/test_regress/t/t_array_method.out b/test_regress/t/t_array_method.out deleted file mode 100644 index 48e2a0a0f..000000000 --- a/test_regress/t/t_array_method.out +++ /dev/null @@ -1,117 +0,0 @@ -%Error: t/t_array_method.v:24:9: Unknown built-in array method 'sort' - : ... In instance t - 24 | q.sort; - | ^~~~ -%Error: t/t_array_method.v:26:9: Unknown built-in array method 'sort' - : ... In instance t - 26 | q.sort with (item == 2); - | ^~~~ -%Error: t/t_array_method.v:28:9: Unknown built-in array method 'sort' - : ... In instance t - 28 | q.sort(x) with (x == 3); - | ^~~~ -%Error: t/t_array_method.v:31:9: Unknown built-in array method 'rsort' - : ... In instance t - 31 | q.rsort; - | ^~~~~ -%Error: t/t_array_method.v:33:9: Unknown built-in array method 'rsort' - : ... In instance t - 33 | q.rsort with (item == 2); - | ^~~~~ -%Error: t/t_array_method.v:36:14: Unknown built-in array method 'unique' - : ... In instance t - 36 | qv = q.unique; - | ^~~~~~ -%Error: t/t_array_method.v:38:14: Unknown built-in array method 'unique_index' - : ... In instance t - 38 | qi = q.unique_index; qi.sort; - | ^~~~~~~~~~~~ -%Error: t/t_array_method.v:40:9: Unknown built-in array method 'reverse' - : ... In instance t - 40 | q.reverse; - | ^~~~~~~ -%Error: t/t_array_method.v:42:9: Unknown built-in array method 'shuffle' - : ... In instance t - 42 | q.shuffle(); q.sort; - | ^~~~~~~ -%Error: t/t_array_method.v:42:22: Unknown built-in array method 'sort' - : ... In instance t - 42 | q.shuffle(); q.sort; - | ^~~~ -%Error: t/t_array_method.v:47:14: Unknown built-in array method 'find' - : ... In instance t - 47 | qv = q.find with (item == 2); - | ^~~~ -%Error: t/t_array_method.v:49:14: Unknown built-in array method 'find_first' - : ... In instance t - 49 | qv = q.find_first with (item == 2); - | ^~~~~~~~~~ -%Error: t/t_array_method.v:51:14: Unknown built-in array method 'find_last' - : ... In instance t - 51 | qv = q.find_last with (item == 2); - | ^~~~~~~~~ -%Error: t/t_array_method.v:54:14: Unknown built-in array method 'find' - : ... In instance t - 54 | qv = q.find with (item == 20); - | ^~~~ -%Error: t/t_array_method.v:56:14: Unknown built-in array method 'find_first' - : ... In instance t - 56 | qv = q.find_first with (item == 20); - | ^~~~~~~~~~ -%Error: t/t_array_method.v:58:14: Unknown built-in array method 'find_last' - : ... In instance t - 58 | qv = q.find_last with (item == 20); - | ^~~~~~~~~ -%Error: t/t_array_method.v:61:14: Unknown built-in array method 'find_index' - : ... In instance t - 61 | qi = q.find_index with (item == 2); qi.sort; - | ^~~~~~~~~~ -%Error: t/t_array_method.v:63:14: Unknown built-in array method 'find_first_index' - : ... In instance t - 63 | qi = q.find_first_index with (item == 2); - | ^~~~~~~~~~~~~~~~ -%Error: t/t_array_method.v:65:14: Unknown built-in array method 'find_last_index' - : ... In instance t - 65 | qi = q.find_last_index with (item == 2); - | ^~~~~~~~~~~~~~~ -%Error: t/t_array_method.v:68:14: Unknown built-in array method 'find_index' - : ... In instance t - 68 | qi = q.find_index with (item == 20); qi.sort; - | ^~~~~~~~~~ -%Error: t/t_array_method.v:70:14: Unknown built-in array method 'find_first_index' - : ... In instance t - 70 | qi = q.find_first_index with (item == 20); - | ^~~~~~~~~~~~~~~~ -%Error: t/t_array_method.v:72:14: Unknown built-in array method 'find_last_index' - : ... In instance t - 72 | qi = q.find_last_index with (item == 20); - | ^~~~~~~~~~~~~~~ -%Error: t/t_array_method.v:75:14: Unknown built-in array method 'min' - : ... In instance t - 75 | qv = q.min; - | ^~~ -%Error: t/t_array_method.v:77:14: Unknown built-in array method 'max' - : ... In instance t - 77 | qv = q.max; - | ^~~ -%Error: t/t_array_method.v:83:17: 'with' not legal on this method - : ... In instance t - 83 | i = q.sum with (item + 1); do if ((i) !== (32'h11)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method.v",83, (i), (32'h11)); $stop; end while(0);; - | ^~~~ -%Error: t/t_array_method.v:85:21: 'with' not legal on this method - : ... In instance t - 85 | i = q.product with (item + 1); do if ((i) !== (32'h168)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method.v",85, (i), (32'h168)); $stop; end while(0);; - | ^~~~ -%Error: t/t_array_method.v:89:17: 'with' not legal on this method - : ... In instance t - 89 | i = q.and with (item + 1); do if ((i) !== (32'b1001)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method.v",89, (i), (32'b1001)); $stop; end while(0);; - | ^~~~ -%Error: t/t_array_method.v:91:16: 'with' not legal on this method - : ... In instance t - 91 | i = q.or with (item + 1); do if ((i) !== (32'b1111)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method.v",91, (i), (32'b1111)); $stop; end while(0);; - | ^~~~ -%Error: t/t_array_method.v:93:17: 'with' not legal on this method - : ... In instance t - 93 | i = q.xor with (item + 1); do if ((i) !== (32'hb)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method.v",93, (i), (32'hb)); $stop; end while(0);; - | ^~~~ -%Error: Exiting due to diff --git a/test_regress/t/t_array_method.pl b/test_regress/t/t_array_method.pl index be66c40e6..b46d46042 100755 --- a/test_regress/t/t_array_method.pl +++ b/test_regress/t/t_array_method.pl @@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( - 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_array_method.v b/test_regress/t/t_array_method.v index e89003046..5326a500a 100644 --- a/test_regress/t/t_array_method.v +++ b/test_regress/t/t_array_method.v @@ -17,39 +17,39 @@ module t (/*AUTOARG*/); string v; q = '{1, 2, 2, 4, 3}; - v = $sformatf("%p", q); `checks(v, "'{1, 2, 2, 4, 3} "); + v = $sformatf("%p", q); `checks(v, "'{'h1, 'h2, 'h2, 'h4, 'h3} "); // NOT tested: with ... selectors q.sort; - v = $sformatf("%p", q); `checks(v, "'{1, 2, 2, 3, 4} "); + v = $sformatf("%p", q); `checks(v, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); q.sort with (item == 2); - v = $sformatf("%p", q); `checks(v, "'{4, 3, 1, 2, 2} "); + v = $sformatf("%p", q); `checks(v, "'{'h1, 'h3, 'h4, 'h2, 'h2} "); q.sort(x) with (x == 3); - v = $sformatf("%p", q); `checks(v, "'{2, 1, 2, 4, 3} "); + v = $sformatf("%p", q); `checks(v, "'{'h1, 'h4, 'h2, 'h2, 'h3} "); q.rsort; - v = $sformatf("%p", q); `checks(v, "'{4, 3, 2, 2, 1} "); + v = $sformatf("%p", q); `checks(v, "'{'h4, 'h3, 'h2, 'h2, 'h1} "); q.rsort with (item == 2); - v = $sformatf("%p", q); `checks(v, "'{2, 2, 4, 1, 3} "); + v = $sformatf("%p", q); `checks(v, "'{'h2, 'h2, 'h4, 'h3, 'h1} "); qv = q.unique; - v = $sformatf("%p", qv); `checks(v, "'{2, 4, 1, 3} "); + v = $sformatf("%p", qv); `checks(v, "'{'h2, 'h4, 'h3, 'h1} "); qi = q.unique_index; qi.sort; - v = $sformatf("%p", qi); `checks(v, "'{0, 2, 3, 4} "); + v = $sformatf("%p", qi); `checks(v, "'{'h0, 'h2, 'h3, 'h4} "); q.reverse; - v = $sformatf("%p", q); `checks(v, "'{3, 1, 4, 2, 2} "); + v = $sformatf("%p", q); `checks(v, "'{'h1, 'h3, 'h4, 'h2, 'h2} "); q.shuffle(); q.sort; - v = $sformatf("%p", q); `checks(v, "'{1, 2, 2, 3, 4} "); + v = $sformatf("%p", q); `checks(v, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); // These require an with clause or are illegal // TODO add a lint check that with clause is provided qv = q.find with (item == 2); - v = $sformatf("%p", qv); `checks(v, "'{2, 2} "); + v = $sformatf("%p", qv); `checks(v, "'{'h2, 'h2} "); qv = q.find_first with (item == 2); - v = $sformatf("%p", qv); `checks(v, "'{2} "); + v = $sformatf("%p", qv); `checks(v, "'{'h2} "); qv = q.find_last with (item == 2); - v = $sformatf("%p", qv); `checks(v, "'{2} "); + v = $sformatf("%p", qv); `checks(v, "'{'h2} "); qv = q.find with (item == 20); v = $sformatf("%p", qv); `checks(v, "'{}"); @@ -59,11 +59,11 @@ module t (/*AUTOARG*/); v = $sformatf("%p", qv); `checks(v, "'{}"); qi = q.find_index with (item == 2); qi.sort; - v = $sformatf("%p", qi); `checks(v, "'{1, 2} "); + v = $sformatf("%p", qi); `checks(v, "'{'h1, 'h2} "); qi = q.find_first_index with (item == 2); - v = $sformatf("%p", qi); `checks(v, "'{1} "); + v = $sformatf("%p", qi); `checks(v, "'{'h1} "); qi = q.find_last_index with (item == 2); - v = $sformatf("%p", qi); `checks(v, "'{2} "); + v = $sformatf("%p", qi); `checks(v, "'{'h2} "); qi = q.find_index with (item == 20); qi.sort; v = $sformatf("%p", qi); `checks(v, "'{}"); @@ -73,24 +73,19 @@ module t (/*AUTOARG*/); v = $sformatf("%p", qi); `checks(v, "'{}"); qv = q.min; - v = $sformatf("%p", qv); `checks(v, "'{1} "); + v = $sformatf("%p", qv); `checks(v, "'{'h1} "); qv = q.max; - v = $sformatf("%p", qv); `checks(v, "'{4} "); + v = $sformatf("%p", qv); `checks(v, "'{'h4} "); // Reduction methods i = q.sum; `checkh(i, 32'hc); - i = q.sum with (item + 1); `checkh(i, 32'h11); i = q.product; `checkh(i, 32'h30); - i = q.product with (item + 1); `checkh(i, 32'h168); q = '{32'b1100, 32'b1010, 32'b1100, 32'b1010, 32'b1010}; i = q.and; `checkh(i, 32'b1000); - i = q.and with (item + 1); `checkh(i, 32'b1001); i = q.or; `checkh(i, 32'b1110); - i = q.or with (item + 1); `checkh(i, 32'b1111); i = q.xor; `checkh(i, 32'ha); - i = q.xor with (item + 1); `checkh(i, 32'hb); $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_array_method_bad.out b/test_regress/t/t_array_method_bad.out new file mode 100644 index 000000000..34f1fcb09 --- /dev/null +++ b/test_regress/t/t_array_method_bad.out @@ -0,0 +1,5 @@ +%Error: t/t_array_method_bad.v:11:9: Unknown built-in array method 'mex' + : ... In instance t + 11 | q.mex; + | ^~~ +%Error: Exiting due to diff --git a/test_regress/t/t_array_method_bad.pl b/test_regress/t/t_array_method_bad.pl new file mode 100755 index 000000000..1d770f716 --- /dev/null +++ b/test_regress/t/t_array_method_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(simulator => 1); + +compile( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_array_method_bad.v b/test_regress/t/t_array_method_bad.v new file mode 100644 index 000000000..ed8a07cde --- /dev/null +++ b/test_regress/t/t_array_method_bad.v @@ -0,0 +1,16 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module t; + initial begin + int q[5]; + + q.mex; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_array_method_unsup.out b/test_regress/t/t_array_method_unsup.out new file mode 100644 index 000000000..792927193 --- /dev/null +++ b/test_regress/t/t_array_method_unsup.out @@ -0,0 +1,21 @@ +%Error: t/t_array_method_unsup.v:24:17: 'with' not legal on this method + : ... In instance t + 24 | i = q.sum with (item + 1); do if ((i) !== (32'h11)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method_unsup.v",24, (i), (32'h11)); $stop; end while(0);; + | ^~~~ +%Error: t/t_array_method_unsup.v:25:21: 'with' not legal on this method + : ... In instance t + 25 | i = q.product with (item + 1); do if ((i) !== (32'h168)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method_unsup.v",25, (i), (32'h168)); $stop; end while(0);; + | ^~~~ +%Error: t/t_array_method_unsup.v:28:17: 'with' not legal on this method + : ... In instance t + 28 | i = q.and with (item + 1); do if ((i) !== (32'b1001)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method_unsup.v",28, (i), (32'b1001)); $stop; end while(0);; + | ^~~~ +%Error: t/t_array_method_unsup.v:29:16: 'with' not legal on this method + : ... In instance t + 29 | i = q.or with (item + 1); do if ((i) !== (32'b1111)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method_unsup.v",29, (i), (32'b1111)); $stop; end while(0);; + | ^~~~ +%Error: t/t_array_method_unsup.v:30:17: 'with' not legal on this method + : ... In instance t + 30 | i = q.xor with (item + 1); do if ((i) !== (32'hb)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_array_method_unsup.v",30, (i), (32'hb)); $stop; end while(0);; + | ^~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_timing_wait.pl b/test_regress/t/t_array_method_unsup.pl similarity index 57% rename from test_regress/t/t_timing_wait.pl rename to test_regress/t/t_array_method_unsup.pl index ecf974b24..bd07fc421 100755 --- a/test_regress/t/t_timing_wait.pl +++ b/test_regress/t/t_array_method_unsup.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing -Wno-WAITCONST"], - make_main => 0, - ); +compile( + fails => $Self->{vlt_all}, + expect_filename => $Self->{golden_filename}, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ) if !$Self->{vlt_all}; ok(1); 1; diff --git a/test_regress/t/t_array_method_unsup.v b/test_regress/t/t_array_method_unsup.v new file mode 100644 index 000000000..fc6c727f1 --- /dev/null +++ b/test_regress/t/t_array_method_unsup.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, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +`define checks(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); + +module t (/*AUTOARG*/); + initial begin + int q[5]; + int qv[$]; // Value returns + int qi[$]; // Index returns + int i; + string v; + + q = '{1, 2, 2, 4, 3}; + v = $sformatf("%p", q); `checks(v, "'{1, 2, 2, 4, 3} "); + + // Reduction methods + + i = q.sum with (item + 1); `checkh(i, 32'h11); + i = q.product with (item + 1); `checkh(i, 32'h168); + + q = '{32'b1100, 32'b1010, 32'b1100, 32'b1010, 32'b1010}; + i = q.and with (item + 1); `checkh(i, 32'b1001); + i = q.or with (item + 1); `checkh(i, 32'b1111); + i = q.xor with (item + 1); `checkh(i, 32'hb); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_array_pattern_bad3.out b/test_regress/t/t_array_pattern_bad3.out new file mode 100644 index 000000000..dd393b336 --- /dev/null +++ b/test_regress/t/t_array_pattern_bad3.out @@ -0,0 +1,13 @@ +%Error: t/t_array_pattern_bad3.v:20:15: Assignment pattern key used multiple times: 1 + : ... In instance t + 20 | 1: '1}; + | ^ +%Error: t/t_array_pattern_bad3.v:21:13: Assignment pattern with too many elements + : ... In instance t + 21 | arr = '{'0, '1, '0, '1}; + | ^~ +%Error: t/t_array_pattern_bad3.v:22:13: Assignment pattern missed initializing elements: 2 + : ... In instance t + 22 | arr = '{'0, '1}; + | ^~ +%Error: Exiting due to diff --git a/test_regress/t/t_array_pattern_bad3.pl b/test_regress/t/t_array_pattern_bad3.pl new file mode 100755 index 000000000..a60503a1f --- /dev/null +++ b/test_regress/t/t_array_pattern_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 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 => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_array_pattern_bad3.v b/test_regress/t/t_array_pattern_bad3.v new file mode 100644 index 000000000..a646ecca9 --- /dev/null +++ b/test_regress/t/t_array_pattern_bad3.v @@ -0,0 +1,25 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2018 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +// bug1364 + +module t (/*AUTOARG*/ + // Inputs + clk, res + ); + input clk; + input res; + + int arr[3]; + initial begin + arr = '{default: '0, + 1: '0, + 1: '1}; // Bad + arr = '{'0, '1, '0, '1}; // Bad, too many + arr = '{'0, '1}; // Bad, too few + end + +endmodule diff --git a/test_regress/t/t_array_query_with.v b/test_regress/t/t_array_query_with.v index bb4fa6137..d69ddfa42 100644 --- a/test_regress/t/t_array_query_with.v +++ b/test_regress/t/t_array_query_with.v @@ -4,11 +4,24 @@ // any use, without warranty, 2022 by Antmicro Ltd. // SPDX-License-Identifier: CC0-1.0 -module t (/*AUTOARG*/ - clk - ); +class Cls; + static function bit get_true(); + return 1'b1; + endfunction - input clk; + static function bit test_find_index_in_class(); + if (get_true) begin + int q[$] = {0, -1, 3, 1, 4, 1}; + int found_idx[$]; + found_idx = q.find_index(node) with (node == 1); + return found_idx[0] == 3; + end + return 0; + endfunction +endclass + +module t (/*AUTOARG*/ + ); function bit test_find; string bar[$]; @@ -31,22 +44,31 @@ module t (/*AUTOARG*/ return first_even_idx[0] == 1; endfunction + function bit is_even(int a); + return a % 2 == 0; + endfunction + + function static bit test_find_first_index_by_func; + int q[] = {1, 2, 3, 4, 5, 6}; + int first_even_idx[$] = q.find_first_index(x) with (is_even(x)); + return first_even_idx[0] == 1; + endfunction + function automatic bit test_sort; int q[] = {-5, 2, -3, 0, 4}; q.sort(x) with (x >= 0 ? x : -x); return q[1] == 2; endfunction - always @(posedge clk) begin - bit [3:0] results = {test_find(), test_find_index(), - test_find_first_index(), test_sort()}; - if (results == '1) begin - $write("*-* All Finished *-*\n"); - $finish; - end - else begin - $write("Results: %b\n", results); - $stop; - end + initial begin + if (!test_find()) $stop; + if (!test_find_index()) $stop; + if (!test_find_first_index()) $stop; + if (!test_find_first_index_by_func()) $stop; + if (!test_sort()) $stop; + if (!Cls::test_find_index_in_class()) $stop; + + $write("*-* All Finished *-*\n"); + $finish; end endmodule diff --git a/test_regress/t/t_array_type_methods.v b/test_regress/t/t_array_type_methods.v index 1ee503146..ca6d5e204 100644 --- a/test_regress/t/t_array_type_methods.v +++ b/test_regress/t/t_array_type_methods.v @@ -14,6 +14,7 @@ module t (/*AUTOARG*/ input clk; logic [3:0] foo [1:0]; + logic [3:0] fooe [1:0]; initial begin foo[0] = 4'b0101; foo[1] = 4'b0011; @@ -24,6 +25,10 @@ module t (/*AUTOARG*/ `checkh(foo.sum, 4'b1000); `checkh(foo.product, 4'b1111); + fooe[0] = 4'b0101; + fooe[1] = 4'b0011; + if (foo != fooe) $stop; + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_assoc_method.v b/test_regress/t/t_assoc_method.v index e4d87f611..0bb9f707c 100644 --- a/test_regress/t/t_assoc_method.v +++ b/test_regress/t/t_assoc_method.v @@ -139,6 +139,11 @@ module t (/*AUTOARG*/); i = qe.xor(); `checkh(i, 32'b0); + q = '{10:1, 11:2}; + qe = '{10:1, 11:2}; + `checkh(q == qe, 1'b1); + `checkh(q != qe, 1'b0); + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_class_assign_cond.pl b/test_regress/t/t_class_assign_cond.pl new file mode 100755 index 000000000..1aa73f80a --- /dev/null +++ b/test_regress/t/t_class_assign_cond.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_class_assign_cond.v b/test_regress/t/t_class_assign_cond.v new file mode 100644 index 000000000..4d78e3a68 --- /dev/null +++ b/test_regress/t/t_class_assign_cond.v @@ -0,0 +1,70 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +class Cls; + int f; + function new(int x); + f = x; + endfunction +endclass + +class ExtendCls extends Cls; + function new(int x); + super.new(x); + endfunction +endclass + +class AnotherExtendCls extends Cls; + function new(int x); + super.new(x); + endfunction +endclass + +class ExtendExtendCls extends ExtendCls; + function new(int x); + super.new(x); + endfunction +endclass + +module t (/*AUTOARG*/); + initial begin + Cls cls1 = null, cls2 = null; + ExtendCls ext_cls = null; + AnotherExtendCls an_ext_cls = null; + ExtendExtendCls ext_ext_cls = null; + + cls1 = (cls1 == null) ? cls2 : cls1; + if (cls1 != null) $stop; + + cls1 = new(1); + cls1 = (cls1 == null) ? cls2 : cls1; + if (cls1.f != 1) $stop; + + cls1 = (cls1 != null) ? cls2 : cls1; + if (cls1 != null) $stop; + + cls1 = new(1); + cls2 = new(2); + cls1 = (cls1 != null) ? cls2 : cls1; + if (cls1.f != 2) $stop; + + cls1 = null; + cls1 = (ext_cls != null) ? ext_cls : cls2; + if (cls1.f != 2) $stop; + + ext_cls = new(3); + cls1 = (ext_cls != null) ? ext_cls : cls2; + if (cls1.f != 3) $stop; + + ext_ext_cls = new(4); + an_ext_cls = new(5); + cls1 = (ext_ext_cls.f != 4) ? ext_ext_cls : an_ext_cls; + if (cls1.f != 5) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_assign_cond_bad.out b/test_regress/t/t_class_assign_cond_bad.out new file mode 100644 index 000000000..d0d347f17 --- /dev/null +++ b/test_regress/t/t_class_assign_cond_bad.out @@ -0,0 +1,21 @@ +%Error: t/t_class_assign_cond_bad.v:22:25: Incompatible types of operands of condition operator: CLASSREFDTYPE 'Cls1' and CLASSREFDTYPE 'Cls2' + : ... In instance t + 22 | c1 = (c1 != null) ? c1 : c2; + | ^ +%Error: t/t_class_assign_cond_bad.v:23:10: Assign RHS expects a CLASSREFDTYPE 'Cls1', got CLASSREFDTYPE 'Cls2' + : ... In instance t + 23 | c1 = (c1 != null) ? c2 : c2; + | ^ +%Error: t/t_class_assign_cond_bad.v:24:25: Incompatible types of operands of condition operator: BASICDTYPE 'logic' and CLASSREFDTYPE 'Cls2' + : ... In instance t + 24 | c2 = (c1 == null) ? 1'b1 : c2; + | ^ +%Error: t/t_class_assign_cond_bad.v:24:10: Assign RHS expects a CLASSREFDTYPE 'Cls2', got BASICDTYPE 'logic' + : ... In instance t + 24 | c2 = (c1 == null) ? 1'b1 : c2; + | ^ +%Error: t/t_class_assign_cond_bad.v:25:29: Incompatible types of operands of condition operator: CLASSREFDTYPE 'ExtCls1' and CLASSREFDTYPE 'Cls1' + : ... In instance t + 25 | ext_c1 = (c1 == null) ? ext_c1 : c1; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_class_assign_cond_bad.pl b/test_regress/t/t_class_assign_cond_bad.pl new file mode 100755 index 000000000..430290c0a --- /dev/null +++ b/test_regress/t/t_class_assign_cond_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(simulator => 1); + +compile( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_class_assign_cond_bad.v b/test_regress/t/t_class_assign_cond_bad.v new file mode 100644 index 000000000..a016c832d --- /dev/null +++ b/test_regress/t/t_class_assign_cond_bad.v @@ -0,0 +1,27 @@ +// 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; +endclass + +class Cls2; +endclass + +class ExtCls1; +endclass + +module t (/*AUTOARG*/); + Cls1 c1; + Cls2 c2; + ExtCls1 ext_c1; + + initial begin + c1 = (c1 != null) ? c1 : c2; + c1 = (c1 != null) ? c2 : c2; + c2 = (c1 == null) ? 1'b1 : c2; + ext_c1 = (c1 == null) ? ext_c1 : c1; + end +endmodule diff --git a/test_regress/t/t_class_extends_param.v b/test_regress/t/t_class_extends_param.v index aaaedce8a..f84fe1fa2 100644 --- a/test_regress/t/t_class_extends_param.v +++ b/test_regress/t/t_class_extends_param.v @@ -63,12 +63,23 @@ module t (/*AUTOARG*/ endfunction endclass + class FooDict; + Foo q[int]; + endclass + + class ExtendFooDict#(type BASE=FooDict) extends BASE; + function int get_x_of_item(int i); + return q[i].x; + 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; + ExtendFooDict extend_foo_dict_i; initial begin bar_foo_i = new; @@ -77,18 +88,26 @@ module t (/*AUTOARG*/ 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 && - 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 - else begin - $stop; - end + extend_foo_dict_i = new; + extend_foo_dict_i.q[1] = new; + + if (bar_foo_i.get_x() != 1) $stop; + if (bar_foo_i.get_3() != 3) $stop; + if (bar_baz_i.get_x() != 2) $stop; + if (bar_baz_i.get_4() != 4) $stop; + if (extend_bar_i.get_x() != 1) $stop; + if (extend_bar_i.get_6() != 6) $stop; + if (extend_bar_i.get_x() != 1) $stop; + if (extend_bar_i.get_6() != 6) $stop; + if (extend_bar1_i.get_x() != 1) $stop; + if (extend_bar1_i.get_6() != 6) $stop; + if (extend_bar_baz_i.get_x() != 2) $stop; + if (extend_bar_baz_i.get_8() != 8) $stop; + if (extend_extend_bar_i.get_x() != 1) $stop; + if (extend_extend_bar_i.get_12() != 12) $stop; + if (extend_foo_dict_i.get_x_of_item(1) != 1) $stop; + + $write("*-* All Finished *-*\n"); + $finish; end endmodule diff --git a/test_regress/t/t_class_extern.v b/test_regress/t/t_class_extern.v index 94a71825f..0cddfdc3f 100644 --- a/test_regress/t/t_class_extern.v +++ b/test_regress/t/t_class_extern.v @@ -9,6 +9,7 @@ class Cls; extern function int ext_f_np; extern function int ext_f_p(); extern function int ext_f_i(int in); + extern static function int get_1(); extern task ext_t_np; extern task ext_t_p(); extern task ext_t_i(int in); @@ -26,6 +27,10 @@ function int Cls::ext_f_i(int in); return in+1; endfunction +function int Cls::get_1(); + return 1; +endfunction + task Cls::ext_t_np(); $write("*-* All Finished *-*\n"); endtask @@ -46,5 +51,6 @@ module t (/*AUTOARG*/); if (c.ext_f_np() != 1) $stop; if (c.ext_f_p() != 2) $stop; if (c.ext_f_i(10) != 11) $stop; + if (Cls::get_1() != 1) $stop; end endmodule diff --git a/test_regress/t/t_class_func_dot.pl b/test_regress/t/t_class_func_dot.pl new file mode 100755 index 000000000..aabcde63e --- /dev/null +++ b/test_regress/t/t_class_func_dot.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_func_dot.v b/test_regress/t/t_class_func_dot.v new file mode 100644 index 000000000..4844009a0 --- /dev/null +++ b/test_regress/t/t_class_func_dot.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 Cls_report_object; + string m_msg; + function string get_msg; + return m_msg; + endfunction +endclass + +function Cls_report_object get_report_object; + Cls_report_object c; + c = new; + c.m_msg = "hello"; + return c; +endfunction + +module t (/*AUTOARG*/); + + string s; + + initial begin + Cls_report_object _local_report_object; + s = get_report_object().get_msg(); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_param_enum.pl b/test_regress/t/t_class_param_enum.pl new file mode 100755 index 000000000..1aa73f80a --- /dev/null +++ b/test_regress/t/t_class_param_enum.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_class_param_enum.v b/test_regress/t/t_class_param_enum.v new file mode 100644 index 000000000..a8d499f47 --- /dev/null +++ b/test_regress/t/t_class_param_enum.v @@ -0,0 +1,25 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +typedef enum bit {A = 0, B = 1} enum_t; + +class Converter #(type T); + function int toInt(T t); + return int'(t); + endfunction +endclass + +module t; + initial begin + Converter#(enum_t) conv1 = new; + Converter#(bit) conv2 = new; + if (conv1.toInt(A) != 0) $stop; + if (conv2.toInt(1) != 1) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_param_enum_bad.out b/test_regress/t/t_class_param_enum_bad.out new file mode 100644 index 000000000..fd4e547e9 --- /dev/null +++ b/test_regress/t/t_class_param_enum_bad.out @@ -0,0 +1,11 @@ +%Error: t/t_class_param_enum_bad.v:20:31: Assign RHS expects a CLASSREFDTYPE 'Converter__Tz2', got CLASSREFDTYPE 'Converter__Tz1' + : ... In instance t + 20 | Converter#(bit) conv2 = conv1; + | ^~~~~ +%Error-ENUMVALUE: t/t_class_param_enum_bad.v:21:19: Implicit conversion to enum 'T' from 'logic[31:0]' (IEEE 1800-2017 6.19.3) + : ... In instance t + : ... Suggest use enum's mnemonic, or static cast + 21 | conv1.toInt(0); + | ^ + ... For error description see https://verilator.org/warn/ENUMVALUE?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_class_param_enum_bad.pl b/test_regress/t/t_class_param_enum_bad.pl new file mode 100755 index 000000000..430290c0a --- /dev/null +++ b/test_regress/t/t_class_param_enum_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(simulator => 1); + +compile( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_class_param_enum_bad.v b/test_regress/t/t_class_param_enum_bad.v new file mode 100644 index 000000000..55b5e824c --- /dev/null +++ b/test_regress/t/t_class_param_enum_bad.v @@ -0,0 +1,24 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +typedef enum bit {A = 0, B = 1} enum_t; + +class Converter #(type T); + function int toInt(T t); + return int'(t); + endfunction +endclass + +module t; + initial begin + Converter#(enum_t) conv1 = new; + // enum types does not match with other types (sections 6.22.1 and 6.22.4 of IEEE Std 1800-2017) + // The assignment and the function call should throw an error. + Converter#(bit) conv2 = conv1; + conv1.toInt(0); + $stop; + end +endmodule diff --git a/test_regress/t/t_class_param_extends.v b/test_regress/t/t_class_param_extends.v index 1c1078ea7..5e59c09d2 100644 --- a/test_regress/t/t_class_param_extends.v +++ b/test_regress/t/t_class_param_extends.v @@ -21,16 +21,55 @@ endclass typedef Cls#(8) Cls8_t; +class Getter1; + function int get_int; + return 1; + endfunction +endclass + +class Getter2; + function int get_int; + return 2; + endfunction +endclass + +class Foo #(type T=Getter1); + T foo_field; + int x; + function new(int y); + foo_field = new; + x = y; + endfunction +endclass + +class Bar #(type S=Getter2) extends Foo#(S); + T field; + function new(int y); + super.new(y); + field = new; + endfunction + + function int get_field_int; + return field.get_int(); + endfunction + + function int get_foo_field_int; + return foo_field.get_int(); + endfunction +endclass + // See also t_class_param_mod.v module t (/*AUTOARG*/); Cls #(.P(4)) c4; Cls8_t c8; + Bar b; initial begin c4 = new; c8 = new; + b = new(1); if (c4.PBASE != 4) $stop; if (c8.PBASE != 8) $stop; if (c4.get_p() != 4) $stop; @@ -46,6 +85,10 @@ module t (/*AUTOARG*/); $display("c4 = %s", $sformatf("%p", c4)); if ($sformatf("%p", c4) != "'{member:'ha}") $stop; + if (b.x != 1) $stop; + if (b.get_field_int() != 2) $stop; + if (b.get_foo_field_int() != 2) $stop; + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_class_param_extends2.pl b/test_regress/t/t_class_param_extends2.pl new file mode 100755 index 000000000..1aa73f80a --- /dev/null +++ b/test_regress/t/t_class_param_extends2.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_class_param_extends2.v b/test_regress/t/t_class_param_extends2.v new file mode 100644 index 000000000..f4420d0d0 --- /dev/null +++ b/test_regress/t/t_class_param_extends2.v @@ -0,0 +1,38 @@ +// 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=bit); + int x = $bits(T); +endclass + +class Bar #(type S=int) extends Foo#(S); +endclass + +typedef Bar#() bar_default_t; + +class Baz; + Bar#(logic[7:0]) bar_string; + int bar_x; + function new; + bar_string = new; + bar_x = bar_string.x; + endfunction +endclass + +typedef Baz baz_t; + +module t; + initial begin + bar_default_t bar_default = new; + baz_t baz = new; + + if (bar_default.x != 32) $stop; + if (baz.bar_x != 8) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_param_func_return.pl b/test_regress/t/t_class_param_func_return.pl new file mode 100755 index 000000000..1aa73f80a --- /dev/null +++ b/test_regress/t/t_class_param_func_return.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_class_param_func_return.v b/test_regress/t/t_class_param_func_return.v new file mode 100644 index 000000000..9f3185d7c --- /dev/null +++ b/test_regress/t/t_class_param_func_return.v @@ -0,0 +1,45 @@ +// 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=int); + typedef Foo default_type; + typedef Foo#(T) this_type; + + T x; + + function default_type get_default(); + default_type o = new; + return o; + endfunction + + function this_type get_this(); + this_type o = new; + return o; + endfunction +endclass + +module t (/*AUTOARG*/); + + Foo f_def1, f_def2; + Foo#(bit) f_bit1, f_bit2; + initial begin + f_def1 = new; + f_bit1 = new; + + f_def2 = f_def1.get_default(); + if ($bits(f_def2.x) != 32) $stop; + f_def2 = f_def1.get_this(); + if ($bits(f_def2.x) != 32) $stop; + + f_def2 = f_bit1.get_default(); + if ($bits(f_def2.x) != 32) $stop; + f_bit2 = f_bit1.get_this(); + if ($bits(f_bit2.x) != 1) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_param_typedef.pl b/test_regress/t/t_class_param_typedef.pl new file mode 100755 index 000000000..1aa73f80a --- /dev/null +++ b/test_regress/t/t_class_param_typedef.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_class_param_typedef.v b/test_regress/t/t_class_param_typedef.v new file mode 100644 index 000000000..0218b205a --- /dev/null +++ b/test_regress/t/t_class_param_typedef.v @@ -0,0 +1,47 @@ +// 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=int); + typedef Foo#(T) this_type; + function int get_x; + return T::get_s_x(); + endfunction +endclass + +class Bar #(type S=int); + typedef Bar #(S) this_type; + typedef Foo#(this_type) foo_type; + function int get_x; + foo_type f = new; + return f.get_x(); + endfunction + static function int get_s_x; + return S::x; + endfunction +endclass + +class Cls1; + static int x = 1; + typedef Bar#(Cls1) type_id; +endclass + +class Cls2; + static int x = 2; + typedef Bar#(Cls2) type_id; +endclass + +module t; + initial begin + Cls1::type_id bar1 = new; + Cls2::type_id bar2 = new; + + if (bar1.get_x() != 1) $stop; + if (bar2.get_x() != 2) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_super_new2.pl b/test_regress/t/t_class_super_new2.pl new file mode 100755 index 000000000..859050d63 --- /dev/null +++ b/test_regress/t/t_class_super_new2.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_super_new2.v b/test_regress/t/t_class_super_new2.v new file mode 100644 index 000000000..95a01b683 --- /dev/null +++ b/test_regress/t/t_class_super_new2.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 Tudor Timi. +// SPDX-License-Identifier: CC0-1.0 + +class svunit_base; + function new(string name); + endfunction +endclass + +class svunit_testcase extends svunit_base; + function new(string name); + super.new(name); + endfunction +endclass + +module dut_unit_test; + svunit_testcase svunit_ut = new("dut_ut"); +endmodule + +module t(/*AUTOARG*/); + + dut_unit_test dut_ut(); + + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_class_typedef.v b/test_regress/t/t_class_typedef.v index ef6c86744..e5034a601 100644 --- a/test_regress/t/t_class_typedef.v +++ b/test_regress/t/t_class_typedef.v @@ -4,18 +4,41 @@ // any use, without warranty, 2020 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 -// Parse check class uvm_resource_types; typedef int rsrc_q_t; endclass + class uvm_resource_pool; uvm_resource_types::rsrc_q_t rtab [string]; +endclass - uvm_resource_types#(1,2,3)::rsrc_q_t rtab_paramed [string]; +virtual class C#(parameter type T = logic, parameter SIZE = 1); + typedef logic [SIZE-1:0] t_vector; + typedef T t_array [SIZE-1:0]; + typedef struct { + t_vector m0 [2*SIZE-1:0]; + t_array m1; + } t_struct; endclass module t (/*AUTOARG*/); initial begin + uvm_resource_pool pool = new; + typedef logic [7:0] t_t0; + C#(t_t0,3)::t_vector v0; + C#(t_t0,3)::t_array a0; + C#(bit,4)::t_struct s0; + + pool.rtab["a"] = 1; + if ($bits(pool.rtab["a"]) != 32) $stop; + + if ($bits(v0) != 3) $stop; + if ($size(a0) != 3) $stop; + if ($bits(a0[0]) != 8) $stop; + if ($size(s0.m0) != 8) $stop; + if ($size(s0.m1) != 4) $stop; + if ($bits(s0.m1[2]) != 1) $stop; + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_class_wide.pl b/test_regress/t/t_class_wide.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_class_wide.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_class_wide.v b/test_regress/t/t_class_wide.v new file mode 100644 index 000000000..a61944960 --- /dev/null +++ b/test_regress/t/t_class_wide.v @@ -0,0 +1,29 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Jomit626. +// SPDX-License-Identifier: CC0-1.0 + +`ifndef WIDTH +`define WIDTH 128 +`endif + +class item; + bit [`WIDTH-1:0] data; +endclass + +module t (); + logic [`WIDTH-1:0] data; + item item0 = new; + + initial begin + item0.data = `WIDTH'hda7ada7a; + data = item0.data; + + if (data != `WIDTH'hda7ada7a) + $stop(); + + $write("*-* All Finished *-*\n"); + $finish(); + end +endmodule diff --git a/test_regress/t/t_clocked_release_combo.pl b/test_regress/t/t_clocked_release_combo.pl new file mode 100755 index 000000000..859050d63 --- /dev/null +++ b/test_regress/t/t_clocked_release_combo.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_clocked_release_combo.v b/test_regress/t/t_clocked_release_combo.v new file mode 100644 index 000000000..fe0605223 --- /dev/null +++ b/test_regress/t/t_clocked_release_combo.v @@ -0,0 +1,51 @@ +// 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 + +// verilator lint_off MULTIDRIVEN +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + logic [31:0] lhs1, lhs2, rhs; + logic cond = 0; + + always_comb lhs1 = rhs; + assign lhs2 = rhs; + + always @(posedge clk) rhs = '1; + + always @(negedge clk) begin + if (cond) begin + force lhs1 = 'hdeadbeef; + force lhs2 = 'hfeedface; + end + else begin + release lhs1; + release lhs2; + end + end + + int cyc = 0; + always @(posedge clk) begin + cyc <= cyc + 1; + if (cyc == 0) cond <= 1; + if (cyc == 3) cond <= 0; + if (cyc > 1 && cyc < 4) begin + if (lhs1 != 'hdeadbeef) $stop; + if (lhs2 != 'hfeedface) $stop; + end + if (cyc > 4 && cyc < 8) begin + if (lhs1 != '1) $stop; + if (lhs2 != '1) $stop; + end + if (cyc >= 8) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_clocking_sched_timing.pl b/test_regress/t/t_clocking_sched_timing.pl index 7fe0b1dee..1d537460a 100755 --- a/test_regress/t/t_clocking_sched_timing.pl +++ b/test_regress/t/t_clocking_sched_timing.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,22 +10,17 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_clocking_sched.v"); +top_filename("t/t_clocking_sched.v"); - compile( - timing_loop => 1, - verilator_flags2 => ["--timing"], - ); +compile( + timing_loop => 1, + verilator_flags2 => ["--timing"], + ); - execute( - check_finished => 1, - expect_filename => $Self->{golden_filename} - ); -} +execute( + check_finished => 1, + expect_filename => $Self->{golden_filename} + ); ok(1); 1; diff --git a/test_regress/t/t_clocking_timing1.pl b/test_regress/t/t_clocking_timing1.pl index ca8f6fa4e..671334c7d 100755 --- a/test_regress/t/t_clocking_timing1.pl +++ b/test_regress/t/t_clocking_timing1.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,21 +10,16 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_clocking_timing.v"); +top_filename("t/t_clocking_timing.v"); - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_clocking_timing2.pl b/test_regress/t/t_clocking_timing2.pl index 6dff6484b..ad6f5308f 100755 --- a/test_regress/t/t_clocking_timing2.pl +++ b/test_regress/t/t_clocking_timing2.pl @@ -10,21 +10,16 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_clocking_timing.v"); +top_filename("t/t_clocking_timing.v"); - compile( - verilator_flags2 => ["--exe --main --timing -DTEST_INPUT_SKEW=12 -DTEST_OUTPUT_SKEW=16"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing -DTEST_INPUT_SKEW=12 -DTEST_OUTPUT_SKEW=16"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_const_opt.v b/test_regress/t/t_const_opt.v index 843bd8870..cfdcc3917 100644 --- a/test_regress/t/t_const_opt.v +++ b/test_regress/t/t_const_opt.v @@ -152,7 +152,7 @@ module bug3182(in, out); bit_source = c_fake_dependency() | in; wire [5:0] tmp = bit_source; // V3Gate should inline this - wire out = ~(tmp >> 5) & (bit_source == 5'd10); + assign out = ~(tmp >> 5) & (bit_source == 5'd10); /* verilator lint_on WIDTH */ endmodule @@ -374,7 +374,6 @@ endmodule // 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]}}; diff --git a/test_regress/t/t_cuse_forward.pl b/test_regress/t/t_cuse_forward.pl new file mode 100755 index 000000000..f17d7f547 --- /dev/null +++ b/test_regress/t/t_cuse_forward.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(vlt => 1); + +compile( + ); + +ok(1); +1; diff --git a/test_regress/t/t_cuse_forward.v b/test_regress/t/t_cuse_forward.v new file mode 100644 index 000000000..c1178a2d7 --- /dev/null +++ b/test_regress/t/t_cuse_forward.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +class Baz; +endclass + +class Bar#(type T) extends T; +endclass + +class Foo; + typedef struct { + int field; + } Zee; + + task t1(); + // Refer to Baz CLASSREFDTYPE node in implementation (via CLASSEXTENDS) + Bar#(Baz) b = new; + endtask + // Refer to the very same Baz CLASSREFDTYPE node again, this time within interface + task t2(Bar#(Baz)::T b); + endtask +endclass + +class Moo; + // Use Foo::Zee to cause inclusion of Foo's header file + Foo::Zee z; +endclass + +module t(); + initial begin + // Use Moo in top module to add Moo to root, causing inclusion of Foo header into + // root header. + Moo moo; + moo = new; + end +endmodule diff --git a/test_regress/t/t_cxx_equal_to.pl b/test_regress/t/t_cxx_equal_to.pl new file mode 100755 index 000000000..efd6274f5 --- /dev/null +++ b/test_regress/t/t_cxx_equal_to.pl @@ -0,0 +1,27 @@ +#!/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); + +top_filename("t/t_cxx_equal_to.v"); + +compile( + verilator_flags2 => ['--binary --timing --trace'], + verilator_make_cmake => 0, + verilator_make_gmake => 0, + make_main => 0, + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_cxx_equal_to.v b/test_regress/t/t_cxx_equal_to.v new file mode 100644 index 000000000..e35c8d6d6 --- /dev/null +++ b/test_regress/t/t_cxx_equal_to.v @@ -0,0 +1,74 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// On some platforms (i.e. FreeBSD 12) this triggered: +// +// Active region did not converge. +// +// due to the mistaken belief that the AstVarScope node for TOP->t__DOT__clk +// is equal to the AstVarScope node for TOP->t__DOT__rst. This occured because +// AstVarScope was missing an appropriate same method and is tickled by the LLVM +// libcxx library. +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by John Wehle. +// SPDX-License-Identifier: CC0-1.0 + +module t; + + wire [1:0] out; + reg in; + reg rst; + reg clk; + + initial begin + clk = 0; + rst = 0; + + #10 rst = 1; + #10 rst = 0; + + in = 1'b0; + + #30 $write("*-* All Finished *-*\n"); + $finish; + end + + always begin + #10 clk <= !clk; + end + + Test test(.out(out), .in(in), + .clk(clk), .rst(rst)); +endmodule + + +module Test(/*AUTOARG*/ + // Outputs + out, + // Inputs + clk, in, rst + ); + + input clk; + input in; + input rst; + output wire [1:0] out; + + reg [1:0] s; + reg sin; + + assign out = s; + + always @(posedge clk) + begin + s[1] <= in; + s[0] <= sin; + end + + always @(negedge clk, posedge rst) + if (rst) + sin <= 1'b0; + else + sin <= in; + +endmodule diff --git a/test_regress/t/t_debug_inputs.pl b/test_regress/t/t_debug_inputs.pl new file mode 100755 index 000000000..9b9054d09 --- /dev/null +++ b/test_regress/t/t_debug_inputs.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(vlt => 1); + +lint( + v_flags => ["--debug --debugi 1 -Wno-MULTITOP t/t_debug_inputs_b.v"], + ); + +file_grep("$Self->{obj_dir}/V$Self->{name}__inputs.vpp", qr/module t_debug_inputs /); +file_grep("$Self->{obj_dir}/V$Self->{name}__inputs.vpp", qr/module t_debug_inputs_a /); +file_grep("$Self->{obj_dir}/V$Self->{name}__inputs.vpp", qr/module t_debug_inputs_b /); + +ok(1); +1; diff --git a/test_regress/t/t_debug_inputs.v b/test_regress/t/t_debug_inputs.v new file mode 100644 index 000000000..0e4f4c44d --- /dev/null +++ b/test_regress/t/t_debug_inputs.v @@ -0,0 +1,11 @@ +// DESCRIPTION: Verilator: Dotted reference that uses another dotted reference +// as the select expression +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`include "t/t_debug_inputs_a.v" + +module t_debug_inputs (/*AUTOARG*/); +endmodule diff --git a/test_regress/t/t_debug_inputs_a.v b/test_regress/t/t_debug_inputs_a.v new file mode 100644 index 000000000..7754ce73a --- /dev/null +++ b/test_regress/t/t_debug_inputs_a.v @@ -0,0 +1,9 @@ +// DESCRIPTION: Verilator: Dotted reference that uses another dotted reference +// as the select expression +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t_debug_inputs_a (/*AUTOARG*/); +endmodule diff --git a/test_regress/t/t_debug_inputs_b.v b/test_regress/t/t_debug_inputs_b.v new file mode 100644 index 000000000..7321188ab --- /dev/null +++ b/test_regress/t/t_debug_inputs_b.v @@ -0,0 +1,9 @@ +// DESCRIPTION: Verilator: Dotted reference that uses another dotted reference +// as the select expression +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t_debug_inputs_b (/*AUTOARG*/); +endmodule diff --git a/test_regress/t/t_delay_incr_timing.pl b/test_regress/t/t_delay_incr_timing.pl index 6b78f49a3..7bc99d468 100755 --- a/test_regress/t/t_delay_incr_timing.pl +++ b/test_regress/t/t_delay_incr_timing.pl @@ -12,21 +12,16 @@ scenarios(simulator => 1); $Self->{main_time_multiplier} = 10e-7 / 10e-9; -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_delay_incr.v"); +top_filename("t/t_delay_incr.v"); - compile( - timing_loop => 1, - verilator_flags2 => ['--timing -Wno-ZERODLY'], - ); +compile( + timing_loop => 1, + verilator_flags2 => ['--timing -Wno-ZERODLY'], + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_delay_timing.pl b/test_regress/t/t_delay_timing.pl index 505ebf865..9514aafb4 100755 --- a/test_regress/t/t_delay_timing.pl +++ b/test_regress/t/t_delay_timing.pl @@ -12,21 +12,16 @@ scenarios(simulator => 1); $Self->{main_time_multiplier} = 10e-7 / 10e-9; -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_delay.v"); +top_filename("t/t_delay.v"); - compile( - timing_loop => 1, - verilator_flags2 => ['--timing -Wno-ZERODLY'], - ); +compile( + timing_loop => 1, + verilator_flags2 => ['--timing -Wno-ZERODLY'], + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_delay_var.pl b/test_regress/t/t_delay_var.pl index c0d64fbeb..68a8b13cd 100755 --- a/test_regress/t/t_delay_var.pl +++ b/test_regress/t/t_delay_var.pl @@ -10,21 +10,16 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - fails => $Self->{vlt}, - expect_filename => $Self->{golden_filename}, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + fails => $Self->{vlt}, + expect_filename => $Self->{golden_filename}, + ); - execute( - check_finished => 1, - ) if !$Self->{vlt}; -} +execute( + check_finished => 1, + ) if !$Self->{vlt}; ok(1); 1; diff --git a/test_regress/t/t_dist_attributes_bad.h b/test_regress/t/t_dist_attributes_bad.h index 5b9ed95b7..7473d57ff 100644 --- a/test_regress/t/t_dist_attributes_bad.h +++ b/test_regress/t/t_dist_attributes_bad.h @@ -345,6 +345,27 @@ class ConstructorCallsLocalCallsClassGlobal { public: ConstructorCallsLocalCallsClassGlobal() { local_function(); } }; +class DummyClass2 { +public: + void dummy_function2() {} +}; +class DummyClass { +public: + DummyClass2 d; + void dummy_function() {} +}; +DummyClass dummyGlobalVar; +class ConstructorCallsGlobalObject { + +public: + ConstructorCallsGlobalObject() { dummyGlobalVar.dummy_function(); } +}; + +class ConstructorCallsGlobalObjectMember { + +public: + ConstructorCallsGlobalObjectMember() { dummyGlobalVar.d.dummy_function2(); } +}; class TestClassConstructor { void safe_function_unsafe_constructor_bad() VL_MT_SAFE { @@ -381,6 +402,12 @@ class TestClassConstructor { void safe_function_calls_constructor_local_calls_class_global_bad() VL_MT_SAFE { ConstructorCallsLocalCallsClassGlobal f{}; } + void safe_function_calls_constructor_global_object_bad() VL_MT_STABLE { + ConstructorCallsGlobalObject f{}; + } + void safe_function_calls_constructor_global_object_member_bad() VL_MT_STABLE { + ConstructorCallsGlobalObjectMember f{}; + } }; #endif // T_DIST_ATTRIBUTES_BAD_H_ diff --git a/test_regress/t/t_dist_attributes_bad.out b/test_regress/t/t_dist_attributes_bad.out index fea14b7b0..f302da187 100644 --- a/test_regress/t/t_dist_attributes_bad.out +++ b/test_regress/t/t_dist_attributes_bad.out @@ -724,32 +724,40 @@ t/t_dist_attributes_bad.cpp:75: [release] TestClass t/t_dist_attributes_bad.h:124: [] TestClass::scm_ua_VL_REQUIRES(VerilatedMutex &) [declaration] t/t_dist_attributes_bad.cpp:75: [requires] TestClass::scm_ua_VL_REQUIRES(VerilatedMutex &) +%Error: "TestClassConstructor::safe_function_calls_constructor_global_object_bad()" is stable_tree but calls non-stable_tree or non-mtsafe +t/t_dist_attributes_bad.h:405: [stable_tree] TestClassConstructor::safe_function_calls_constructor_global_object_bad() +t/t_dist_attributes_bad.h:355: [] DummyClass::dummy_function() + +%Error: "TestClassConstructor::safe_function_calls_constructor_global_object_member_bad()" is stable_tree but calls non-stable_tree or non-mtsafe +t/t_dist_attributes_bad.h:408: [stable_tree] TestClassConstructor::safe_function_calls_constructor_global_object_member_bad() +t/t_dist_attributes_bad.h:350: [] DummyClass2::dummy_function2() + %Error: "TestClassConstructor::safe_function_calls_constructor_local_calls_class_global_bad()" is mtsafe but calls non-mtsafe function(s) -t/t_dist_attributes_bad.h:381: [mt_safe] TestClassConstructor::safe_function_calls_constructor_local_calls_class_global_bad() +t/t_dist_attributes_bad.h:402: [mt_safe] TestClassConstructor::safe_function_calls_constructor_local_calls_class_global_bad() t/t_dist_attributes_bad.h:280: [] StaticClass::static_class_function() %Error: "TestClassConstructor::safe_function_calls_constructor_local_calls_global_bad()" is mtsafe but calls non-mtsafe function(s) -t/t_dist_attributes_bad.h:378: [mt_safe] TestClassConstructor::safe_function_calls_constructor_local_calls_global_bad() +t/t_dist_attributes_bad.h:399: [mt_safe] TestClassConstructor::safe_function_calls_constructor_local_calls_global_bad() t/t_dist_attributes_bad.h:276: [] static_function() %Error: "TestClassConstructor::safe_function_calls_constructor_with_unsafepointer_bad()" is mtsafe but calls non-mtsafe function(s) -t/t_dist_attributes_bad.h:370: [mt_safe] TestClassConstructor::safe_function_calls_constructor_with_unsafepointer_bad() +t/t_dist_attributes_bad.h:391: [mt_safe] TestClassConstructor::safe_function_calls_constructor_with_unsafepointer_bad() t/t_dist_attributes_bad.h:311: [mt_unsafe] UnsafeFunction::unsafe_function() %Error: "TestClassConstructor::safe_function_calls_constructor_with_unsafereference_bad()" is mtsafe but calls non-mtsafe function(s) -t/t_dist_attributes_bad.h:374: [mt_safe] TestClassConstructor::safe_function_calls_constructor_with_unsafereference_bad() +t/t_dist_attributes_bad.h:395: [mt_safe] TestClassConstructor::safe_function_calls_constructor_with_unsafereference_bad() t/t_dist_attributes_bad.h:311: [mt_unsafe] UnsafeFunction::unsafe_function() %Error: "TestClassConstructor::safe_function_local_function_global_bad()" is mtsafe but calls non-mtsafe function(s) -t/t_dist_attributes_bad.h:356: [mt_safe] TestClassConstructor::safe_function_local_function_global_bad() +t/t_dist_attributes_bad.h:377: [mt_safe] TestClassConstructor::safe_function_local_function_global_bad() t/t_dist_attributes_bad.h:276: [] static_function() %Error: "TestClassConstructor::safe_function_static_constructor_bad()" is mtsafe but calls non-mtsafe function(s) -t/t_dist_attributes_bad.h:353: [mt_safe] TestClassConstructor::safe_function_static_constructor_bad() +t/t_dist_attributes_bad.h:374: [mt_safe] TestClassConstructor::safe_function_static_constructor_bad() t/t_dist_attributes_bad.h:276: [] static_function() %Error: "TestClassConstructor::safe_function_unsafe_constructor_bad()" is mtsafe but calls non-mtsafe function(s) -t/t_dist_attributes_bad.h:350: [mt_safe] TestClassConstructor::safe_function_unsafe_constructor_bad() +t/t_dist_attributes_bad.h:371: [mt_safe] TestClassConstructor::safe_function_unsafe_constructor_bad() t/t_dist_attributes_bad.h:285: [mt_unsafe] ConstructorCallsUnsafeLocalFunction::unsafe_function() %Error: "ifh_test_caller_func_VL_MT_SAFE(VerilatedMutex &)" is mtsafe but calls non-mtsafe function(s) @@ -1185,4 +1193,4 @@ t/t_dist_attributes_bad.cpp:60: [mt_unsafe_one] sfc_VL_ t/t_dist_attributes_bad.cpp:60: [release] sfc_VL_RELEASE(VerilatedMutex &) t/t_dist_attributes_bad.cpp:60: [release] sfc_VL_RELEASE_SHARED(VerilatedMutex &) t/t_dist_attributes_bad.cpp:60: [requires] sfc_VL_REQUIRES(VerilatedMutex &) -Number of functions reported unsafe: 224 +Number of functions reported unsafe: 226 diff --git a/test_regress/t/t_dist_warn_coverage.pl b/test_regress/t/t_dist_warn_coverage.pl index 234e2bce3..bddd75ed5 100755 --- a/test_regress/t/t_dist_warn_coverage.pl +++ b/test_regress/t/t_dist_warn_coverage.pl @@ -38,14 +38,11 @@ foreach my $s ( 'Array initialization has too few elements, need element ', 'Assert not allowed under another assert', 'Assigned pin is neither input nor output', - 'Assignment pattern key used multiple times: ', '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 ', 'Cannot write preprocessor output: ', 'Circular logic when ordering code (non-cutable edge loop)', 'Deferred assertions must use \'#0\' (IEEE 1800-2017 16.4)', @@ -161,12 +158,14 @@ sub check { print(" Line is: ", $line, "\n") if $Debug; } } + print "\n"; for my $msg (sort keys %Suppressed) { if (!$used_suppressed{$msg}) { print "Suppression not used: '$msg'\n"; } - } + } + print "\n"; } sub read_messages { diff --git a/test_regress/t/t_dpi_import_mix_bad.out b/test_regress/t/t_dpi_import_mix_bad.out new file mode 100644 index 000000000..ef362e51b --- /dev/null +++ b/test_regress/t/t_dpi_import_mix_bad.out @@ -0,0 +1,4 @@ +%Error: t/t_dpi_import_mix_bad.v:11:32: Cannot mix DPI import, DPI export, class methods, and/or public on same function: 't.foo' + 11 | import "DPI-C" function int foo (int i); + | ^~~ +%Error: Exiting due to diff --git a/test_regress/t/t_dpi_import_mix_bad.pl b/test_regress/t/t_dpi_import_mix_bad.pl new file mode 100755 index 000000000..a60503a1f --- /dev/null +++ b/test_regress/t/t_dpi_import_mix_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 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 => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_dpi_import_mix_bad.v b/test_regress/t/t_dpi_import_mix_bad.v new file mode 100644 index 000000000..e924d2f94 --- /dev/null +++ b/test_regress/t/t_dpi_import_mix_bad.v @@ -0,0 +1,18 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 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 + +module t (); + + import "DPI-C" function int foo (int i); + export "DPI-C" function foo; // Bad mix + + initial begin + $stop; + end + +endmodule diff --git a/test_regress/t/t_dynarray_method.v b/test_regress/t/t_dynarray_method.v index 3142782b5..76b56012f 100644 --- a/test_regress/t/t_dynarray_method.v +++ b/test_regress/t/t_dynarray_method.v @@ -165,6 +165,11 @@ module t (/*AUTOARG*/); `checkh(qi.size, 1); `checkh(qi[0], 2); + d = '{1, 2}; + de = '{1, 2}; + `checkh(d == de, 1'b1); + `checkh(d != de, 1'b0); + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_event_control_timing.pl b/test_regress/t/t_event_control_timing.pl index 6a4829740..750ff60c9 100755 --- a/test_regress/t/t_event_control_timing.pl +++ b/test_regress/t/t_event_control_timing.pl @@ -10,21 +10,16 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_event_control.v"); +top_filename("t/t_event_control.v"); - compile( - verilator_flags2 => ["--timing"], - ); +compile( + verilator_flags2 => ["--timing"], + ); - execute( - check_finished => 1, - expect_filename => $Self->{golden_filename}, - ); -} +execute( + check_finished => 1, + expect_filename => $Self->{golden_filename}, + ); ok(1); 1; diff --git a/test_regress/t/t_flag_binary.pl b/test_regress/t/t_flag_binary.pl index c8f265aa1..e810b1eb6 100755 --- a/test_regress/t/t_flag_binary.pl +++ b/test_regress/t/t_flag_binary.pl @@ -12,24 +12,19 @@ scenarios(simulator => 1); top_filename("t/t_flag_main.v"); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags => [# Custom as don't want -cc - "-Mdir $Self->{obj_dir}", - "--debug-check", ], - verilator_flags2 => ['--binary'], - verilator_make_cmake => 0, - verilator_make_gmake => 0, - make_main => 0, - ); +compile( + verilator_flags => [# Custom as don't want -cc + "-Mdir $Self->{obj_dir}", + "--debug-check", ], + verilator_flags2 => ['--binary'], + verilator_make_cmake => 0, + verilator_make_gmake => 0, + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_flag_i_empty.pl b/test_regress/t/t_flag_i_empty.pl new file mode 100755 index 000000000..8cee660a7 --- /dev/null +++ b/test_regress/t/t_flag_i_empty.pl @@ -0,0 +1,18 @@ +#!/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); + +compile( + verilator_flags2 => ["-Wno-MODDUP -I t_flag_i_empty.v t_flag_i_empty.v"], + ); + +ok(1); +1; diff --git a/test_regress/t/t_flag_i_empty.v b/test_regress/t/t_flag_i_empty.v new file mode 100644 index 000000000..df8679e7d --- /dev/null +++ b/test_regress/t/t_flag_i_empty.v @@ -0,0 +1,8 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2016 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t; +endmodule diff --git a/test_regress/t/t_flag_main_top_name.pl b/test_regress/t/t_flag_main_top_name.pl new file mode 100755 index 000000000..876acdd1c --- /dev/null +++ b/test_regress/t/t_flag_main_top_name.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 Don Williamson and 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); + +top_filename("t/t_flag_main_top_name.v"); + +compile( + verilator_flags => ["-Mdir $Self->{obj_dir}", "--exe", "--build", "--main"], + verilator_flags2 => ["--top-module top", "--main-top-name ALTOP"], + verilator_make_cmake => 0, + verilator_make_gmake => 0, + make_main => 0, + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_flag_main_top_name.v b/test_regress/t/t_flag_main_top_name.v new file mode 100644 index 000000000..ced845c9a --- /dev/null +++ b/test_regress/t/t_flag_main_top_name.v @@ -0,0 +1,22 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2023 by Don Williamson and 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 top; + string scope; + initial begin + scope = $sformatf("%m"); + $write("[%0t] In %s\n", $time, scope); + `ifdef MAIN_TOP_NAME_EMPTY + if (scope != "top") $stop; + `else + if (scope != "ALTOP.top") $stop; + `endif + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_flag_main_top_name_empty.pl b/test_regress/t/t_flag_main_top_name_empty.pl new file mode 100755 index 000000000..bd3cc906f --- /dev/null +++ b/test_regress/t/t_flag_main_top_name_empty.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 Don Williamson and 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); + +top_filename("t/t_flag_main_top_name.v"); + +compile( + verilator_flags => ["-Mdir $Self->{obj_dir}", "--exe", "--build", "--main"], + verilator_flags2 => ["--top-module top", "--main-top-name -", "-DMAIN_TOP_NAME_EMPTY"], + verilator_make_cmake => 0, + verilator_make_gmake => 0, + make_main => 0, + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_foreach_blkname.v b/test_regress/t/t_foreach_blkname.v index 768e7697f..3eceb8a3c 100644 --- a/test_regress/t/t_foreach_blkname.v +++ b/test_regress/t/t_foreach_blkname.v @@ -12,5 +12,8 @@ module t; end foreach (a[i]) begin end + begin + int x; + end endfunction endmodule diff --git a/test_regress/t/t_fork_label_timing.pl b/test_regress/t/t_fork_label_timing.pl index 8d67932b9..da97b37e2 100755 --- a/test_regress/t/t_fork_label_timing.pl +++ b/test_regress/t/t_fork_label_timing.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,21 +10,16 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_fork_label.v"); +top_filename("t/t_fork_label.v"); - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_fork_timing.pl b/test_regress/t/t_fork_timing.pl index f31aef7c3..db741a7d5 100755 --- a/test_regress/t/t_fork_timing.pl +++ b/test_regress/t/t_fork_timing.pl @@ -10,20 +10,15 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_fork.v"); +top_filename("t/t_fork.v"); - compile( - verilator_flags2 => ["--timing"], - ); +compile( + verilator_flags2 => ["--timing"], + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_func_lib_sub_timing.pl b/test_regress/t/t_func_lib_sub_timing.pl index 0acb617a6..7498c6694 100755 --- a/test_regress/t/t_func_lib_sub_timing.pl +++ b/test_regress/t/t_func_lib_sub_timing.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,16 +10,12 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); # UNOPTTHREADS in vltmt -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_func_lib_sub.v"); +top_filename("t/t_func_lib_sub.v"); + +compile( + verilator_flags2 => ["--timing"], + ); - compile( - verilator_flags2 => ["--timing"], - ); -} # No execute ok(1); 1; diff --git a/test_regress/t/t_func_no_lifetime_bad.out b/test_regress/t/t_func_no_lifetime_bad.out index ac2132066..bc145b029 100644 --- a/test_regress/t/t_func_no_lifetime_bad.out +++ b/test_regress/t/t_func_no_lifetime_bad.out @@ -1,11 +1,27 @@ -%Warning-IMPLICITSTATIC: t/t_func_no_lifetime_bad.v:7:14: Function/task's lifetime implicitly set to static - : ... Suggest use 'function automatic' or 'function static' - 7 | function int f_implicit_static(); - | ^~~~~~~~~~~~~~~~~ +%Warning-IMPLICITSTATIC: t/t_func_no_lifetime_bad.v:29:17: Function/task's lifetime implicitly set to static + : ... Suggest use 'function automatic' or 'function static' + 29 | function int f_implicit_static(); + | ^~~~~~~~~~~~~~~~~ + t/t_func_no_lifetime_bad.v:30:11: ... Location of implicit static variable + 30 | int cnt = 0; + | ^~~ +... Suggest use 'function automatic' or 'function static' ... For warning description see https://verilator.org/warn/IMPLICITSTATIC?v=latest ... Use "/* verilator lint_off IMPLICITSTATIC */" and lint_on around source to disable this message. -%Warning-IMPLICITSTATIC: t/t_func_no_lifetime_bad.v:12:6: Function/task's lifetime implicitly set to static +%Warning-IMPLICITSTATIC: t/t_func_no_lifetime_bad.v:34:9: Function/task's lifetime implicitly set to static : ... Suggest use 'function automatic' or 'function static' - 12 | task t_implicit_static(); - | ^~~~~~~~~~~~~~~~~ + 34 | task t_implicit_static(); + | ^~~~~~~~~~~~~~~~~ + t/t_func_no_lifetime_bad.v:35:11: ... Location of implicit static variable + 35 | int cnt = 0; + | ^~~ +... Suggest use 'function automatic' or 'function static' +%Warning-IMPLICITSTATIC: t/t_func_no_lifetime_bad.v:9:8: Variable's lifetime implicitly set to static + : ... Suggest use 'static' before variable declaration' + 9 | int cnt = 0; + | ^~~ +%Warning-IMPLICITSTATIC: t/t_func_no_lifetime_bad.v:15:8: Variable's lifetime implicitly set to static + : ... Suggest use 'static' before variable declaration' + 15 | int cnt = 0; + | ^~~ %Error: Exiting due to diff --git a/test_regress/t/t_func_no_lifetime_bad.v b/test_regress/t/t_func_no_lifetime_bad.v index e77979cf3..1c2fe0c2d 100644 --- a/test_regress/t/t_func_no_lifetime_bad.v +++ b/test_regress/t/t_func_no_lifetime_bad.v @@ -4,22 +4,38 @@ // any use, without warranty, 2023 by Antmicro Ltd. // SPDX-License-Identifier: CC0-1.0 -function int f_implicit_static(); - int cnt = 0; +// Not legal to put "static" here, so no warning +function int f_dunit_static(); + int cnt = 0; // Ok to require "static" here somehday return ++cnt; endfunction -task t_implicit_static(); - int cnt = 0; +// Not legal to put "static" here, so no warning +task t_dunit_static(); + int cnt = 0; // Ok to require "static" here somehday $display("%d", ++cnt); endtask +task t_dunit_static_ok(input int in_ok = 1); + static int cnt_ok = 0; // No warning here + $display("%d", ++cnt_ok); +endtask module t (/*AUTOARG*/ // Inputs clk ); + function int f_implicit_static(); + int cnt = 0; + return ++cnt; + endfunction + + task t_implicit_static(); + int cnt = 0; + $display("%d", ++cnt); + endtask + // verilator lint_off IMPLICITSTATIC function int f_implicit_but_lint_off(); int cnt = 0; @@ -30,6 +46,10 @@ module t (/*AUTOARG*/ int a, b; initial begin + a = f_dunit_static(); + t_dunit_static(); + t_dunit_static_ok(); + a = f_implicit_static(); t_implicit_static(); b = f_implicit_but_lint_off(); diff --git a/test_regress/t/t_gate_basic_timing.pl b/test_regress/t/t_gate_basic_timing.pl index 713929427..efbe15374 100755 --- a/test_regress/t/t_gate_basic_timing.pl +++ b/test_regress/t/t_gate_basic_timing.pl @@ -12,21 +12,16 @@ scenarios(simulator => 1); $Self->{main_time_multiplier} = 10e-7 / 10e-9; -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_gate_basic.v"); +top_filename("t/t_gate_basic.v"); - compile( - timing_loop => 1, - verilator_flags2 => ["--timing --timescale 10ns/1ns -Wno-RISEFALLDLY"], - ); +compile( + timing_loop => 1, + verilator_flags2 => ["--timing --timescale 10ns/1ns -Wno-RISEFALLDLY"], + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_gate_fdup.pl b/test_regress/t/t_gate_fdup.pl index 2eb6a63bc..d2f13f637 100755 --- a/test_regress/t/t_gate_fdup.pl +++ b/test_regress/t/t_gate_fdup.pl @@ -13,5 +13,9 @@ scenarios(simulator => 1); compile( ); +lint( + verilator_flags2 => ["--language 1364-2005"] + ); + ok(1); 1; diff --git a/test_regress/t/t_hier_block_sc_trace_vcd.out b/test_regress/t/t_hier_block_sc_trace_vcd.out index bcb002864..aa5149eb5 100644 --- a/test_regress/t/t_hier_block_sc_trace_vcd.out +++ b/test_regress/t/t_hier_block_sc_trace_vcd.out @@ -156,20 +156,20 @@ $timescale 1ps $end $var wire 8 N in [7:0] $end $var wire 8 P out [7:0] $end $upscope $end - $scope interface in $end + $scope module in $end $var wire 1 K clk $end $var wire 8 N data [7:0] $end $upscope $end - $scope interface out $end + $scope module out $end $var wire 1 K clk $end $var wire 8 O data [7:0] $end $upscope $end $upscope $end - $scope interface in_ifs $end + $scope module in_ifs $end $var wire 1 K clk $end $var wire 8 N data [7:0] $end $upscope $end - $scope interface out_ifs $end + $scope module out_ifs $end $var wire 1 K clk $end $var wire 8 O data [7:0] $end $upscope $end diff --git a/test_regress/t/t_hier_block_trace_vcd.out b/test_regress/t/t_hier_block_trace_vcd.out index e9b9dbdcb..2e89a537a 100644 --- a/test_regress/t/t_hier_block_trace_vcd.out +++ b/test_regress/t/t_hier_block_trace_vcd.out @@ -157,20 +157,20 @@ $timescale 1ps $end $var wire 8 N in [7:0] $end $var wire 8 P out [7:0] $end $upscope $end - $scope interface in $end + $scope module in $end $var wire 1 K clk $end $var wire 8 N data [7:0] $end $upscope $end - $scope interface out $end + $scope module out $end $var wire 1 K clk $end $var wire 8 O data [7:0] $end $upscope $end $upscope $end - $scope interface in_ifs $end + $scope module in_ifs $end $var wire 1 K clk $end $var wire 8 N data [7:0] $end $upscope $end - $scope interface out_ifs $end + $scope module out_ifs $end $var wire 1 K clk $end $var wire 8 O data [7:0] $end $upscope $end diff --git a/test_regress/t/t_incorrect_multi_driven.pl b/test_regress/t/t_incorrect_multi_driven.pl new file mode 100755 index 000000000..0fc71f8e9 --- /dev/null +++ b/test_regress/t/t_incorrect_multi_driven.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(linter => 1); + +top_filename("t/t_incorrect_multi_driven.v"); + +lint( + fails => 0 + ); + +ok(1); +1; diff --git a/test_regress/t/t_incorrect_multi_driven.v b/test_regress/t/t_incorrect_multi_driven.v new file mode 100644 index 000000000..3020398db --- /dev/null +++ b/test_regress/t/t_incorrect_multi_driven.v @@ -0,0 +1,56 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2023 by Adrien Le Masle. +// SPDX-License-Identifier: CC0-1.0 + +interface test_if #(parameter int AA = 2, BB=5); + + logic [AA-1 : 0] a; + logic [BB-1 : 0] b; + logic c; + logic d; + + modport slave (input a, + input b, + input c, + input d); + + modport master (output a, + output b, + output c, + output d); + +endinterface : test_if + +module test + (input logic [28:0] a, + output logic [28:0] b); + + always_comb begin + b = a; + end +endmodule + +module multi_driven + ( + input logic [20-1 : 0] data_in, + output logic [20-1 : 0] data_out, + test_if.slave test_if_in, + test_if.master test_if_out + ); + + test test_inst + ( + .a({data_in, + test_if_in.a, + test_if_in.b, + test_if_in.c, + test_if_in.d}), + .b({data_out, + test_if_out.a, + test_if_out.b, + test_if_out.c, + test_if_out.d})); + +endmodule; diff --git a/test_regress/t/t_inside_nonint.pl b/test_regress/t/t_inside_nonint.pl new file mode 100755 index 000000000..859050d63 --- /dev/null +++ b/test_regress/t/t_inside_nonint.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_inside_nonint.v b/test_regress/t/t_inside_nonint.v new file mode 100644 index 000000000..90f5cecce --- /dev/null +++ b/test_regress/t/t_inside_nonint.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 Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +function bit check_string(string s); + if (s inside {"RW", "WO"}) + return 1'b1; + return 1'b0; +endfunction + +function bit check_double(real d); + if (d inside {0.0, 2.5}) + return 1'b1; + return 1'b0; +endfunction + +module t(); + initial begin + if (!check_string("WO")) + $stop; + if (!check_string("RW")) + $stop; + if (check_string("ABC")) + $stop; + if (!check_double(0.0)) + $stop; + if (!check_double(2.5)) + $stop; + if (check_double(1.0)) + $stop; + + $display("*-* All Finished *-*"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_ref_trace.out b/test_regress/t/t_interface_ref_trace.out index 5e2a4c6b1..9f59e314d 100644 --- a/test_regress/t/t_interface_ref_trace.out +++ b/test_regress/t/t_interface_ref_trace.out @@ -9,76 +9,76 @@ $timescale 1ps $end $var wire 32 # cyc [31:0] $end $scope module a $end $scope module ac1 $end - $scope interface intf_for_check $end + $scope module intf_for_check $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 $ value [31:0] $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 % val100 [31:0] $end $var wire 32 & val200 [31:0] $end $upscope $end $upscope $end $upscope $end $scope module ac2 $end - $scope interface intf_for_check $end + $scope module intf_for_check $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 ' value [31:0] $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 ( val100 [31:0] $end $var wire 32 ) val200 [31:0] $end $upscope $end $upscope $end $upscope $end $scope module ac3 $end - $scope interface intf_for_check $end + $scope module intf_for_check $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 * value [31:0] $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 + val100 [31:0] $end $var wire 32 , val200 [31:0] $end $upscope $end $upscope $end $upscope $end $scope module as3 $end - $scope interface intf_for_struct $end + $scope module intf_for_struct $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 * value [31:0] $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 + val100 [31:0] $end $var wire 32 , val200 [31:0] $end $upscope $end $upscope $end $upscope $end - $scope interface intf_in_sub_all $end + $scope module intf_in_sub_all $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 * value [31:0] $end - $scope interface inner $end + $scope module inner $end $var wire 32 # cyc [31:0] $end $var wire 32 3 value [31:0] $end $upscope $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 + val100 [31:0] $end $var wire 32 , val200 [31:0] $end $upscope $end $upscope $end - $scope interface intf_one $end + $scope module intf_one $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 $ value [31:0] $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 % val100 [31:0] $end $var wire 32 & val200 [31:0] $end $upscope $end $upscope $end - $scope interface intf_two $end + $scope module intf_two $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 ' value [31:0] $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 ( val100 [31:0] $end $var wire 32 ) val200 [31:0] $end $upscope $end @@ -86,146 +86,146 @@ $timescale 1ps $end $upscope $end $scope module abcdefghijklmnopqrstuvwxyz $end $scope module ac1 $end - $scope interface intf_for_check $end + $scope module intf_for_check $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 ' value [31:0] $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 ( val100 [31:0] $end $var wire 32 ) val200 [31:0] $end $upscope $end $upscope $end $upscope $end $scope module ac2 $end - $scope interface intf_for_check $end + $scope module intf_for_check $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 $ value [31:0] $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 % val100 [31:0] $end $var wire 32 & val200 [31:0] $end $upscope $end $upscope $end $upscope $end $scope module ac3 $end - $scope interface intf_for_check $end + $scope module intf_for_check $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 - value [31:0] $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 . val100 [31:0] $end $var wire 32 / val200 [31:0] $end $upscope $end $upscope $end $upscope $end $scope module as3 $end - $scope interface intf_for_struct $end + $scope module intf_for_struct $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 - value [31:0] $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 . val100 [31:0] $end $var wire 32 / val200 [31:0] $end $upscope $end $upscope $end $upscope $end - $scope interface intf_in_sub_all $end + $scope module intf_in_sub_all $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 - value [31:0] $end - $scope interface inner $end + $scope module inner $end $var wire 32 # cyc [31:0] $end $var wire 32 4 value [31:0] $end $upscope $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 . val100 [31:0] $end $var wire 32 / val200 [31:0] $end $upscope $end $upscope $end - $scope interface intf_one $end + $scope module intf_one $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 ' value [31:0] $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 ( val100 [31:0] $end $var wire 32 ) val200 [31:0] $end $upscope $end $upscope $end - $scope interface intf_two $end + $scope module intf_two $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 $ value [31:0] $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 % val100 [31:0] $end $var wire 32 & val200 [31:0] $end $upscope $end $upscope $end $upscope $end $scope module c1 $end - $scope interface intf_for_check $end + $scope module intf_for_check $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 $ value [31:0] $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 % val100 [31:0] $end $var wire 32 & val200 [31:0] $end $upscope $end $upscope $end $upscope $end $scope module c2 $end - $scope interface intf_for_check $end + $scope module intf_for_check $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 ' value [31:0] $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 ( val100 [31:0] $end $var wire 32 ) val200 [31:0] $end $upscope $end $upscope $end $upscope $end - $scope interface intf_1 $end + $scope module intf_1 $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 $ value [31:0] $end - $scope interface inner $end + $scope module inner $end $var wire 32 # cyc [31:0] $end $var wire 32 1 value [31:0] $end $upscope $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 % val100 [31:0] $end $var wire 32 & val200 [31:0] $end $upscope $end $upscope $end - $scope interface intf_2 $end + $scope module intf_2 $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 ' value [31:0] $end - $scope interface inner $end + $scope module inner $end $var wire 32 # cyc [31:0] $end $var wire 32 2 value [31:0] $end $upscope $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 ( val100 [31:0] $end $var wire 32 ) val200 [31:0] $end $upscope $end $upscope $end $scope module s1 $end - $scope interface intf_for_struct $end + $scope module intf_for_struct $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 $ value [31:0] $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 % val100 [31:0] $end $var wire 32 & val200 [31:0] $end $upscope $end $upscope $end $upscope $end $scope module s2 $end - $scope interface intf_for_struct $end + $scope module intf_for_struct $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 ' value [31:0] $end - $scope struct the_struct $end + $scope module the_struct $end $var wire 32 ( val100 [31:0] $end $var wire 32 ) val200 [31:0] $end $upscope $end diff --git a/test_regress/t/t_lib.pl b/test_regress/t/t_lib.pl index ef6fcd3b2..de31f55b6 100755 --- a/test_regress/t/t_lib.pl +++ b/test_regress/t/t_lib.pl @@ -11,10 +11,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( - vlt => 1, - xsim => 1, - ); +scenarios(vlt => 1, xsim => 1); top_filename("t/t_lib_prot.v"); diff --git a/test_regress/t/t_lib_nolib.pl b/test_regress/t/t_lib_nolib.pl index 0a385f0cb..b463b16b6 100755 --- a/test_regress/t/t_lib_nolib.pl +++ b/test_regress/t/t_lib_nolib.pl @@ -11,10 +11,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( - vlt => 1, - xsim => 1, - ); +scenarios(vlt => 1, xsim => 1); $Self->{sim_time} = $Self->{benchmark} * 100 if $Self->{benchmark}; top_filename("t/t_lib_prot.v"); diff --git a/test_regress/t/t_lib_prot.pl b/test_regress/t/t_lib_prot.pl index 14c01dc7c..dddc68012 100755 --- a/test_regress/t/t_lib_prot.pl +++ b/test_regress/t/t_lib_prot.pl @@ -11,10 +11,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( - vlt => 1, - xsim => 1, - ); +scenarios(vlt => 1, xsim => 1); $Self->{sim_time} = $Self->{benchmark} * 100 if $Self->{benchmark}; diff --git a/test_regress/t/t_lib_prot_clk_gated.pl b/test_regress/t/t_lib_prot_clk_gated.pl index b1286cca2..787f69ad9 100755 --- a/test_regress/t/t_lib_prot_clk_gated.pl +++ b/test_regress/t/t_lib_prot_clk_gated.pl @@ -11,10 +11,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( - vlt => 1, - xsim => 1, - ); +scenarios(vlt => 1, xsim => 1); $Self->{sim_time} = $Self->{benchmark} * 100 if $Self->{benchmark}; diff --git a/test_regress/t/t_lib_prot_comb.pl b/test_regress/t/t_lib_prot_comb.pl index 73cfd8aef..961af3799 100755 --- a/test_regress/t/t_lib_prot_comb.pl +++ b/test_regress/t/t_lib_prot_comb.pl @@ -11,10 +11,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( - vlt => 1, - xsim => 1, - ); +scenarios(vlt => 1, xsim => 1); $Self->{sim_time} = $Self->{benchmark} * 100 if $Self->{benchmark}; diff --git a/test_regress/t/t_lib_prot_shared.pl b/test_regress/t/t_lib_prot_shared.pl index b7fab1e92..02d95a9e7 100755 --- a/test_regress/t/t_lib_prot_shared.pl +++ b/test_regress/t/t_lib_prot_shared.pl @@ -11,11 +11,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( - vlt => 1, - vltmt => 1, - xsim => 1, - ); +scenarios(vlt_all => 1, xsim => 1); top_filename("t/t_lib_prot.v"); $Self->{sim_time} = $Self->{benchmark} * 100 if $Self->{benchmark}; diff --git a/test_regress/t/t_lint_historical.v b/test_regress/t/t_lint_historical.v index b1c1caf24..95f11bcc2 100644 --- a/test_regress/t/t_lint_historical.v +++ b/test_regress/t/t_lint_historical.v @@ -7,6 +7,7 @@ module t; // Test all warnings, including those that are historically removed still parse // verilator lint_off ALWCOMBORDER + // verilator lint_off ASCRANGE // verilator lint_off ASSIGNDLY // verilator lint_off ASSIGNIN // verilator lint_off BADSTDPRAGMA @@ -24,13 +25,14 @@ module t; // verilator lint_off CMPCONST // verilator lint_off COLONPLUS // verilator lint_off COMBDLY + // verilator lint_off CONSTRAINTIGN // verilator lint_off CONTASSREG - // verilator lint_off DEFPARAM // verilator lint_off DECLFILENAME + // verilator lint_off DEFPARAM // verilator lint_off DEPRECATED - // verilator lint_off RISEFALLDLY - // verilator lint_off MINTYPMAXDLY + // verilator lint_off ENCAPSULATED // verilator lint_off ENDLABEL + // verilator lint_off ENUMVALUE // verilator lint_off EOFNEWLINE // verilator lint_off GENCLK // verilator lint_off HIERBLOCK @@ -38,6 +40,7 @@ module t; // verilator lint_off IGNOREDRETURN // verilator lint_off IMPERFECTSCH // verilator lint_off IMPLICIT + // verilator lint_off IMPLICITSTATIC // verilator lint_off IMPORTSTAR // verilator lint_off IMPURE // verilator lint_off INCABSPATH @@ -45,10 +48,12 @@ module t; // verilator lint_off INITIALDLY // verilator lint_off INSECURE // verilator lint_off LATCH - // verilator lint_off ASCRANGE + // verilator lint_off LITENDIAN + // verilator lint_off MINTYPMAXDLY // verilator lint_off MODDUP // verilator lint_off MULTIDRIVEN // verilator lint_off MULTITOP + // verilator lint_off NEWERSTD // verilator lint_off NOLATCH // verilator lint_off NULLPORT // verilator lint_off PINCONNECTEMPTY @@ -62,9 +67,11 @@ module t; // verilator lint_off RANDC // verilator lint_off REALCVT // verilator lint_off REDEFMACRO + // verilator lint_off RISEFALLDLY // verilator lint_off SELRANGE // verilator lint_off SHORTREAL // verilator lint_off SPLITVAR + // verilator lint_off STATICVAR // verilator lint_off STMTDLY // verilator lint_off SYMRSVDWORD // verilator lint_off SYNCASYNCNET @@ -87,6 +94,9 @@ module t; // verilator lint_off WAITCONST // verilator lint_off WIDTH // verilator lint_off WIDTHCONCAT + // verilator lint_off WIDTHEXPAND + // verilator lint_off WIDTHTRUNC + // verilator lint_off WIDTHXZEXPAND // verilator lint_off ZERODLY endmodule diff --git a/test_regress/t/t_lint_latch_6.pl b/test_regress/t/t_lint_latch_6.pl new file mode 100755 index 000000000..629a44bbb --- /dev/null +++ b/test_regress/t/t_lint_latch_6.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 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(vlt => 1); + +lint( + ); + +ok(1); +1; diff --git a/test_regress/t/t_lint_latch_6.v b/test_regress/t/t_lint_latch_6.v new file mode 100644 index 000000000..9f55fb831 --- /dev/null +++ b/test_regress/t/t_lint_latch_6.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module for Issue#221 +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2023 by Julien Margetts (Originally provided by Adrien Le Masle) +// SPDX-License-Identifier: Unlicense + +module verilator_latch +( + input logic state, + output logic [31:0] b +); + + function logic [31:0 ] toto (); + logic [31:0] res; + res = 10; + return res; + endfunction + + always_comb + begin + b = 0; + if (state) + b = toto(); + end + +endmodule; diff --git a/test_regress/t/t_lint_latch_7.pl b/test_regress/t/t_lint_latch_7.pl new file mode 100755 index 000000000..629a44bbb --- /dev/null +++ b/test_regress/t/t_lint_latch_7.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 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(vlt => 1); + +lint( + ); + +ok(1); +1; diff --git a/test_regress/t/t_lint_latch_7.v b/test_regress/t/t_lint_latch_7.v new file mode 100644 index 000000000..d8fbbc1c6 --- /dev/null +++ b/test_regress/t/t_lint_latch_7.v @@ -0,0 +1,21 @@ +// DESCRIPTION: Verilator: Verilog Test module for Issue#xxxx +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2021 by Julien Margetts +// SPDX-License-Identifier: Unlicense + +module test #(parameter W = 65) + (input logic [W-1:0] a, + input logic e, + output logic [W-1:0] z); + + integer i; + + always @(*) + if (e) + for (i=0;i 1); -top_filename("t/t_timing_wait.v"); +top_filename("t/t_timing_wait1.v"); lint( verilator_flags2 => ["--timing"], diff --git a/test_regress/t/t_lint_width_arraydecl.pl b/test_regress/t/t_lint_width_arraydecl.pl new file mode 100755 index 000000000..629a44bbb --- /dev/null +++ b/test_regress/t/t_lint_width_arraydecl.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 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(vlt => 1); + +lint( + ); + +ok(1); +1; diff --git a/test_regress/t/t_lint_width_arraydecl.v b/test_regress/t/t_lint_width_arraydecl.v new file mode 100644 index 000000000..5de0ecc50 --- /dev/null +++ b/test_regress/t/t_lint_width_arraydecl.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, 2009 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +localparam UADDR_WIDTH = 4'd10; +localparam UROM_WIDTH = 5'd17; +localparam UROM_DEPTH = 11'd1024; + + +module t( + input clk, + input [UADDR_WIDTH-1:0] mAddr, + output logic [UROM_WIDTH-1:0] mOutput); + + reg [UROM_WIDTH-1:0] uRam[UROM_DEPTH]; + + always @(posedge clk) mOutput <= uRam[mAddr]; + +endmodule diff --git a/test_regress/t/t_mailbox.pl b/test_regress/t/t_mailbox.pl index d7ae01e91..30306c8ce 100755 --- a/test_regress/t/t_mailbox.pl +++ b/test_regress/t/t_mailbox.pl @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing -Wall"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing -Wall"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_mailbox_class.pl b/test_regress/t/t_mailbox_class.pl index 3329d516e..6e30bd25d 100755 --- a/test_regress/t/t_mailbox_class.pl +++ b/test_regress/t/t_mailbox_class.pl @@ -10,14 +10,9 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--timing"], - ); -} +compile( + verilator_flags2 => ["--timing"], + ); ok(1); 1; diff --git a/test_regress/t/t_mailbox_std.pl b/test_regress/t/t_mailbox_std.pl index 1229e6524..31b9a5203 100755 --- a/test_regress/t/t_mailbox_std.pl +++ b/test_regress/t/t_mailbox_std.pl @@ -10,21 +10,16 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_mailbox.v"); +top_filename("t/t_mailbox.v"); - compile( - verilator_flags2 => ["--exe --main --timing -Wall --Wpedantic -DMAILBOX_T=std::mailbox"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing -Wall --Wpedantic -DMAILBOX_T=std::mailbox"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_math_signed5_timing.pl b/test_regress/t/t_math_signed5_timing.pl index 2365fef9a..34482e187 100755 --- a/test_regress/t/t_math_signed5_timing.pl +++ b/test_regress/t/t_math_signed5_timing.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,21 +10,16 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_math_signed5.v"); +top_filename("t/t_math_signed5.v"); - compile( - verilator_flags2 => ['--timing'], - timing_loop => 1, - ); +compile( + verilator_flags2 => ['--timing'], + timing_loop => 1, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_net_delay_timing.pl b/test_regress/t/t_net_delay_timing.pl index 63a94fdbd..0ebdb8a45 100755 --- a/test_regress/t/t_net_delay_timing.pl +++ b/test_regress/t/t_net_delay_timing.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,21 +10,16 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_net_delay.v"); +top_filename("t/t_net_delay.v"); - compile( - timing_loop => 1, - verilator_flags2 => ["--timing"], - ); +compile( + timing_loop => 1, + verilator_flags2 => ["--timing"], + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_net_delay_timing_sc.pl b/test_regress/t/t_net_delay_timing_sc.pl index 1fd75cffb..3dd55e6e1 100755 --- a/test_regress/t/t_net_delay_timing_sc.pl +++ b/test_regress/t/t_net_delay_timing_sc.pl @@ -12,23 +12,15 @@ scenarios(simulator => 1); $Self->{main_time_multiplier} = 2; -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -elsif (!$Self->have_sc) { - skip("No SystemC installed"); -} -else { - top_filename("t/t_net_delay.v"); +top_filename("t/t_net_delay.v"); - compile( - verilator_flags2 => ["--sc --exe --timing --timescale 10ps/1ps"], - ); +compile( + verilator_flags2 => ["--sc --exe --timing --timescale 10ps/1ps"], + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_no_typedef_bad.out b/test_regress/t/t_no_typedef_bad.out new file mode 100644 index 000000000..8b5c328f7 --- /dev/null +++ b/test_regress/t/t_no_typedef_bad.out @@ -0,0 +1,4 @@ +%Error: t/t_no_typedef_bad.v:10:4: Can't find typedef: 'sometype' + 10 | sometype p; + | ^~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_no_typedef_bad.pl b/test_regress/t/t_no_typedef_bad.pl new file mode 100755 index 000000000..8aaa0fb99 --- /dev/null +++ b/test_regress/t/t_no_typedef_bad.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 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); + +lint( + verilator_flags2 => ["--xml-only"], + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_no_typedef_bad.v b/test_regress/t/t_no_typedef_bad.v new file mode 100644 index 000000000..61cc81209 --- /dev/null +++ b/test_regress/t/t_no_typedef_bad.v @@ -0,0 +1,11 @@ +// 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 + +typedef sometype; + +module t(/*AUTOARG*/); + sometype p; +endmodule diff --git a/test_regress/t/t_number_v_bad.out b/test_regress/t/t_number_v_bad.out new file mode 100644 index 000000000..af57029f9 --- /dev/null +++ b/test_regress/t/t_number_v_bad.out @@ -0,0 +1,21 @@ +%Warning-NEWERSTD: t/t_number_v_bad.v:11:25: Unbased unsized literals require IEEE 1800-2005 or later. + 11 | wire [127:0] FOO1 = '0; + | ^~ + ... For warning description see https://verilator.org/warn/NEWERSTD?v=latest + ... Use "/* verilator lint_off NEWERSTD */" and lint_on around source to disable this message. +%Warning-NEWERSTD: t/t_number_v_bad.v:12:25: Unbased unsized literals require IEEE 1800-2005 or later. + 12 | wire [127:0] FOO2 = '1; + | ^~ +%Warning-NEWERSTD: t/t_number_v_bad.v:13:25: Unbased unsized literals require IEEE 1800-2005 or later. + 13 | wire [127:0] FOO3 = 'x; + | ^~ +%Warning-NEWERSTD: t/t_number_v_bad.v:14:25: Unbased unsized literals require IEEE 1800-2005 or later. + 14 | wire [127:0] FOO4 = 'X; + | ^~ +%Warning-NEWERSTD: t/t_number_v_bad.v:15:25: Unbased unsized literals require IEEE 1800-2005 or later. + 15 | wire [127:0] FOO5 = 'z; + | ^~ +%Warning-NEWERSTD: t/t_number_v_bad.v:16:25: Unbased unsized literals require IEEE 1800-2005 or later. + 16 | wire [127:0] FOO6 = 'Z; + | ^~ +%Error: Exiting due to diff --git a/test_regress/t/t_number_v_bad.pl b/test_regress/t/t_number_v_bad.pl new file mode 100755 index 000000000..24b1e474d --- /dev/null +++ b/test_regress/t/t_number_v_bad.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 2023 by Ethan Sifferman and 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( + verilator_flags2 => ["--language 1364-2005"], + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_number_v_bad.v b/test_regress/t/t_number_v_bad.v new file mode 100644 index 000000000..d8b503ba8 --- /dev/null +++ b/test_regress/t/t_number_v_bad.v @@ -0,0 +1,18 @@ +// DESCRIPTION: Verilator: Test of Verilog and SystemVerilog integer literal differences +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Ethan Sifferman. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + + // "unbased_unsized_literal" is SystemVerilog only + // Should fail with "NEWERSTD" + wire [127:0] FOO1 = '0; + wire [127:0] FOO2 = '1; + wire [127:0] FOO3 = 'x; + wire [127:0] FOO4 = 'X; + wire [127:0] FOO5 = 'z; + wire [127:0] FOO6 = 'Z; + +endmodule diff --git a/test_regress/t/t_order_timing.pl b/test_regress/t/t_order_timing.pl index ee807ce82..d00f26d4a 100755 --- a/test_regress/t/t_order_timing.pl +++ b/test_regress/t/t_order_timing.pl @@ -12,21 +12,16 @@ scenarios(simulator => 1); $Self->{main_time_multiplier} = 1e-8 / 1e-9; -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_order.v"); +top_filename("t/t_order.v"); - compile( - timing_loop => 1, - verilator_flags2 => ["--timescale 10ns/1ns --timing"], - ); +compile( + timing_loop => 1, + verilator_flags2 => ["--timescale 10ns/1ns --timing"], + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_package_ddecl_timing.pl b/test_regress/t/t_package_ddecl_timing.pl index d471a7adb..f7858dcb8 100755 --- a/test_regress/t/t_package_ddecl_timing.pl +++ b/test_regress/t/t_package_ddecl_timing.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,21 +10,16 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_package_ddecl.v"); +top_filename("t/t_package_ddecl.v"); - compile( - verilator_flags2 => ['--timing'], - timing_loop => 1, - ); +compile( + verilator_flags2 => ['--timing'], + timing_loop => 1, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_package_dup_bad.out b/test_regress/t/t_package_dup_bad.out new file mode 100644 index 000000000..7cc93102e --- /dev/null +++ b/test_regress/t/t_package_dup_bad.out @@ -0,0 +1,21 @@ +%Warning-MODDUP: t/t_package_dup_bad.v:11:9: Duplicate declaration of module: 'pkg' + 11 | package pkg; + | ^~~ + t/t_package_dup_bad.v:7:9: ... Location of original declaration + 7 | package pkg; + | ^~~ + ... For warning description see https://verilator.org/warn/MODDUP?v=latest + ... Use "/* verilator lint_off MODDUP */" and lint_on around source to disable this message. +%Warning-MODDUP: t/t_package_dup_bad.v:19:9: Duplicate declaration of module: 'pkg' + 19 | package pkg; + | ^~~ + t/t_package_dup_bad.v:7:9: ... Location of original declaration + 7 | package pkg; + | ^~~ +%Warning-MODDUP: t/t_package_dup_bad.v:22:9: Duplicate declaration of module: 'pkg' + 22 | package pkg; + | ^~~ + t/t_package_dup_bad.v:7:9: ... Location of original declaration + 7 | package pkg; + | ^~~ +%Error: Exiting due to diff --git a/test_regress/t/t_package_dup_bad.pl b/test_regress/t/t_package_dup_bad.pl new file mode 100755 index 000000000..c35c8bc93 --- /dev/null +++ b/test_regress/t/t_package_dup_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(simulator => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_package_dup_bad.v b/test_regress/t/t_package_dup_bad.v new file mode 100644 index 000000000..7a118fa59 --- /dev/null +++ b/test_regress/t/t_package_dup_bad.v @@ -0,0 +1,31 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +package pkg; + localparam PARAM = 10; +endpackage + +package pkg; + localparam PARAM = 10; +endpackage + +module sub import pkg::*; + #( ) (); +endmodule + +package pkg; +endpackage + +package pkg; +endpackage + +module t (/*AUTOARG*/); + sub sub (); + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_param_default_presv_bad.out b/test_regress/t/t_param_default_presv_bad.out index b491e2719..1e86bff74 100644 --- a/test_regress/t/t_param_default_presv_bad.out +++ b/test_regress/t/t_param_default_presv_bad.out @@ -1,4 +1,10 @@ -%Error: t/t_param_default_bad.v:7:26: Parameter requires default value, or use IEEE 1800-2009 or later. +%Warning-NEWERSTD: t/t_param_default_bad.v:7:26: Parameter requires default value, or use IEEE 1800-2009 or later. + 7 | module m #(parameter int Foo); + | ^~~ + ... For warning description see https://verilator.org/warn/NEWERSTD?v=latest + ... Use "/* verilator lint_off NEWERSTD */" and lint_on around source to disable this message. +%Error: t/t_param_default_bad.v:7:26: Parameter without initial value is never given value (IEEE 1800-2017 6.20.1): 'Foo' + : ... In instance t.foo 7 | module m #(parameter int Foo); | ^~~ %Error: Exiting due to diff --git a/test_regress/t/t_parse_delay_timing.pl b/test_regress/t/t_parse_delay_timing.pl index f4847bf8c..d466d38c7 100755 --- a/test_regress/t/t_parse_delay_timing.pl +++ b/test_regress/t/t_parse_delay_timing.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,16 +10,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_parse_delay.v"); +top_filename("t/t_parse_delay.v"); - compile( - verilator_flags2 => ['--timing'], - ); -} +compile( + verilator_flags2 => ['--timing'], + ); ok(1); 1; diff --git a/test_regress/t/t_process.out b/test_regress/t/t_process.out index ab04bc4e8..f0d3cbfc9 100644 --- a/test_regress/t/t_process.out +++ b/test_regress/t/t_process.out @@ -1,3 +1,2 @@ -[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... +'{m_process:process} +*-* All Finished *-* diff --git a/test_regress/t/t_process.pl b/test_regress/t/t_process.pl index 39ea187ff..7eac3054e 100755 --- a/test_regress/t/t_process.pl +++ b/test_regress/t/t_process.pl @@ -11,12 +11,12 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( + v_flags2 => ["--timing"], ); execute( - fails => $Self->{vlt_all}, + check_finished => 1, expect_filename => $Self->{golden_filename}, - check_finished => !$Self->{vlt_all}, ); ok(1); diff --git a/test_regress/t/t_process.v b/test_regress/t/t_process.v index 0d495ab17..9d4f3fbb8 100644 --- a/test_regress/t/t_process.v +++ b/test_regress/t/t_process.v @@ -38,6 +38,8 @@ module t(/*AUTOARG*/); p.srandom(0); p.set_randstate(p.get_randstate()); + $display("%p", p); + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_process_bad.pl b/test_regress/t/t_process_bad.pl index dd3bcbbf3..c87b2fc6f 100755 --- a/test_regress/t/t_process_bad.pl +++ b/test_regress/t/t_process_bad.pl @@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); lint( - verilator_flags2 => ["--xml-only"], + verilator_flags2 => ["--xml-only", "--timing"], fails => 1, expect_filename => $Self->{golden_filename}, ); diff --git a/test_regress/t/t_process_finished.pl b/test_regress/t/t_process_finished.pl new file mode 100755 index 000000000..b267631e9 --- /dev/null +++ b/test_regress/t/t_process_finished.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 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( + verilator_flags2 => ["--timing"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_process_finished.v b/test_regress/t/t_process_finished.v new file mode 100644 index 000000000..df6f6cc28 --- /dev/null +++ b/test_regress/t/t_process_finished.v @@ -0,0 +1,24 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + process p; + + initial begin + p = process::self(); + end + + always @(posedge clk) begin + if (p.status() != process::FINISHED) + $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_process_fork.out b/test_regress/t/t_process_fork.out new file mode 100644 index 000000000..8b4a1a959 --- /dev/null +++ b/test_regress/t/t_process_fork.out @@ -0,0 +1,11 @@ +job started +job started +job started +job started +job started +job started +job started +job started +all jobs started +all jobs finished +*-* All Finished *-* diff --git a/test_regress/t/t_process_fork.pl b/test_regress/t/t_process_fork.pl new file mode 100755 index 000000000..604f632d4 --- /dev/null +++ b/test_regress/t/t_process_fork.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); + +compile( + verilator_flags2 => ["--timing"], + ); + +execute( + check_finished => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_process_fork.v b/test_regress/t/t_process_fork.v new file mode 100644 index 000000000..1ea1c477e --- /dev/null +++ b/test_regress/t/t_process_fork.v @@ -0,0 +1,30 @@ +// 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; + process job[] = new [8]; + bit is_alloc = 0; + + initial begin + foreach (job[j]) fork + begin + $write("job started\n"); + job[j] = process::self(); + end + join_none + foreach (job[j]) begin + is_alloc = !!job[j]; + wait (is_alloc); + end + $write("all jobs started\n"); + foreach (job[j]) begin + job[j].await(); + end + $write("all jobs finished\n"); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_process_kill.pl b/test_regress/t/t_process_kill.pl new file mode 100755 index 000000000..b267631e9 --- /dev/null +++ b/test_regress/t/t_process_kill.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 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( + verilator_flags2 => ["--timing"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_process_kill.v b/test_regress/t/t_process_kill.v new file mode 100644 index 000000000..8c0252939 --- /dev/null +++ b/test_regress/t/t_process_kill.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 Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + process p; + bit s = 0; + + initial begin + wait (s); + p.kill(); + p.await(); + $write("*-* All Finished *-*\n"); + $finish; + end + + always @(posedge clk) begin + if (!p) begin + p = process::self(); + s = 1; + end else begin + $stop; + end + end +endmodule diff --git a/test_regress/t/t_process_notiming.out b/test_regress/t/t_process_notiming.out new file mode 100644 index 000000000..833040f13 --- /dev/null +++ b/test_regress/t/t_process_notiming.out @@ -0,0 +1,54 @@ +%Error-NOTIMING: t/t_process.v:26:20: process::self() requires --timing + : ... In instance t + 26 | p = process::self(); + | ^~~~ + ... For error description see https://verilator.org/warn/NOTIMING?v=latest +%Error-NOTIMING: t/t_process.v:27:13: process::status() requires --timing + : ... In instance t + 27 | if (p.status() != process::RUNNING) $stop; + | ^~~~~~ +%Error-NOTIMING: t/t_process.v:28:13: process::status() requires --timing + : ... In instance t + 28 | if (p.status() == process::WAITING) $stop; + | ^~~~~~ +%Error-NOTIMING: t/t_process.v:29:13: process::status() requires --timing + : ... In instance t + 29 | if (p.status() == process::SUSPENDED) $stop; + | ^~~~~~ +%Error-NOTIMING: t/t_process.v:30:13: process::status() requires --timing + : ... In instance t + 30 | if (p.status() == process::KILLED) $stop; + | ^~~~~~ +%Error-NOTIMING: t/t_process.v:31:13: process::status() requires --timing + : ... In instance t + 31 | if (p.status() == process::FINISHED) $stop; + | ^~~~~~ +%Error-NOTIMING: t/t_process.v:33:16: process::kill() requires --timing + : ... In instance t + 33 | if (0) p.kill(); + | ^~~~ +%Error-NOTIMING: t/t_process.v:34:16: process::await() requires --timing + : ... In instance t + 34 | if (0) p.await(); + | ^~~~~ +%Error-NOTIMING: t/t_process.v:35:16: process::suspend() requires --timing + : ... In instance t + 35 | if (0) p.suspend(); + | ^~~~~~~ +%Error-NOTIMING: t/t_process.v:36:16: process::resume() requires --timing + : ... In instance t + 36 | if (0) p.resume(); + | ^~~~~~ +%Error-NOTIMING: t/t_process.v:38:9: process::srandom() requires --timing + : ... In instance t + 38 | p.srandom(0); + | ^~~~~~~ +%Error-NOTIMING: t/t_process.v:39:25: process::get_randstate() requires --timing + : ... In instance t + 39 | p.set_randstate(p.get_randstate()); + | ^~~~~~~~~~~~~ +%Error-NOTIMING: t/t_process.v:39:9: process::set_randstate() requires --timing + : ... In instance t + 39 | p.set_randstate(p.get_randstate()); + | ^~~~~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_process_notiming.pl b/test_regress/t/t_process_notiming.pl new file mode 100755 index 000000000..9a1abce98 --- /dev/null +++ b/test_regress/t/t_process_notiming.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 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); + +top_filename("t/t_process.v"); + +compile( + expect_filename => $Self->{golden_filename}, + v_flags2 => ["+define+T_PROCESS+std::process", "--no-timing"], + fails => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_process_parse.pl b/test_regress/t/t_process_parse.pl index 71e0db056..0023276e1 100755 --- a/test_regress/t/t_process_parse.pl +++ b/test_regress/t/t_process_parse.pl @@ -12,9 +12,16 @@ scenarios(vlt => 1); top_filename("t_process.v"); -lint( - verilator_flags2 => ["--debug-exit-uvm"], +my $out_filename = "$Self->{obj_dir}/V$Self->{name}.xml"; + +compile( + verilator_flags2 => ["--debug-exit-uvm", "--xml-only"], + make_main => 0, + make_top_shell => 0, + verilator_make_gmake => 0, ); +file_grep($out_filename, qr/./); # Exists + ok(1); 1; diff --git a/test_regress/t/t_process_rand.pl b/test_regress/t/t_process_rand.pl new file mode 100755 index 000000000..4152e3d9b --- /dev/null +++ b/test_regress/t/t_process_rand.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( + v_flags2 => ["--timing"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_process_rand.v b/test_regress/t/t_process_rand.v new file mode 100644 index 000000000..20978fc04 --- /dev/null +++ b/test_regress/t/t_process_rand.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 Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module t; + process p; + + integer seed; + string state; + int a; + int b; + + initial begin + p = process::self(); + + // Test setting RNG state with state string + state = p.get_randstate(); + p.set_randstate(state); + a = $random; + p.set_randstate(state); + b = $random; + $display("a=%d, b=%d", a, b); + if (a != b) $stop; + + // Test the same with $urandom + state = p.get_randstate(); + p.set_randstate(state); + a = $urandom; + p.set_randstate(state); + b = $urandom; + $display("a=%d, b=%d", a, b); + if (a != b) $stop; + + // Test if the results repeat after the state is reset + state = p.get_randstate(); + for (int i = 0; i < 10; i++) + $random; + a = $random; + // Now reset the state and take 11th result again + p.set_randstate(state); + for (int i = 0; i < 10; i++) + $random; + b = $random; + $display("a=%d, b=%d", a, b); + if (a != b) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_process_std.pl b/test_regress/t/t_process_std.pl index 64cabbccb..c9318acf2 100755 --- a/test_regress/t/t_process_std.pl +++ b/test_regress/t/t_process_std.pl @@ -13,14 +13,12 @@ scenarios(simulator => 1); top_filename("t/t_process.v"); compile( - v_flags2 => ["+define+T_PROCESS+std::process"], + v_flags2 => ["+define+T_PROCESS+std::process", "--timing"], ); execute( - check_finished => !$Self->{vlt_all}, - fails => $Self->{vlt_all}, - expect_filename => $Self->{golden_filename}, - ); + check_finished => 1, + ) if !$Self->{vlt_all}; ok(1); 1; diff --git a/test_regress/t/t_queue_method.v b/test_regress/t/t_queue_method.v index 24c90e470..59c4dc4a5 100644 --- a/test_regress/t/t_queue_method.v +++ b/test_regress/t/t_queue_method.v @@ -188,6 +188,11 @@ module t (/*AUTOARG*/); i = qe.xor; `checkh(i, 32'b0); + q = '{1, 2}; + qe = '{1, 2}; + `checkh(q == qe, 1'b1); + `checkh(q != qe, 1'b0); + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_randc.out b/test_regress/t/t_randc.out new file mode 100644 index 000000000..f83777149 --- /dev/null +++ b/test_regress/t/t_randc.out @@ -0,0 +1,12 @@ +%Warning-RANDC: t/t_randc.v:8:26: Unsupported: Converting 'randc' to 'rand' + 8 | randc bit [WIDTH-1:0] m_var; + | ^~~~~ + ... For warning description see https://verilator.org/warn/RANDC?v=latest + ... Use "/* verilator lint_off RANDC */" and lint_on around source to disable this message. +%Warning-RANDC: t/t_randc.v:46:26: Unsupported: Converting 'randc' to 'rand' + 46 | randc bit [WIDTH-1:0] m_var; + | ^~~~~ +%Warning-RANDC: t/t_randc.v:76:17: Unsupported: Converting 'randc' to 'rand' + 76 | randc enum_t m_var; + | ^~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_randc.pl b/test_regress/t/t_randc.pl new file mode 100755 index 000000000..66fa61649 --- /dev/null +++ b/test_regress/t/t_randc.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_randc.v b/test_regress/t/t_randc.v new file mode 100644 index 000000000..683cb996c --- /dev/null +++ b/test_regress/t/t_randc.v @@ -0,0 +1,134 @@ +// 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 ClsNarrow #(parameter int WIDTH); + randc bit [WIDTH-1:0] m_var; + + function void test; + automatic int i; + automatic int count[2**WIDTH]; + automatic int maxcount; + automatic bit bad; + automatic int randomize_result; + $display("Test %m"); + for (int trial = 0; trial < 10; ++trial) begin + for (i = 0; i < (2 ** WIDTH); ++i) begin + randomize_result = randomize(); + if (randomize_result !== 1) $stop; +`ifdef TEST_VERBOSE + $display("w%0d i=%0d m_var=%x", WIDTH, i, m_var); +`endif + ++count[m_var]; + end + end + maxcount = count[0]; + bad = '0; +`ifndef TEST_IGNORE_RANDC + for (i = 0; i < (2 ** WIDTH); ++i) begin + if (maxcount != count[i]) bad = '1; + end +`endif + if (bad) begin + $display("%%Error: count mismatch"); + for (i = 0; i < (2 ** WIDTH); ++i) begin + $display("w%0d entry[%0d]=%0d", WIDTH, i, count[i]); + end + $stop; + end + endfunction + +endclass + +class ClsWide #(parameter int WIDTH); + randc bit [WIDTH-1:0] m_var; + + function void test; + automatic bit [WIDTH-1:0] last; + automatic int randomize_result; + $display("Test %m"); + for (int i = 0; i < 100; ++i) begin + randomize_result = randomize(); + if (randomize_result !== 1) $stop; +`ifdef TEST_VERBOSE + $display("ww%0d i=%0d m_var=%x", WIDTH, i, m_var); +`endif + if (i != 0) begin +`ifndef TEST_IGNORE_RANDC + if (m_var == last) $stop; +`endif + end + last = m_var; + end + endfunction + +endclass + +class ClsEnum; + typedef enum bit [3:0] { + TWO = 2, + FIVE = 5, + SIX = 6 + } enum_t; + + randc enum_t m_var; + + function void test; + automatic enum_t last; + automatic int randomize_result; + $display("Test %m"); + for (int trial = 0; trial < 10; ++trial) begin + for (int i = 0; i < 3; ++i) begin + randomize_result = randomize(); + if (randomize_result !== 1) $stop; +`ifdef TEST_VERBOSE + $display("we i=%0d m_var=%x", i, m_var); +`endif + if (m_var != TWO && m_var != FIVE && m_var != SIX) $stop; + if (i != 0) begin +`ifndef TEST_IGNORE_RANDC + if (m_var == last) $stop; +`endif + end + last = m_var; + end + end + endfunction + +endclass + +module t (/*AUTOARG*/); + + ClsNarrow #(1) c1; // Degenerate case + ClsNarrow #(2) c2; + ClsNarrow #(3) c3; + ClsNarrow #(3) c3b; // Need to have two of same size to cover dtype dedup code + ClsNarrow #(9) c9; + ClsWide #(31) c31; + ClsWide #(32) c32; + ClsEnum ce; + + initial begin + c1 = new; + c1.test(); + c2 = new; + c2.test(); + c3 = new; + c3.test(); + c3b = new; + c3b.test(); + c9 = new; + c9.test(); + c31 = new; + c31.test(); + c32 = new; + c32.test(); + ce = new; + ce.test(); + $write("*-* All Finished *-*\n"); + $finish(); + end + +endmodule diff --git a/test_regress/t/t_randc_ignore_unsup.pl b/test_regress/t/t_randc_ignore_unsup.pl index a161852b1..af711b6c9 100755 --- a/test_regress/t/t_randc_ignore_unsup.pl +++ b/test_regress/t/t_randc_ignore_unsup.pl @@ -10,8 +10,10 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); +top_filename("t/t_randc.v"); + compile( - verilator_flags2 => ['-Wno-RANDC'], + verilator_flags2 => ['-Wno-RANDC -DTEST_IGNORE_RANDC'], ); execute( diff --git a/test_regress/t/t_randc_ignore_unsup.v b/test_regress/t/t_randc_ignore_unsup.v deleted file mode 100644 index a2087e6b0..000000000 --- a/test_regress/t/t_randc_ignore_unsup.v +++ /dev/null @@ -1,38 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2019 by Wilson Snyder. -// SPDX-License-Identifier: CC0-1.0 - -class Cls; - randc int i; - - function new; - i = 0; - endfunction - -endclass - -module t (/*AUTOARG*/); - bit ok = 0; - - Cls obj; - - initial begin - int rand_result; - int prev_i; - for (int i = 0; i < 10; i++) begin - obj = new; - rand_result = obj.randomize(); - if (i > 0 && obj.i != prev_i) begin - ok = 1; - end - prev_i = obj.i; - end - if (ok) begin - $write("*-* All Finished *-*\n"); - $finish; - end - else $stop; - end -endmodule diff --git a/test_regress/t/t_randc_unsup.out b/test_regress/t/t_randc_oversize_bad.out similarity index 59% rename from test_regress/t/t_randc_unsup.out rename to test_regress/t/t_randc_oversize_bad.out index 7d3436a4a..293258c1f 100644 --- a/test_regress/t/t_randc_unsup.out +++ b/test_regress/t/t_randc_oversize_bad.out @@ -1,6 +1,6 @@ -%Warning-RANDC: t/t_randc_unsup.v:8:14: Unsupported: Converting 'randc' to 'rand' - 8 | randc int i; - | ^ +%Warning-RANDC: t/t_randc_oversize_bad.v:8:21: Unsupported: Converting 'randc' to 'rand' + 8 | randc bit [33:0] i; + | ^ ... For warning description see https://verilator.org/warn/RANDC?v=latest ... Use "/* verilator lint_off RANDC */" and lint_on around source to disable this message. %Error: Exiting due to diff --git a/test_regress/t/t_randc_unsup.pl b/test_regress/t/t_randc_oversize_bad.pl similarity index 100% rename from test_regress/t/t_randc_unsup.pl rename to test_regress/t/t_randc_oversize_bad.pl diff --git a/test_regress/t/t_randc_unsup.v b/test_regress/t/t_randc_oversize_bad.v similarity index 73% rename from test_regress/t/t_randc_unsup.v rename to test_regress/t/t_randc_oversize_bad.v index 1a966261a..852f4743c 100644 --- a/test_regress/t/t_randc_unsup.v +++ b/test_regress/t/t_randc_oversize_bad.v @@ -1,11 +1,11 @@ // DESCRIPTION: Verilator: Verilog Test module // // This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2019 by Wilson Snyder. +// any use, without warranty, 2023 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 class Cls; - randc int i; + randc bit [33:0] i; endclass module t (/*AUTOARG*/); diff --git a/test_regress/t/t_randcase_bad.out b/test_regress/t/t_randcase_bad.out index 4e3daeeac..6d1b58341 100644 --- a/test_regress/t/t_randcase_bad.out +++ b/test_regress/t/t_randcase_bad.out @@ -1,2 +1,2 @@ -[0] %Error: t_randcase_bad.v:12: Assertion failed in top.t.unnamedblk1: All randcase items had 0 weights (IEEE 1800-2017 18.16) +[0] %Error: t_randcase_bad.v:12: Assertion failed in top.t.unnamedblk2_1: All randcase items had 0 weights (IEEE 1800-2017 18.16) *-* All Finished *-* diff --git a/test_regress/t/t_randomize_small.pl b/test_regress/t/t_randomize_small.pl new file mode 100755 index 000000000..49330a5fe --- /dev/null +++ b/test_regress/t/t_randomize_small.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 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( + ); + +ok(1); +1; diff --git a/test_regress/t/t_randomize_small.v b/test_regress/t/t_randomize_small.v new file mode 100644 index 000000000..1b91feab4 --- /dev/null +++ b/test_regress/t/t_randomize_small.v @@ -0,0 +1,29 @@ +// 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 Cls; + rand int m_val; + + function void test; + automatic int rand_result; + + rand_result = randomize(); + if (rand_result != 1) $stop; + endfunction +endclass + +module t(/*AUTOARG*/); + + initial begin + Cls c; + c = new; + + c.test; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randstate_func.pl b/test_regress/t/t_randstate_func.pl new file mode 100755 index 000000000..aabcde63e --- /dev/null +++ b/test_regress/t/t_randstate_func.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_randstate_func.v b/test_regress/t/t_randstate_func.v new file mode 100644 index 000000000..87563d1f4 --- /dev/null +++ b/test_regress/t/t_randstate_func.v @@ -0,0 +1,48 @@ +// 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 Cls; + rand int length; + + function void test; + automatic int rand_result, v1, v2; + automatic string s; + + // UVM 2023 does a print, so check is ascii + $display("get_randstate = '%s'", get_randstate()); + + s = get_randstate(); + + rand_result = randomize(); + if (rand_result != 1) $stop; + v1 = length; + + set_randstate(s); + + rand_result = randomize(); + if (rand_result != 1) $stop; + v2 = length; + +`ifdef VERILATOR // About half of the other simulators fail at this + if (v1 != v2) $stop; +`endif + endfunction +endclass + +module t(/*AUTOARG*/); + + automatic int rand_result, v1, v2; + automatic string s; + + initial begin + Cls c; + c = new; + c.test; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randstate_obj.out b/test_regress/t/t_randstate_obj.out new file mode 100644 index 000000000..74f858877 --- /dev/null +++ b/test_regress/t/t_randstate_obj.out @@ -0,0 +1,10 @@ +%Error-UNSUPPORTED: t/t_randstate_obj.v:20:13: Unsupported: 'get_randstate'/'set_randstate' called on object. Suggest call from inside class. + : ... In instance t + 20 | s = c.get_randstate(); + | ^~~~~~~~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_randstate_obj.v:26:9: Unsupported: 'get_randstate'/'set_randstate' called on object. Suggest call from inside class. + : ... In instance t + 26 | c.set_randstate(s); + | ^~~~~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_randstate_obj.pl b/test_regress/t/t_randstate_obj.pl new file mode 100755 index 000000000..b50a85167 --- /dev/null +++ b/test_regress/t/t_randstate_obj.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(simulator => 1); + +compile( + expect_filename => $Self->{golden_filename}, + fails => $Self->{vlt_all}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_randstate_obj.v b/test_regress/t/t_randstate_obj.v new file mode 100644 index 000000000..bc5255913 --- /dev/null +++ b/test_regress/t/t_randstate_obj.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class Cls; + rand int length; +endclass + +module t(/*AUTOARG*/); + + automatic int rand_result, v1, v2; + automatic string s; + + initial begin + Cls c; + c = new; + + s = c.get_randstate(); + + rand_result = c.randomize(); + if (rand_result != 1) $stop; + v1 = c.length; + + c.set_randstate(s); + + rand_result = c.randomize(); + if (rand_result != 1) $stop; + v2 = c.length; + +`ifdef VERILATOR // About half of the other simulators fail at this + if (v1 != v2) $stop; +`endif + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randstate_seed_bad.out b/test_regress/t/t_randstate_seed_bad.out new file mode 100644 index 000000000..75e3250f6 --- /dev/null +++ b/test_regress/t/t_randstate_seed_bad.out @@ -0,0 +1,2 @@ +%Warning: set_randstate ignored as state string not from get_randstate +*-* All Finished *-* diff --git a/test_regress/t/t_randstate_seed_bad.pl b/test_regress/t/t_randstate_seed_bad.pl new file mode 100755 index 000000000..c7c7ef8ca --- /dev/null +++ b/test_regress/t/t_randstate_seed_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 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( + ); + +execute( + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_randstate_seed_bad.v b/test_regress/t/t_randstate_seed_bad.v new file mode 100644 index 000000000..ebcf2c0a9 --- /dev/null +++ b/test_regress/t/t_randstate_seed_bad.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 + +class Cls; + function void test; + automatic string s; + + s = get_randstate(); + // Vlt only result check + if (s[0] !== "R") $fatal(2, $sformatf("Bad get_randstate = '%s'", s)); + + set_randstate("000bad"); // Bad + endfunction +endclass + +module t(/*AUTOARG*/); + + initial begin + Cls c; + c = new; + c.test; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_semaphore.pl b/test_regress/t/t_semaphore.pl index d7ae01e91..30306c8ce 100755 --- a/test_regress/t/t_semaphore.pl +++ b/test_regress/t/t_semaphore.pl @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing -Wall"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing -Wall"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_semaphore_always.pl b/test_regress/t/t_semaphore_always.pl new file mode 100755 index 000000000..b267631e9 --- /dev/null +++ b/test_regress/t/t_semaphore_always.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 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( + verilator_flags2 => ["--timing"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_semaphore_always.v b/test_regress/t/t_semaphore_always.v new file mode 100644 index 000000000..ddfc9b568 --- /dev/null +++ b/test_regress/t/t_semaphore_always.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 (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + semaphore s = new; + + initial begin + s.put(); + end + + always @(posedge clk) begin + s.get(); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_semaphore_class.pl b/test_regress/t/t_semaphore_class.pl index 3329d516e..6e30bd25d 100755 --- a/test_regress/t/t_semaphore_class.pl +++ b/test_regress/t/t_semaphore_class.pl @@ -10,14 +10,9 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--timing"], - ); -} +compile( + verilator_flags2 => ["--timing"], + ); ok(1); 1; diff --git a/test_regress/t/t_semaphore_std.pl b/test_regress/t/t_semaphore_std.pl index 7fcb5b404..47041497b 100755 --- a/test_regress/t/t_semaphore_std.pl +++ b/test_regress/t/t_semaphore_std.pl @@ -10,21 +10,16 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_semaphore.v"); +top_filename("t/t_semaphore.v"); - compile( - verilator_flags2 => ["--exe --main --timing -Wall -DSEMAPHORE_T=std::semaphore"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing -Wall -DSEMAPHORE_T=std::semaphore"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_static_dup_name.pl b/test_regress/t/t_static_dup_name.pl new file mode 100755 index 000000000..db885e46d --- /dev/null +++ b/test_regress/t/t_static_dup_name.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 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); + +$Self->{verilated_randReset} = 1; + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_static_dup_name.v b/test_regress/t/t_static_dup_name.v new file mode 100644 index 000000000..9d3765238 --- /dev/null +++ b/test_regress/t/t_static_dup_name.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, 2020 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t; + + function do_stuff(); + static int some_int; + begin: block0 + static int some_int; + end + begin: block1 + static int some_int; + end + begin + static int some_int; + end + begin: block2 + begin: block3 + static int some_int; + end + begin + static int some_int; + end + end + endfunction + + initial begin + $write("*-* All Finished *-*\n"); + $finish(); + end + +endmodule diff --git a/test_regress/t/t_strength_assignments_constants.pl b/test_regress/t/t_strength_assignments_constants.pl index 1aa73f80a..85ac7d5bc 100755 --- a/test_regress/t/t_strength_assignments_constants.pl +++ b/test_regress/t/t_strength_assignments_constants.pl @@ -17,5 +17,9 @@ execute( check_finished => 1, ); +lint( + verilator_flags2 => ["--language 1364-2005"] + ); + ok(1); 1; diff --git a/test_regress/t/t_strength_assignments_constants.v b/test_regress/t/t_strength_assignments_constants.v index 8344adbdd..c91b7104f 100644 --- a/test_regress/t/t_strength_assignments_constants.v +++ b/test_regress/t/t_strength_assignments_constants.v @@ -14,18 +14,18 @@ module t (/*AUTOARG*/); assign (strong0, strong1) b = 0; wire [1:0] c; - assign (weak0, supply1) c = '1; - assign (supply0, pull1) c = '1; - assign (strong0, strong1) c = '0; + assign (weak0, supply1) c = 2'b11; + assign (supply0, pull1) c = 2'b11; + assign (strong0, strong1) c = 0; supply0 d; assign (strong0, strong1) d = 1; - wire (supply0, supply1) e = 'z; + wire (supply0, supply1) e = 1'bz; assign (weak0, weak1) e = 1; always begin - if (a && !b && c === '1 && !d && e) begin + if (a && !b && c === 2'b11 && !d && e) begin $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_strength_highz.pl b/test_regress/t/t_strength_highz.pl index 48bf31461..6537d741c 100755 --- a/test_regress/t/t_strength_highz.pl +++ b/test_regress/t/t_strength_highz.pl @@ -11,6 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); lint( + verilator_flags2 => ["--language 1364-2005"], fails => $Self->{vlt_all}, expect_filename => $Self->{golden_filename}, ); diff --git a/test_regress/t/t_struct_assign.v b/test_regress/t/t_struct_assign.v index c0dc33341..75939c37b 100644 --- a/test_regress/t/t_struct_assign.v +++ b/test_regress/t/t_struct_assign.v @@ -9,7 +9,12 @@ module t (/*AUTOARG*/); int fst, snd; } pair_t; + class Cls; + pair_t p; + endclass + pair_t a, b; + Cls c = new; initial begin a.fst = 1; @@ -21,6 +26,15 @@ module t (/*AUTOARG*/); $display("(%d, %d) (%d, %d)", a.fst, a.snd, b.fst, b.snd); $display("%%p=%p", a); + + c.p.fst = 5; + if (c.p.fst != 5) $stop; + a = c.p; + if (a.fst != 5) $stop; + c.p = b; + if (c.p.fst != 3) $stop; + if (c.p.snd != 4) $stop; + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_struct_nest_uarray.pl b/test_regress/t/t_struct_nest_uarray.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_struct_nest_uarray.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_struct_nest_uarray.v b/test_regress/t/t_struct_nest_uarray.v new file mode 100644 index 000000000..81cdeee3f --- /dev/null +++ b/test_regress/t/t_struct_nest_uarray.v @@ -0,0 +1,48 @@ +// 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 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); + +typedef struct { + struct { + struct { + logic [31:0] next; + } val; + } el[1]; +} pstr_t; + +module t (/*AUTOARG*/); + + typedef struct { + struct { + struct { + logic [31:0] next; + } val; + } el[1]; + } str_t; + + str_t str; + pstr_t pstr; + + initial begin + string s; + + str.el[0].val.next = 6; + s = $sformatf("%p", str); + $display("%s", s); + `checks(s, "'{el:'{'{val:'{next:'h6}}} }"); + + pstr.el[0].val.next = 6; + s = $sformatf("%p", pstr); + $display("%s", s); + `checks(s, "'{el:'{'{val:'{next:'h6}}} }"); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_structu_wide.pl b/test_regress/t/t_structu_wide.pl new file mode 100755 index 000000000..e039728bc --- /dev/null +++ b/test_regress/t/t_structu_wide.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 => [ '-DWIDE_WIDTH=128' ], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_structu_wide.v b/test_regress/t/t_structu_wide.v new file mode 100644 index 000000000..11858ba44 --- /dev/null +++ b/test_regress/t/t_structu_wide.v @@ -0,0 +1,29 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Jomit626. +// SPDX-License-Identifier: CC0-1.0 + +`ifndef WIDE_WIDTH +`define WIDE_WIDTH 128 +`endif + +module t (); + typedef struct { + bit [`WIDE_WIDTH-1:0] data; + } wide_t; + + logic [`WIDE_WIDTH-1:0] ldata; + wide_t wide_0; + + initial begin + wide_0.data = `WIDE_WIDTH'hda7ada7a; + ldata = wide_0.data; + + if (ldata != `WIDE_WIDTH'hda7ada7a) + $stop(); + + $write("*-* All Finished *-*\n"); + $finish(); + end +endmodule diff --git a/test_regress/t/t_suspendable_deep.pl b/test_regress/t/t_suspendable_deep.pl new file mode 100755 index 000000000..e7824bce9 --- /dev/null +++ b/test_regress/t/t_suspendable_deep.pl @@ -0,0 +1,18 @@ +#!/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( + verilator_flags2 => ["--timing"], + ); + +ok(1); +1; diff --git a/test_regress/t/t_suspendable_deep.v b/test_regress/t/t_suspendable_deep.v new file mode 100644 index 000000000..32ecd0a4c --- /dev/null +++ b/test_regress/t/t_suspendable_deep.v @@ -0,0 +1,35 @@ +// DESCRIPTION: Verilator: Verilog Test module for specialized type default values +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +`timescale 1ns/1ns + +event evt; + +class Baz; + virtual task do_something(); endtask +endclass + +class Foo extends Baz; +endclass + +class Bar extends Foo; + virtual task do_something(); + @evt $display("Hello"); + endtask +endclass + +module top(); + initial begin + Bar bar; + bar = new; + + fork + #10 bar.do_something(); + #20 $display("world!"); + #10 ->evt; + join + end +endmodule diff --git a/test_regress/t/t_sys_file_basic.v b/test_regress/t/t_sys_file_basic.v index 7a0f0bda6..ac276d9a4 100644 --- a/test_regress/t/t_sys_file_basic.v +++ b/test_regress/t/t_sys_file_basic.v @@ -33,6 +33,9 @@ module t; integer v_length, v_off; + wire signed [16:0] wire17 = 17'h1ffff; + logic signed [16:0] scan17; + `ifdef TEST_VERBOSE `define verbose 1'b1 `else @@ -309,6 +312,11 @@ module t; $fclose(file); end + begin + $sscanf("-1", "%d", scan17); + if (scan17 !== wire17) $stop; + end + $write("*-* All Finished *-*\n"); $finish(0); // Test arguments to finish end diff --git a/test_regress/t/t_timing_always.pl b/test_regress/t/t_timing_always.pl index f86c4b944..b8493bd06 100755 --- a/test_regress/t/t_timing_always.pl +++ b/test_regress/t/t_timing_always.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_class.pl b/test_regress/t/t_timing_class.pl index c469d3de3..002312d88 100755 --- a/test_regress/t/t_timing_class.pl +++ b/test_regress/t/t_timing_class.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_class.v b/test_regress/t/t_timing_class.v index 263b2e44f..fbd4136c2 100644 --- a/test_regress/t/t_timing_class.v +++ b/test_regress/t/t_timing_class.v @@ -10,10 +10,18 @@ `define WRITE_VERBOSE(args) `endif +class BaseClass; + virtual task sleep; + endtask + + virtual task await; + endtask +endclass + module t; // ============================================= // EVENTS - class EventClass; + class EventClass extends BaseClass; event e; int trig_count; @@ -35,7 +43,7 @@ module t; endtask endclass - class WaitClass; + class WaitClass extends BaseClass; int a; int b; logic ok; @@ -53,7 +61,7 @@ module t; endtask endclass - class LocalWaitClass; + class LocalWaitClass extends BaseClass; logic ok; function new; diff --git a/test_regress/t/t_timing_clkgen1.pl b/test_regress/t/t_timing_clkgen1.pl index aa7288ef4..edbe747b4 100755 --- a/test_regress/t/t_timing_clkgen1.pl +++ b/test_regress/t/t_timing_clkgen1.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing -Wno-MINTYPMAXDLY"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing -Wno-MINTYPMAXDLY"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_clkgen2.pl b/test_regress/t/t_timing_clkgen2.pl index f86c4b944..b8493bd06 100755 --- a/test_regress/t/t_timing_clkgen2.pl +++ b/test_regress/t/t_timing_clkgen2.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_clkgen3.pl b/test_regress/t/t_timing_clkgen3.pl index f86c4b944..b8493bd06 100755 --- a/test_regress/t/t_timing_clkgen3.pl +++ b/test_regress/t/t_timing_clkgen3.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_clkgen_sc.pl b/test_regress/t/t_timing_clkgen_sc.pl index a5fe43850..728e33f11 100755 --- a/test_regress/t/t_timing_clkgen_sc.pl +++ b/test_regress/t/t_timing_clkgen_sc.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,23 +10,15 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -elsif (!$Self->have_sc) { - skip("No SystemC installed"); -} -else { - top_filename("t/t_timing_clkgen2.v"); +top_filename("t/t_timing_clkgen2.v"); - compile( - verilator_flags2 => ["--sc --exe --timing --timescale 10ps/1ps"], - ); +compile( + verilator_flags2 => ["--sc --exe --timing --timescale 10ps/1ps"], + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_debug1.pl b/test_regress/t/t_timing_debug1.pl index 0d4523725..06be36082 100755 --- a/test_regress/t/t_timing_debug1.pl +++ b/test_regress/t/t_timing_debug1.pl @@ -10,25 +10,20 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt_all => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_timing_sched.v"); +top_filename("t/t_timing_sched.v"); - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - all_run_flags => ["+verilator+debug"], - check_finished => 1, - ); +execute( + all_run_flags => ["+verilator+debug"], + check_finished => 1, + ); - if (!$Self->{vltmt}) { # vltmt output may vary between thread exec order - files_identical("$Self->{obj_dir}/vlt_sim.log", $Self->{golden_filename}, "logfile"); - } +if (!$Self->{vltmt}) { # vltmt output may vary between thread exec order + files_identical("$Self->{obj_dir}/vlt_sim.log", $Self->{golden_filename}, "logfile"); } ok(1); diff --git a/test_regress/t/t_timing_debug2.out b/test_regress/t/t_timing_debug2.out index 3a4ac0ecb..fb5698b73 100644 --- a/test_regress/t/t_timing_debug2.out +++ b/test_regress/t/t_timing_debug2.out @@ -1,5 +1,7 @@ -V{t#,#}- Verilated::debug is on. Message prefix indicates {,}. -V{t#,#}+ Vt_timing_debug2___024root___ctor_var_reset +-V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass__Vclpkg___ctor_var_reset +-V{t#,#}+ Vt_timing_debug2___024unit___ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t___ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass__Vclpkg___ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10__Vclpkg___ctor_var_reset @@ -17,15 +19,21 @@ -V{t#,#}+ Initial -V{t#,#}+ Vt_timing_debug2___024root___eval_static -V{t#,#}+ Vt_timing_debug2_t___eval_static__TOP__t +-V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::new +-V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass::new -V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass::_ctor_var_reset +-V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::new +-V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t__03a__03aWaitClass::new -V{t#,#}+ Vt_timing_debug2_t__03a__03aWaitClass::_ctor_var_reset +-V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::new +-V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t__03a__03aLocalWaitClass::new -V{t#,#}+ Vt_timing_debug2_t__03a__03aLocalWaitClass::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2___024root___eval_initial -V{t#,#}+ Vt_timing_debug2_t___eval_initial__TOP__t__0 --V{t#,#} Suspending process waiting for @([event] t.ec.e) at t/t_timing_class.v:80 +-V{t#,#} Suspending process waiting for @([event] t.ec.e) at t/t_timing_class.v:88 -V{t#,#}+ Vt_timing_debug2_t___eval_initial__TOP__t__1 -V{t#,#}+ Vt_timing_debug2_t___eval_initial__TOP__t__2 -V{t#,#}+ Vt_timing_debug2_t___eval_initial__TOP__t__3 @@ -57,7 +65,7 @@ -V{t#,#}+ Vt_timing_debug2_t__03a__03aForkClass::__Vfork_h########__0__0 -V{t#,#}+ Vt_timing_debug2_t__03a__03aForkClass::__Vfork_h########__0__1 -V{t#,#}+ Vt_timing_debug2_t__03a__03aForkClass::__Vfork_h########__0__2 --V{t#,#} Awaiting join of fork at: t/t_timing_class.v:209 +-V{t#,#} Awaiting join of fork at: t/t_timing_class.v:217 -V{t#,#}+ Vt_timing_debug2_t___eval_initial__TOP__t__7 -V{t#,#}+ Vt_timing_debug2___024root___eval_settle -V{t#,#}MTask0 starting @@ -69,7 +77,7 @@ -V{t#,#} No triggers active -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#} Committing processes waiting for @([event] t.ec.e): --V{t#,#} - Process waiting at t/t_timing_class.v:80 +-V{t#,#} - Process waiting at t/t_timing_class.v:88 -V{t#,#}End-of-eval cleanup -V{t#,#}+++++TOP Evaluate Vt_timing_debug2::eval_step -V{t#,#}+ Vt_timing_debug2___024root___eval_debug_assertions @@ -83,21 +91,21 @@ -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} Delayed processes: --V{t#,#} Awaiting time 10: Process waiting at t/t_timing_class.v:88 --V{t#,#} Awaiting time 20: Process waiting at t/t_timing_class.v:89 --V{t#,#} Awaiting time 10: Process waiting at t/t_timing_class.v:91 --V{t#,#} Awaiting time 30: Process waiting at t/t_timing_class.v:101 --V{t#,#} Awaiting time 40: Process waiting at t/t_timing_class.v:137 --V{t#,#} Awaiting time 50: Process waiting at t/t_timing_class.v:211 --V{t#,#} Awaiting time 20: Process waiting at t/t_timing_class.v:215 --V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:220 --V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:236 +-V{t#,#} Awaiting time 10: Process waiting at t/t_timing_class.v:96 +-V{t#,#} Awaiting time 20: Process waiting at t/t_timing_class.v:97 +-V{t#,#} Awaiting time 10: Process waiting at t/t_timing_class.v:99 +-V{t#,#} Awaiting time 30: Process waiting at t/t_timing_class.v:109 +-V{t#,#} Awaiting time 40: Process waiting at t/t_timing_class.v:145 +-V{t#,#} Awaiting time 50: Process waiting at t/t_timing_class.v:219 +-V{t#,#} Awaiting time 20: Process waiting at t/t_timing_class.v:223 +-V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:228 +-V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:244 -V{t#,#} Resuming delayed processes --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:236 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:244 -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::__VnoInFunc_do_delay --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:138 --V{t#,#} Process forked at t/t_timing_class.v:210 finished +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:146 +-V{t#,#} Process forked at t/t_timing_class.v:218 finished -V{t#,#}+ Vt_timing_debug2___024root___eval_act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} No suspended processes waiting for dynamic trigger evaluation @@ -123,20 +131,20 @@ -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} Delayed processes: --V{t#,#} Awaiting time 20: Process waiting at t/t_timing_class.v:88 --V{t#,#} Awaiting time 20: Process waiting at t/t_timing_class.v:89 --V{t#,#} Awaiting time 30: Process waiting at t/t_timing_class.v:91 --V{t#,#} Awaiting time 30: Process waiting at t/t_timing_class.v:101 --V{t#,#} Awaiting time 40: Process waiting at t/t_timing_class.v:137 --V{t#,#} Awaiting time 50: Process waiting at t/t_timing_class.v:211 --V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:215 --V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:220 +-V{t#,#} Awaiting time 20: Process waiting at t/t_timing_class.v:96 +-V{t#,#} Awaiting time 20: Process waiting at t/t_timing_class.v:97 +-V{t#,#} Awaiting time 30: Process waiting at t/t_timing_class.v:99 +-V{t#,#} Awaiting time 30: Process waiting at t/t_timing_class.v:109 +-V{t#,#} Awaiting time 40: Process waiting at t/t_timing_class.v:145 +-V{t#,#} Awaiting time 50: Process waiting at t/t_timing_class.v:219 +-V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:223 +-V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:228 -V{t#,#} Resuming delayed processes --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:220 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:228 -V{t#,#}+ Vt_timing_debug2_t__03a__03aForkDelayClass::new -V{t#,#}+ Vt_timing_debug2_t__03a__03aForkDelayClass::_ctor_var_reset --V{t#,#} Process forked at t/t_timing_class.v:214 finished --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:215 +-V{t#,#} Process forked at t/t_timing_class.v:222 finished +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:223 -V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass::__VnoInFunc_wake -V{t#,#}+ Vt_timing_debug2___024root___eval_act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act @@ -146,38 +154,38 @@ -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} Ready processes waiting for @([event] t.ec.e): --V{t#,#} - Process waiting at t/t_timing_class.v:80 +-V{t#,#} - Process waiting at t/t_timing_class.v:88 -V{t#,#} Resuming processes waiting for @([event] t.ec.e) --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:80 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:88 -V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass::__VnoInFunc_sleep --V{t#,#} Suspending process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:29 +-V{t#,#} Suspending process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:37 -V{t#,#}+ Vt_timing_debug2___024root___eval_act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:29 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:29 --V{t#,#} Process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:29 awaiting the post update step +-V{t#,#} - Process waiting at t/t_timing_class.v:37 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:37 +-V{t#,#} Process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:37 awaiting the post update step -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} No triggers active -V{t#,#} Doing post updates for processes: --V{t#,#} - Process waiting at t/t_timing_class.v:29 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:29 --V{t#,#} Suspending process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:29 +-V{t#,#} - Process waiting at t/t_timing_class.v:37 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:37 +-V{t#,#} Suspending process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:37 -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___eval_nba -V{t#,#}+ Vt_timing_debug2_t___nba_sequent__TOP__t__0 -V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass::__VnoInFunc_inc_trig_count -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:29 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:29 --V{t#,#} Process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:29 awaiting the post update step +-V{t#,#} - Process waiting at t/t_timing_class.v:37 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:37 +-V{t#,#} Process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:37 awaiting the post update step -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} No triggers active -V{t#,#} Doing post updates for processes: --V{t#,#} - Process waiting at t/t_timing_class.v:29 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:29 --V{t#,#} Suspending process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:29 +-V{t#,#} - Process waiting at t/t_timing_class.v:37 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:37 +-V{t#,#} Suspending process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:37 -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}End-of-eval cleanup -V{t#,#}+++++TOP Evaluate Vt_timing_debug2::eval_step @@ -187,55 +195,55 @@ -V{t#,#}+ Vt_timing_debug2___024root___eval -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:29 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:29 --V{t#,#} Process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:29 awaiting the post update step +-V{t#,#} - Process waiting at t/t_timing_class.v:37 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:37 +-V{t#,#} Process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:37 awaiting the post update step -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 1 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#} Doing post updates for processes: --V{t#,#} - Process waiting at t/t_timing_class.v:29 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:29 --V{t#,#} Suspending process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:29 +-V{t#,#} - Process waiting at t/t_timing_class.v:37 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:37 +-V{t#,#} Suspending process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:37 -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} Delayed processes: --V{t#,#} Awaiting time 30: Process waiting at t/t_timing_class.v:88 --V{t#,#} Awaiting time 30: Process waiting at t/t_timing_class.v:89 --V{t#,#} Awaiting time 50: Process waiting at t/t_timing_class.v:91 --V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:101 --V{t#,#} Awaiting time 40: Process waiting at t/t_timing_class.v:137 --V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:211 +-V{t#,#} Awaiting time 30: Process waiting at t/t_timing_class.v:96 +-V{t#,#} Awaiting time 30: Process waiting at t/t_timing_class.v:97 +-V{t#,#} Awaiting time 50: Process waiting at t/t_timing_class.v:99 +-V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:109 +-V{t#,#} Awaiting time 40: Process waiting at t/t_timing_class.v:145 +-V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:219 -V{t#,#} Resuming delayed processes --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:211 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:219 -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::__VnoInFunc_do_delay --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:139 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:147 -V{t#,#}+ Vt_timing_debug2_t__03a__03aForkDelayClass::__VnoInFunc_do_delay -V{t#,#}+ Vt_timing_debug2___024root___eval_act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:29 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:29 --V{t#,#} Process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:29 awaiting the post update step +-V{t#,#} - Process waiting at t/t_timing_class.v:37 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:37 +-V{t#,#} Process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:37 awaiting the post update step -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} No triggers active -V{t#,#} Doing post updates for processes: --V{t#,#} - Process waiting at t/t_timing_class.v:29 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:29 --V{t#,#} Suspending process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:29 +-V{t#,#} - Process waiting at t/t_timing_class.v:37 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:37 +-V{t#,#} Suspending process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:37 -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___eval_nba -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:29 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:29 --V{t#,#} Process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:29 awaiting the post update step +-V{t#,#} - Process waiting at t/t_timing_class.v:37 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:37 +-V{t#,#} Process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:37 awaiting the post update step -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} No triggers active -V{t#,#} Doing post updates for processes: --V{t#,#} - Process waiting at t/t_timing_class.v:29 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:29 --V{t#,#} Suspending process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:29 +-V{t#,#} - Process waiting at t/t_timing_class.v:37 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:37 +-V{t#,#} Suspending process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:37 -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}End-of-eval cleanup -V{t#,#}+++++TOP Evaluate Vt_timing_debug2::eval_step @@ -245,38 +253,38 @@ -V{t#,#}+ Vt_timing_debug2___024root___eval -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:29 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:29 --V{t#,#} Process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:29 awaiting the post update step +-V{t#,#} - Process waiting at t/t_timing_class.v:37 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:37 +-V{t#,#} Process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:37 awaiting the post update step -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 1 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#} Doing post updates for processes: --V{t#,#} - Process waiting at t/t_timing_class.v:29 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:29 --V{t#,#} Suspending process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:29 +-V{t#,#} - Process waiting at t/t_timing_class.v:37 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:37 +-V{t#,#} Suspending process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:37 -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} Delayed processes: --V{t#,#} Awaiting time 40: Process waiting at t/t_timing_class.v:88 --V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:89 --V{t#,#} Awaiting time 50: Process waiting at t/t_timing_class.v:91 --V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:101 --V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:137 --V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:202 +-V{t#,#} Awaiting time 40: Process waiting at t/t_timing_class.v:96 +-V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:97 +-V{t#,#} Awaiting time 50: Process waiting at t/t_timing_class.v:99 +-V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:109 +-V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:145 +-V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:210 -V{t#,#} Resuming delayed processes --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:202 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:210 -V{t#,#}+ Vt_timing_debug2___024root___eval_act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:29 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:29 --V{t#,#} Process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:29 awaiting the post update step +-V{t#,#} - Process waiting at t/t_timing_class.v:37 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:37 +-V{t#,#} Process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:37 awaiting the post update step -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 0 is active: @([event] t.ec.e) -V{t#,#} Doing post updates for processes: --V{t#,#} - Process waiting at t/t_timing_class.v:29 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:29 --V{t#,#} Process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:29 awaiting resumption +-V{t#,#} - Process waiting at t/t_timing_class.v:37 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:37 +-V{t#,#} Process waiting for @([event] t::EventClass.e) at t/t_timing_class.v:37 awaiting resumption -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} No ready processes waiting for @([event] t.ec.e) @@ -289,17 +297,17 @@ -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} Resuming processes: --V{t#,#} - Process waiting at t/t_timing_class.v:29 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:29 +-V{t#,#} - Process waiting at t/t_timing_class.v:37 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:37 -V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass::__VnoInFunc_inc_trig_count -V{t#,#}+ Vt_timing_debug2_t__03a__03aWaitClass::__VnoInFunc_await --V{t#,#} Suspending process waiting for @([true] ((32'sh4 == t::WaitClass.a) & (32'sh10 < t::WaitClass.b))) at t/t_timing_class.v:50 +-V{t#,#} Suspending process waiting for @([true] ((32'sh4 == t::WaitClass.a) & (32'sh10 < t::WaitClass.b))) at t/t_timing_class.v:58 -V{t#,#}+ Vt_timing_debug2___024root___eval_act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:50 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:50 --V{t#,#} Suspending process waiting for @([true] ((32'sh4 == t::WaitClass.a) & (32'sh10 < t::WaitClass.b))) at t/t_timing_class.v:50 +-V{t#,#} - Process waiting at t/t_timing_class.v:58 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:58 +-V{t#,#} Suspending process waiting for @([true] ((32'sh4 == t::WaitClass.a) & (32'sh10 < t::WaitClass.b))) at t/t_timing_class.v:58 -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} No triggers active -V{t#,#}+ Vt_timing_debug2___024root___timing_commit @@ -308,9 +316,9 @@ -V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass::__VnoInFunc_inc_trig_count -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:50 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:50 --V{t#,#} Suspending process waiting for @([true] ((32'sh4 == t::WaitClass.a) & (32'sh10 < t::WaitClass.b))) at t/t_timing_class.v:50 +-V{t#,#} - Process waiting at t/t_timing_class.v:58 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:58 +-V{t#,#} Suspending process waiting for @([true] ((32'sh4 == t::WaitClass.a) & (32'sh10 < t::WaitClass.b))) at t/t_timing_class.v:58 -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} No triggers active -V{t#,#}+ Vt_timing_debug2___024root___timing_commit @@ -322,36 +330,36 @@ -V{t#,#}+ Vt_timing_debug2___024root___eval -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:50 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:50 --V{t#,#} Suspending process waiting for @([true] ((32'sh4 == t::WaitClass.a) & (32'sh10 < t::WaitClass.b))) at t/t_timing_class.v:50 +-V{t#,#} - Process waiting at t/t_timing_class.v:58 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:58 +-V{t#,#} Suspending process waiting for @([true] ((32'sh4 == t::WaitClass.a) & (32'sh10 < t::WaitClass.b))) at t/t_timing_class.v:58 -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 1 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} Delayed processes: --V{t#,#} Awaiting time 50: Process waiting at t/t_timing_class.v:88 --V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:89 --V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:91 --V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:101 --V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:137 +-V{t#,#} Awaiting time 50: Process waiting at t/t_timing_class.v:96 +-V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:97 +-V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:99 +-V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:109 +-V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:145 -V{t#,#} Resuming delayed processes --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:137 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:145 -V{t#,#}+ Vt_timing_debug2___024root___eval_act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:50 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:50 --V{t#,#} Suspending process waiting for @([true] ((32'sh4 == t::WaitClass.a) & (32'sh10 < t::WaitClass.b))) at t/t_timing_class.v:50 +-V{t#,#} - Process waiting at t/t_timing_class.v:58 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:58 +-V{t#,#} Suspending process waiting for @([true] ((32'sh4 == t::WaitClass.a) & (32'sh10 < t::WaitClass.b))) at t/t_timing_class.v:58 -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} No triggers active -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___eval_nba -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:50 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:50 --V{t#,#} Suspending process waiting for @([true] ((32'sh4 == t::WaitClass.a) & (32'sh10 < t::WaitClass.b))) at t/t_timing_class.v:50 +-V{t#,#} - Process waiting at t/t_timing_class.v:58 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:58 +-V{t#,#} Suspending process waiting for @([true] ((32'sh4 == t::WaitClass.a) & (32'sh10 < t::WaitClass.b))) at t/t_timing_class.v:58 -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} No triggers active -V{t#,#}+ Vt_timing_debug2___024root___timing_commit @@ -363,54 +371,54 @@ -V{t#,#}+ Vt_timing_debug2___024root___eval -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:50 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:50 --V{t#,#} Suspending process waiting for @([true] ((32'sh4 == t::WaitClass.a) & (32'sh10 < t::WaitClass.b))) at t/t_timing_class.v:50 +-V{t#,#} - Process waiting at t/t_timing_class.v:58 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:58 +-V{t#,#} Suspending process waiting for @([true] ((32'sh4 == t::WaitClass.a) & (32'sh10 < t::WaitClass.b))) at t/t_timing_class.v:58 -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 1 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} Delayed processes: --V{t#,#} Awaiting time 60: Process waiting at t/t_timing_class.v:88 --V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:89 --V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:91 --V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:101 --V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:92 +-V{t#,#} Awaiting time 60: Process waiting at t/t_timing_class.v:96 +-V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:97 +-V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:99 +-V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:109 +-V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:100 -V{t#,#} Resuming delayed processes --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:92 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:100 -V{t#,#}+ Vt_timing_debug2___024root___eval_act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:50 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:50 --V{t#,#} Process waiting for @([true] ((32'sh4 == t::WaitClass.a) & (32'sh10 < t::WaitClass.b))) at t/t_timing_class.v:50 awaiting resumption +-V{t#,#} - Process waiting at t/t_timing_class.v:58 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:58 +-V{t#,#} Process waiting for @([true] ((32'sh4 == t::WaitClass.a) & (32'sh10 < t::WaitClass.b))) at t/t_timing_class.v:58 awaiting resumption -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 2 is active: @([true] __VdynSched.evaluate()) -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} Resuming processes: --V{t#,#} - Process waiting at t/t_timing_class.v:50 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:50 +-V{t#,#} - Process waiting at t/t_timing_class.v:58 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:58 -V{t#,#}+ Vt_timing_debug2_t__03a__03aLocalWaitClass::__VnoInFunc_await -V{t#,#}+ Vt_timing_debug2_t__03a__03aLocalWaitClass::__Vfork_h########__0__0 --V{t#,#} Suspending process waiting for @([true] ((32'sh2a == t::LocalWaitClass.a) | (32'sh64 != t::LocalWaitClass.b))) at t/t_timing_class.v:67 +-V{t#,#} Suspending process waiting for @([true] ((32'sh2a == t::LocalWaitClass.a) | (32'sh64 != t::LocalWaitClass.b))) at t/t_timing_class.v:75 -V{t#,#}+ Vt_timing_debug2_t__03a__03aLocalWaitClass::__Vfork_h########__0__1 --V{t#,#} Awaiting join of fork at: t/t_timing_class.v:66 +-V{t#,#} Awaiting join of fork at: t/t_timing_class.v:74 -V{t#,#}+ Vt_timing_debug2___024root___eval_act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:67 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:67 --V{t#,#} Suspending process waiting for @([true] ((32'sh2a == t::LocalWaitClass.a) | (32'sh64 != t::LocalWaitClass.b))) at t/t_timing_class.v:67 +-V{t#,#} - Process waiting at t/t_timing_class.v:75 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:75 +-V{t#,#} Suspending process waiting for @([true] ((32'sh2a == t::LocalWaitClass.a) | (32'sh64 != t::LocalWaitClass.b))) at t/t_timing_class.v:75 -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} No triggers active -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___eval_nba -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:67 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:67 --V{t#,#} Suspending process waiting for @([true] ((32'sh2a == t::LocalWaitClass.a) | (32'sh64 != t::LocalWaitClass.b))) at t/t_timing_class.v:67 +-V{t#,#} - Process waiting at t/t_timing_class.v:75 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:75 +-V{t#,#} Suspending process waiting for @([true] ((32'sh2a == t::LocalWaitClass.a) | (32'sh64 != t::LocalWaitClass.b))) at t/t_timing_class.v:75 -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} No triggers active -V{t#,#}+ Vt_timing_debug2___024root___timing_commit @@ -422,45 +430,45 @@ -V{t#,#}+ Vt_timing_debug2___024root___eval -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:67 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:67 --V{t#,#} Suspending process waiting for @([true] ((32'sh2a == t::LocalWaitClass.a) | (32'sh64 != t::LocalWaitClass.b))) at t/t_timing_class.v:67 +-V{t#,#} - Process waiting at t/t_timing_class.v:75 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:75 +-V{t#,#} Suspending process waiting for @([true] ((32'sh2a == t::LocalWaitClass.a) | (32'sh64 != t::LocalWaitClass.b))) at t/t_timing_class.v:75 -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 1 is active: @([true] __VdlySched.awaitingCurrentTime()) -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} Delayed processes: --V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:88 --V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:89 --V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:91 --V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:101 --V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:68 +-V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:96 +-V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:97 +-V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:99 +-V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:109 +-V{t#,#} Awaiting time 70: Process waiting at t/t_timing_class.v:76 -V{t#,#} Resuming delayed processes --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:68 --V{t#,#} Process forked at t/t_timing_class.v:219 finished +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:76 +-V{t#,#} Process forked at t/t_timing_class.v:227 finished -V{t#,#} Resuming: Process waiting at (null):0 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:101 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:109 -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::__VnoInFunc_do_delay -V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t____Vfork_h########__0__0 -V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::__VnoInFunc_do_assign --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:154 --V{t#,#} Process forked at t/t_timing_class.v:68 finished +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:162 +-V{t#,#} Process forked at t/t_timing_class.v:76 finished -V{t#,#}+ Vt_timing_debug2___024root___eval_act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} Suspended processes waiting for dynamic trigger evaluation: --V{t#,#} - Process waiting at t/t_timing_class.v:67 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:67 --V{t#,#} Process waiting for @([true] ((32'sh2a == t::LocalWaitClass.a) | (32'sh64 != t::LocalWaitClass.b))) at t/t_timing_class.v:67 awaiting resumption +-V{t#,#} - Process waiting at t/t_timing_class.v:75 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:75 +-V{t#,#} Process waiting for @([true] ((32'sh2a == t::LocalWaitClass.a) | (32'sh64 != t::LocalWaitClass.b))) at t/t_timing_class.v:75 awaiting resumption -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 2 is active: @([true] __VdynSched.evaluate()) -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} Resuming processes: --V{t#,#} - Process waiting at t/t_timing_class.v:67 --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:67 --V{t#,#} Process forked at t/t_timing_class.v:67 finished +-V{t#,#} - Process waiting at t/t_timing_class.v:75 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:75 +-V{t#,#} Process forked at t/t_timing_class.v:75 finished -V{t#,#} Resuming: Process waiting at (null):0 -V{t#,#}+ Vt_timing_debug2___024root___eval_act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act @@ -489,12 +497,12 @@ -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} Delayed processes: --V{t#,#} Awaiting time 75: Process waiting at t/t_timing_class.v:88 --V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:89 --V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:91 --V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:188 +-V{t#,#} Awaiting time 75: Process waiting at t/t_timing_class.v:96 +-V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:97 +-V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:99 +-V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:196 -V{t#,#} Resuming delayed processes --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:188 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:196 -V{t#,#}+ Vt_timing_debug2___024root___eval_act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} No suspended processes waiting for dynamic trigger evaluation @@ -520,12 +528,12 @@ -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} Delayed processes: --V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:88 --V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:89 --V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:91 +-V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:96 +-V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:97 +-V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:99 -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#,#} Resuming: Process waiting at t/t_timing_class.v:99 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:97 -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 @@ -553,11 +561,11 @@ -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} Delayed processes: --V{t#,#} Awaiting time 85: Process waiting at t/t_timing_class.v:88 --V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:154 --V{t#,#} Awaiting time 90: Process waiting at t/t_timing_class.v:194 +-V{t#,#} Awaiting time 85: Process waiting at t/t_timing_class.v:96 +-V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:162 +-V{t#,#} Awaiting time 90: Process waiting at t/t_timing_class.v:202 -V{t#,#} Resuming delayed processes --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:194 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:202 -V{t#,#}+ Vt_timing_debug2___024root___eval_act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} No suspended processes waiting for dynamic trigger evaluation @@ -583,11 +591,11 @@ -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} Delayed processes: --V{t#,#} Awaiting time 90: Process waiting at t/t_timing_class.v:88 --V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:154 --V{t#,#} Awaiting time 100: Process waiting at t/t_timing_class.v:195 +-V{t#,#} Awaiting time 90: Process waiting at t/t_timing_class.v:96 +-V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:162 +-V{t#,#} Awaiting time 100: Process waiting at t/t_timing_class.v:203 -V{t#,#} Resuming delayed processes --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:195 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:203 -V{t#,#}+ Vt_timing_debug2___024root___eval_act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act -V{t#,#} No suspended processes waiting for dynamic trigger evaluation @@ -614,10 +622,10 @@ -V{t#,#}+ Vt_timing_debug2___024root___timing_commit -V{t#,#}+ Vt_timing_debug2___024root___timing_resume -V{t#,#} Delayed processes: --V{t#,#} Awaiting time 100: Process waiting at t/t_timing_class.v:88 --V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:154 +-V{t#,#} Awaiting time 100: Process waiting at t/t_timing_class.v:96 +-V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:162 -V{t#,#} Resuming delayed processes --V{t#,#} Resuming: Process waiting at t/t_timing_class.v:154 +-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:162 *-* All Finished *-* -V{t#,#}+ Vt_timing_debug2___024root___eval_act -V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act @@ -642,5 +650,8 @@ -V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::~ -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ -V{t#,#}+ Vt_timing_debug2_t__03a__03aLocalWaitClass::~ +-V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ -V{t#,#}+ Vt_timing_debug2_t__03a__03aWaitClass::~ +-V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ -V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass::~ +-V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ diff --git a/test_regress/t/t_timing_debug2.pl b/test_regress/t/t_timing_debug2.pl index 56ef19876..4ff0b44ea 100755 --- a/test_regress/t/t_timing_debug2.pl +++ b/test_regress/t/t_timing_debug2.pl @@ -10,25 +10,20 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt_all => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_timing_class.v"); +top_filename("t/t_timing_class.v"); - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - all_run_flags => ["+verilator+debug"], - check_finished => 1, - ); +execute( + all_run_flags => ["+verilator+debug"], + check_finished => 1, + ); - if (!$Self->{vltmt}) { # vltmt output may vary between thread exec order - files_identical("$Self->{obj_dir}/vlt_sim.log", $Self->{golden_filename}, "logfile"); - } +if (!$Self->{vltmt}) { # vltmt output may vary between thread exec order + files_identical("$Self->{obj_dir}/vlt_sim.log", $Self->{golden_filename}, "logfile"); } ok(1); diff --git a/test_regress/t/t_timing_delay_callstack.pl b/test_regress/t/t_timing_delay_callstack.pl index f86c4b944..b8493bd06 100755 --- a/test_regress/t/t_timing_delay_callstack.pl +++ b/test_regress/t/t_timing_delay_callstack.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_dlyassign.pl b/test_regress/t/t_timing_dlyassign.pl index f86c4b944..b8493bd06 100755 --- a/test_regress/t/t_timing_dlyassign.pl +++ b/test_regress/t/t_timing_dlyassign.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_dpi.pl b/test_regress/t/t_timing_dpi.pl deleted file mode 100755 index 2c494cced..000000000 --- a/test_regress/t/t_timing_dpi.pl +++ /dev/null @@ -1,28 +0,0 @@ -#!/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.cpp b/test_regress/t/t_timing_dpi_unsup.cpp similarity index 100% rename from test_regress/t/t_timing_dpi.cpp rename to test_regress/t/t_timing_dpi_unsup.cpp diff --git a/test_regress/t/t_timing_dpi_unsup.out b/test_regress/t/t_timing_dpi_unsup.out new file mode 100644 index 000000000..c4cc70ed0 --- /dev/null +++ b/test_regress/t/t_timing_dpi_unsup.out @@ -0,0 +1,5 @@ +%Error-UNSUPPORTED: t/t_timing_dpi_unsup.v:28:19: Unsupported: Timing controls inside DPI-exported tasks + 28 | repeat(n) @(negedge clk); + | ^ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_timing_dpi_unsup.pl b/test_regress/t/t_timing_dpi_unsup.pl new file mode 100755 index 000000000..a6399f3f7 --- /dev/null +++ b/test_regress/t/t_timing_dpi_unsup.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 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(linter => 1); + +top_filename("t/t_timing_dpi_unsup.v"); + +lint( + verilator_flags2 => ["--timing"], + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_timing_dpi.v b/test_regress/t/t_timing_dpi_unsup.v similarity index 79% rename from test_regress/t/t_timing_dpi.v rename to test_regress/t/t_timing_dpi_unsup.v index 63d3bd43d..996ef1e39 100644 --- a/test_regress/t/t_timing_dpi.v +++ b/test_regress/t/t_timing_dpi_unsup.v @@ -24,19 +24,19 @@ module t; export "DPI-C" task tb_sv_wait; task automatic tb_sv_wait(input int n); - `WRITE_VERBOSE("tb_sv_wait start..."); + `WRITE_VERBOSE("tb_sv_wait start...\n"); repeat(n) @(negedge clk); - `WRITE_VERBOSE("tb_sv_wait done!"); + `WRITE_VERBOSE("tb_sv_wait done!\n"); endtask always #halfcycle clk = ~clk; initial begin - `WRITE_VERBOSE("test start"); + `WRITE_VERBOSE("test start\n"); repeat(10) @(posedge clk); - `WRITE_VERBOSE("calling tb_c_wait..."); + `WRITE_VERBOSE("calling tb_c_wait...\n"); tb_c_wait(); - `WRITE_VERBOSE("tb_c_wait finish"); + `WRITE_VERBOSE("tb_c_wait finish\n"); repeat(10) @(posedge clk); $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_timing_events.pl b/test_regress/t/t_timing_events.pl index c469d3de3..002312d88 100755 --- a/test_regress/t/t_timing_events.pl +++ b/test_regress/t/t_timing_events.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_fork_comb.pl b/test_regress/t/t_timing_fork_comb.pl index d96687ccb..ac0e116ba 100755 --- a/test_regress/t/t_timing_fork_comb.pl +++ b/test_regress/t/t_timing_fork_comb.pl @@ -10,27 +10,22 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - # Should convert the first always into combo and detect cycle - compile( - fails => 1, - verilator_flags2 => ["--timing"], - expect => - '%Warning-UNOPTFLAT: t/t_timing_fork_comb.v:\d+:\d+: Signal unoptimizable: Circular combinational logic:' - ); +# Should convert the first always into combo and detect cycle +compile( + fails => 1, + verilator_flags2 => ["--timing"], + expect => + '%Warning-UNOPTFLAT: t/t_timing_fork_comb.v:\d+:\d+: Signal unoptimizable: Circular combinational logic:' + ); - compile( - verilator_flags2 => ["--exe --main --timing -Wno-UNOPTFLAT"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing -Wno-UNOPTFLAT"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_fork_join.pl b/test_regress/t/t_timing_fork_join.pl index 4ab3c9652..3feb337e3 100755 --- a/test_regress/t/t_timing_fork_join.pl +++ b/test_regress/t/t_timing_fork_join.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,20 +10,15 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - expect_filename => $Self->{golden_filename}, - ); -} +execute( + check_finished => 1, + expect_filename => $Self->{golden_filename}, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_fork_many.pl b/test_regress/t/t_timing_fork_many.pl index f86c4b944..b8493bd06 100755 --- a/test_regress/t/t_timing_fork_many.pl +++ b/test_regress/t/t_timing_fork_many.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_fork_nba.pl b/test_regress/t/t_timing_fork_nba.pl index 372246cb3..e23c975f9 100755 --- a/test_regress/t/t_timing_fork_nba.pl +++ b/test_regress/t/t_timing_fork_nba.pl @@ -10,14 +10,9 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --timing"], - ); -} +compile( + verilator_flags2 => ["--exe --timing"], + ); ok(1); 1; diff --git a/test_regress/t/t_timing_fork_no_timing_ctrl.pl b/test_regress/t/t_timing_fork_no_timing_ctrl.pl new file mode 100755 index 000000000..b8493bd06 --- /dev/null +++ b/test_regress/t/t_timing_fork_no_timing_ctrl.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); + +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_no_timing_ctrl.v b/test_regress/t/t_timing_fork_no_timing_ctrl.v new file mode 100644 index 000000000..2f9bb387c --- /dev/null +++ b/test_regress/t/t_timing_fork_no_timing_ctrl.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 + +module t; + initial + fork + begin + $write("*-* All Finished *-*\n"); + $finish; + end + join_none +endmodule diff --git a/test_regress/t/t_timing_fork_taskcall.pl b/test_regress/t/t_timing_fork_taskcall.pl index 439181d0a..b8493bd06 100755 --- a/test_regress/t/t_timing_fork_taskcall.pl +++ b/test_regress/t/t_timing_fork_taskcall.pl @@ -2,7 +2,7 @@ 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 +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_intra_assign.pl b/test_regress/t/t_timing_intra_assign.pl index 6852f73f7..fabe5ebc4 100755 --- a/test_regress/t/t_timing_intra_assign.pl +++ b/test_regress/t/t_timing_intra_assign.pl @@ -10,31 +10,25 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing -Wno-UNOPTFLAT"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing -Wno-UNOPTFLAT"], + make_main => 0, + ); - execute( - check_finished => 1, - expect_filename => $Self->{golden_filename}, - ); +execute( + check_finished => 1, + expect_filename => $Self->{golden_filename}, + ); - compile( - verilator_flags2 => ["--exe --main --timing -Wno-UNOPTFLAT -fno-localize"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing -Wno-UNOPTFLAT -fno-localize"], + make_main => 0, + ); - execute( - check_finished => 1, - expect_filename => $Self->{golden_filename}, - ); - -} +execute( + check_finished => 1, + expect_filename => $Self->{golden_filename}, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_long.pl b/test_regress/t/t_timing_long.pl index 3efef3dff..834a8c4f5 100755 --- a/test_regress/t/t_timing_long.pl +++ b/test_regress/t/t_timing_long.pl @@ -56,7 +56,7 @@ gen($Self->{top_filename}); if ($Self->have_coroutines) { compile( - verilator_flags2 => ["--exe --build --main --timing"], + verilator_flags2 => ["--exe --build --main --tim" . "ing"], verilator_make_cmake => 0, verilator_make_gmake => 0, make_main => 0, diff --git a/test_regress/t/t_timing_nba.pl b/test_regress/t/t_timing_nba.pl index f86c4b944..b8493bd06 100755 --- a/test_regress/t/t_timing_nba.pl +++ b/test_regress/t/t_timing_nba.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_off.pl b/test_regress/t/t_timing_off.pl index f86c4b944..b8493bd06 100755 --- a/test_regress/t/t_timing_off.pl +++ b/test_regress/t/t_timing_off.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_pong.pl b/test_regress/t/t_timing_pong.pl index f86c4b944..b8493bd06 100755 --- a/test_regress/t/t_timing_pong.pl +++ b/test_regress/t/t_timing_pong.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_reentry.pl b/test_regress/t/t_timing_reentry.pl index f86c4b944..b8493bd06 100755 --- a/test_regress/t/t_timing_reentry.pl +++ b/test_regress/t/t_timing_reentry.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_sched.pl b/test_regress/t/t_timing_sched.pl index f86c4b944..b8493bd06 100755 --- a/test_regress/t/t_timing_sched.pl +++ b/test_regress/t/t_timing_sched.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_sched_if.pl b/test_regress/t/t_timing_sched_if.pl index f86c4b944..b8493bd06 100755 --- a/test_regress/t/t_timing_sched_if.pl +++ b/test_regress/t/t_timing_sched_if.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_sched_nba.pl b/test_regress/t/t_timing_sched_nba.pl index f86c4b944..b8493bd06 100755 --- a/test_regress/t/t_timing_sched_nba.pl +++ b/test_regress/t/t_timing_sched_nba.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,19 +10,14 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_strobe.pl b/test_regress/t/t_timing_strobe.pl index 4ab3c9652..3feb337e3 100755 --- a/test_regress/t/t_timing_strobe.pl +++ b/test_regress/t/t_timing_strobe.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,20 +10,15 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); - execute( - check_finished => 1, - expect_filename => $Self->{golden_filename}, - ); -} +execute( + check_finished => 1, + expect_filename => $Self->{golden_filename}, + ); ok(1); 1; diff --git a/test_regress/t/t_timing_trace.pl b/test_regress/t/t_timing_trace.pl index 6a98bee9d..73c643cee 100755 --- a/test_regress/t/t_timing_trace.pl +++ b/test_regress/t/t_timing_trace.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -10,21 +10,16 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing --trace -Wno-MINTYPMAXDLY"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing --trace -Wno-MINTYPMAXDLY"], + make_main => 0, + ); - execute( - check_finished => 1, - ); +execute( + check_finished => 1, + ); - vcd_identical($Self->trace_filename, $Self->{golden_filename}); -} +vcd_identical($Self->trace_filename, $Self->{golden_filename}); ok(1); 1; diff --git a/test_regress/t/t_timing_trace_fst.pl b/test_regress/t/t_timing_trace_fst.pl index 0ac004740..261ee5e84 100755 --- a/test_regress/t/t_timing_trace_fst.pl +++ b/test_regress/t/t_timing_trace_fst.pl @@ -12,21 +12,16 @@ scenarios(simulator => 1); top_filename("t/t_timing_trace.v"); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing --trace-fst -Wno-MINTYPMAXDLY"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing --trace-fst -Wno-MINTYPMAXDLY"], + make_main => 0, + ); - execute( - check_finished => 1, - ); +execute( + check_finished => 1, + ); - fst_identical($Self->trace_filename, $Self->{golden_filename}); -} +fst_identical($Self->trace_filename, $Self->{golden_filename}); ok(1); 1; diff --git a/test_regress/t/t_timing_wait1.pl b/test_regress/t/t_timing_wait1.pl new file mode 100755 index 000000000..3508f0a14 --- /dev/null +++ b/test_regress/t/t_timing_wait1.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); + +compile( + verilator_flags2 => ["--exe --main --timing -Wno-WAITCONST"], + make_main => 0, + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_timing_wait.v b/test_regress/t/t_timing_wait1.v similarity index 100% rename from test_regress/t/t_timing_wait.v rename to test_regress/t/t_timing_wait1.v diff --git a/test_regress/t/t_timing_wait2.out b/test_regress/t/t_timing_wait2.out new file mode 100644 index 000000000..3916a7dbf --- /dev/null +++ b/test_regress/t/t_timing_wait2.out @@ -0,0 +1,4 @@ +2 +1 +0 +*-* All Finished *-* diff --git a/test_regress/t/t_timing_wait2.pl b/test_regress/t/t_timing_wait2.pl new file mode 100755 index 000000000..3feb337e3 --- /dev/null +++ b/test_regress/t/t_timing_wait2.pl @@ -0,0 +1,24 @@ +#!/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( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); + +execute( + check_finished => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_timing_wait2.v b/test_regress/t/t_timing_wait2.v new file mode 100644 index 000000000..0bb5ec6b5 --- /dev/null +++ b/test_regress/t/t_timing_wait2.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, 2023 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module t; + bit s[3:0] = {0, 0, 0, 0}; + + initial begin + wait (s[1]); + s[0] = 1; + $display("0"); + end + + initial begin + wait (s[2]); + s[1] = 1; + $display("1"); + #1 $write("*-* All Finished *-*\n"); + $finish; + end + + initial begin + wait (s[3]); + s[2] = 1; + $display("2"); + end + + initial begin + s[3] = 1; + end + + initial #2 $stop; // timeout +endmodule diff --git a/test_regress/t/t_timing_wait_long.pl b/test_regress/t/t_timing_wait_long.pl index 549b8ba35..3feb337e3 100755 --- a/test_regress/t/t_timing_wait_long.pl +++ b/test_regress/t/t_timing_wait_long.pl @@ -2,7 +2,7 @@ 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 +# 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. @@ -10,19 +10,15 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing"], + make_main => 0, + ); + +execute( + check_finished => 1, + expect_filename => $Self->{golden_filename}, + ); - execute( - check_finished => 1, - expect_filename => $Self->{golden_filename}, - ); -} ok(1); 1; diff --git a/test_regress/t/t_trace_array.out b/test_regress/t/t_trace_array.out index 4791912ee..15d934b35 100644 --- a/test_regress/t/t_trace_array.out +++ b/test_regress/t/t_trace_array.out @@ -7,7 +7,7 @@ $timescale 1ps $end $scope module t $end $var wire 1 ]b# clk $end $var wire 32 # cyc [31:0] $end - $scope struct biggie $end + $scope module biggie $end $var wire 1048577 $ d [1048576:0] $end $upscope $end $upscope $end diff --git a/test_regress/t/t_trace_binary.pl b/test_regress/t/t_trace_binary.pl index 03fd8a3c5..8304613d5 100755 --- a/test_regress/t/t_trace_binary.pl +++ b/test_regress/t/t_trace_binary.pl @@ -10,26 +10,21 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags => [# Custom as don't want -cc - "-Mdir $Self->{obj_dir}", - "--debug-check", ], - verilator_flags2 => ['--binary --trace'], - verilator_make_cmake => 0, - verilator_make_gmake => 0, - make_main => 0, - ); +compile( + verilator_flags => [# Custom as don't want -cc + "-Mdir $Self->{obj_dir}", + "--debug-check", ], + verilator_flags2 => ['--binary --trace'], + verilator_make_cmake => 0, + verilator_make_gmake => 0, + make_main => 0, + ); - execute( - check_finished => 1, - ); +execute( + check_finished => 1, + ); - vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); -} +vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); ok(1); 1; diff --git a/test_regress/t/t_trace_binary_flag_off.pl b/test_regress/t/t_trace_binary_flag_off.pl index eaa6a3518..6de81c2ca 100755 --- a/test_regress/t/t_trace_binary_flag_off.pl +++ b/test_regress/t/t_trace_binary_flag_off.pl @@ -12,24 +12,19 @@ scenarios(vlt => 1); top_filename("t/t_trace_binary.v"); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags => [# Custom as don't want -cc - "-Mdir $Self->{obj_dir}", - "--debug-check", ], - verilator_flags2 => ['--binary'], - verilator_make_cmake => 0, - verilator_make_gmake => 0, - make_main => 0, - ); +compile( + verilator_flags => [# Custom as don't want -cc + "-Mdir $Self->{obj_dir}", + "--debug-check", ], + verilator_flags2 => ['--binary'], + verilator_make_cmake => 0, + verilator_make_gmake => 0, + make_main => 0, + ); - execute( - expect_filename => $Self->{golden_filename}, - ); -} +execute( + expect_filename => $Self->{golden_filename}, + ); ok(1); 1; diff --git a/test_regress/t/t_trace_complex_structs.out b/test_regress/t/t_trace_complex_structs.out index c421a6293..8b5d5f780 100644 --- a/test_regress/t/t_trace_complex_structs.out +++ b/test_regress/t/t_trace_complex_structs.out @@ -37,52 +37,52 @@ $timescale 1ps $end $var wire 32 H a [31:0] $end $upscope $end $upscope $end - $scope struct v_arrp_strp[3] $end + $scope module v_arrp_strp[3] $end $var wire 1 1 b0 $end $var wire 1 0 b1 $end $upscope $end - $scope struct v_arrp_strp[4] $end + $scope module v_arrp_strp[4] $end $var wire 1 3 b0 $end $var wire 1 2 b1 $end $upscope $end - $scope struct v_arru_strp[3] $end + $scope module v_arru_strp[3] $end $var wire 1 7 b0 $end $var wire 1 6 b1 $end $upscope $end - $scope struct v_arru_strp[4] $end + $scope module v_arru_strp[4] $end $var wire 1 9 b0 $end $var wire 1 8 b1 $end $upscope $end - $scope struct v_enumb2_str $end + $scope module v_enumb2_str $end $var wire 3 E a [2:0] $end $var wire 3 F b [2:0] $end $upscope $end - $scope struct v_str32x2[0] $end + $scope module v_str32x2[0] $end $var wire 32 @ data [31:0] $end $upscope $end - $scope struct v_str32x2[1] $end + $scope module v_str32x2[1] $end $var wire 32 A data [31:0] $end $upscope $end - $scope struct v_strp_strp $end - $scope struct x0 $end + $scope module v_strp_strp $end + $scope module x0 $end $var wire 1 * b0 $end $var wire 1 ) b1 $end $upscope $end - $scope struct x1 $end + $scope module x1 $end $var wire 1 ( b0 $end $var wire 1 ' b1 $end $upscope $end $upscope $end - $scope struct v_strp $end + $scope module v_strp $end $var wire 1 & b0 $end $var wire 1 % b1 $end $upscope $end - $scope union v_unip_strp $end - $scope struct x0 $end + $scope module v_unip_strp $end + $scope module x0 $end $var wire 1 , b0 $end $var wire 1 + b1 $end $upscope $end - $scope struct x1 $end + $scope module x1 $end $var wire 1 , b0 $end $var wire 1 + b1 $end $upscope $end diff --git a/test_regress/t/t_trace_timing1.pl b/test_regress/t/t_trace_timing1.pl index 2ae74250a..a0528ef80 100755 --- a/test_regress/t/t_trace_timing1.pl +++ b/test_regress/t/t_trace_timing1.pl @@ -10,24 +10,19 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags => [# Custom as don't want -cc - "-Mdir $Self->{obj_dir}", - "--debug-check", ], - verilator_flags2 => ['--binary --trace'], - verilator_make_cmake => 0, - verilator_make_gmake => 0, - make_main => 0, - ); +compile( + verilator_flags => [# Custom as don't want -cc + "-Mdir $Self->{obj_dir}", + "--debug-check", ], + verilator_flags2 => ['--binary --trace'], + verilator_make_cmake => 0, + verilator_make_gmake => 0, + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); diff --git a/test_regress/t/t_trace_ub_misaligned_address.out b/test_regress/t/t_trace_ub_misaligned_address.out new file mode 100644 index 000000000..1fe9f3feb --- /dev/null +++ b/test_regress/t/t_trace_ub_misaligned_address.out @@ -0,0 +1 @@ +*-* All Finished *-* diff --git a/test_regress/t/t_trace_ub_misaligned_address.pl b/test_regress/t/t_trace_ub_misaligned_address.pl new file mode 100755 index 000000000..83dcf0fb4 --- /dev/null +++ b/test_regress/t/t_trace_ub_misaligned_address.pl @@ -0,0 +1,33 @@ +#!/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); + +top_filename("t/t_trace_ub_misaligned_address.v"); + +compile( + verilator_flags2 => ["--binary --trace", + "-CFLAGS -fsanitize=address,undefined", + "-LDFLAGS -fsanitize=address,undefined"], + verilator_make_cmake => 0, + verilator_make_gmake => 0, + make_main => 0, + ); + +execute( + check_finished => 1, + ); + +# Make sure that there are no additional messages (such as runtime messages +# regarding undefined behavior). +files_identical("$Self->{obj_dir}/vlt_sim.log", $Self->{golden_filename}, "logfile"); + +ok(1); +1; diff --git a/test_regress/t/t_trace_ub_misaligned_address.v b/test_regress/t/t_trace_ub_misaligned_address.v new file mode 100644 index 000000000..379412d3a --- /dev/null +++ b/test_regress/t/t_trace_ub_misaligned_address.v @@ -0,0 +1,92 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// When compiled using -fsanitize=address,undefined this triggered: +// +// verilated_trace_imp.h:875:5: runtime error: store to misaligned address ... +// verilated_trace.h:450:31: runtime error: load of misaligned address ... +// +// due to 32 bit aligned addresses being used for types which require +// stricter alignment. +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by John Wehle. +// SPDX-License-Identifier: CC0-1.0 + +`define STRINGIFY(x) `"x`" + +module t; + + wire [2:0] out; + reg in; + reg [39:0] p; + reg rst; + reg clk; + + initial begin + $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); + $dumpvars(0, test); + + clk = 0; + rst = 0; + + for (int i = 0; i < 2; i++) + begin + #10 rst = 1; + #10 rst = 0; + + p = 40'b0000000000111111111111111111110000000000; + + in = i[0]; + + for (int k = 0; k < 31; k++) + begin + in = p[39 - k] ^ i[0]; + #1; + end + + end + + #30 $write("*-* All Finished *-*\n"); + $finish; + end + + always begin + #10 clk <= !clk; + end + + Test test(.out(out), .in(in), + .clk(clk), .rst(rst)); +endmodule + + +module Test(/*AUTOARG*/ + // Outputs + out, + // Inputs + clk, in, rst + ); + + input clk; + input in; + input rst; + output wire [2:0] out; + + reg [2:0] s; + reg sin; + + assign out = s; + + always @(posedge clk, posedge rst) + begin + s[0] <= s[2]; + s[2] <= in; + s[1] <= sin; + end + + always @(negedge clk, posedge rst) + if (rst) + sin <= 1'b0; + else + sin <= in; + +endmodule diff --git a/test_regress/t/t_trace_wide_struct.pl b/test_regress/t/t_trace_wide_struct.pl new file mode 100755 index 000000000..7914ddcc8 --- /dev/null +++ b/test_regress/t/t_trace_wide_struct.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 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 => ['--trace --trace-structs'],); + +ok(1); +1; diff --git a/test_regress/t/t_trace_wide_struct.v b/test_regress/t/t_trace_wide_struct.v new file mode 100644 index 000000000..4a381de5a --- /dev/null +++ b/test_regress/t/t_trace_wide_struct.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, 2011 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + typedef struct { + logic [64:0] long_signal; + } mystruct_t; + + mystruct_t mystruct; + + initial begin + $finish; + end + +endmodule diff --git a/test_regress/t/t_unpacked_struct_eq.pl b/test_regress/t/t_unpacked_struct_eq.pl new file mode 100755 index 000000000..a17622844 --- /dev/null +++ b/test_regress/t/t_unpacked_struct_eq.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 2004 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_unpacked_struct_eq.v b/test_regress/t/t_unpacked_struct_eq.v new file mode 100644 index 000000000..384f10801 --- /dev/null +++ b/test_regress/t/t_unpacked_struct_eq.v @@ -0,0 +1,67 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Use this file as a template for submitting bugs, etc. +// This module takes a single clock input, and should either +// $write("*-* All Finished *-*\n"); +// $finish; +// on success, or $stop. +// +// The code as shown applies a random vector to the Test +// module, then calculates a CRC on the Test module's outputs. +// +// **If you do not wish for your code to be released to the public +// please note it here, otherwise:** +// +// 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; + typedef struct{ + logic [31:0] subarr[4]; + } arr_str_t; + typedef struct { + string txt; + struct { + logic m0; + logic [3:0] m1; + logic [7:0] arr[2][3]; + arr_str_t str[5]; + } sub; + } struct_t; + struct_t s1; + struct_t s2; + struct_t s3; + + assign {s1.sub.m0, s1.sub.m1} = {1'b0, 4'h5}; + assign {s2.sub.m0, s2.sub.m1} = {1'b0, 4'h5}; + assign s1.txt = "text"; + assign s2.txt = "text"; + + assign {s1.sub.arr[0][0], s2.sub.arr[0][0]} = {8'h01, 8'h01}; + assign {s1.sub.arr[0][1], s2.sub.arr[0][1]} = {8'h02, 8'h02}; + assign {s1.sub.arr[0][2], s2.sub.arr[0][2]} = {8'h03, 8'h03}; + assign {s1.sub.arr[1][0], s2.sub.arr[1][0]} = {8'h04, 8'h04}; + assign {s1.sub.arr[1][1], s2.sub.arr[1][1]} = {8'h05, 8'h05}; + assign {s1.sub.arr[1][2], s2.sub.arr[1][2]} = {8'h06, 8'h06}; + + assign {s3.sub.m0, s3.sub.m1} = {1'b0, 4'h5}; + assign s3.txt = "text"; + + assign s3.sub.arr[0][0] = 8'h01; + assign s3.sub.arr[0][1] = 8'h02; + assign s3.sub.arr[0][2] = 8'h03; + assign s3.sub.arr[1][0] = 8'h24; // One mismatch + assign s3.sub.arr[1][1] = 8'h05; + assign s3.sub.arr[1][2] = 8'h06; + + initial begin + if(s3 == s1) $stop; + if(s1 == s2 && s3 != s1) begin + $write("*-* All Finished *-*\n"); + $finish; + end else begin + $fatal; + end + end +endmodule diff --git a/test_regress/t/t_unpacked_struct_redef.pl b/test_regress/t/t_unpacked_struct_redef.pl new file mode 100755 index 000000000..859050d63 --- /dev/null +++ b/test_regress/t/t_unpacked_struct_redef.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_unpacked_struct_redef.v b/test_regress/t/t_unpacked_struct_redef.v new file mode 100644 index 000000000..931177607 --- /dev/null +++ b/test_regress/t/t_unpacked_struct_redef.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 Class#(parameter WIDTH); + typedef logic [WIDTH-1:0] word; + typedef struct { + word w; + } Struct; +endclass + +module t; + Class#(1)::Struct s1; + Class#(1)::Struct s2; + Class#(2)::Struct s3; + + initial begin + $display("%p", s1); + $display("%p", s2); + $display("%p", s3); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_var_dup2_bad.out b/test_regress/t/t_var_dup2_bad.out index 347ba1095..79d73749b 100644 --- a/test_regress/t/t_var_dup2_bad.out +++ b/test_regress/t/t_var_dup2_bad.out @@ -11,4 +11,16 @@ t/t_var_dup2_bad.v:11:11: ... Location of original declaration 11 | output bad_o_r); | ^~~~~~~ +%Error: t/t_var_dup2_bad.v:17:9: Duplicate declaration of signal: 'bad_w_r' + 17 | reg bad_w_r; + | ^~~~~~~ + t/t_var_dup2_bad.v:16:9: ... Location of original declaration + 16 | wire bad_w_r; + | ^~~~~~~ +%Error: t/t_var_dup2_bad.v:20:9: Duplicate declaration of signal: 'bad_r_w' + 20 | reg bad_r_w; + | ^~~~~~~ + t/t_var_dup2_bad.v:19:9: ... Location of original declaration + 19 | wire bad_r_w; + | ^~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_var_dup2_bad.v b/test_regress/t/t_var_dup2_bad.v index 21eb3cb4b..3d762be23 100644 --- a/test_regress/t/t_var_dup2_bad.v +++ b/test_regress/t/t_var_dup2_bad.v @@ -12,4 +12,11 @@ module t wire bad_o_w; reg bad_o_r; + + wire bad_w_r; + reg bad_w_r; + + wire bad_r_w; + reg bad_r_w; + endmodule diff --git a/test_regress/t/t_var_dup_bad.out b/test_regress/t/t_var_dup_bad.out index 4f86e876a..b029c5809 100644 --- a/test_regress/t/t_var_dup_bad.out +++ b/test_regress/t/t_var_dup_bad.out @@ -47,7 +47,6 @@ 64 | output bad_reout_port | ^~~~~~~~~~~~~~ %Error: t/t_var_dup_bad.v:73:9: Duplicate declaration of signal: 'bad_rewire' - : ... note: ANSI ports must have type declared with the I/O (IEEE 1800-2017 23.2.2.2) 73 | wire bad_rewire; | ^~~~~~~~~~ t/t_var_dup_bad.v:70:16: ... Location of original declaration diff --git a/test_regress/t/t_var_static.pl b/test_regress/t/t_var_static.pl index 93a3a1be3..efb3d2dfc 100755 --- a/test_regress/t/t_var_static.pl +++ b/test_regress/t/t_var_static.pl @@ -16,6 +16,7 @@ compile( execute( check_finished => 1, + all_run_flags => ['+plusarg=value'], ); ok(1); diff --git a/test_regress/t/t_var_static.v b/test_regress/t/t_var_static.v index cb54f6a0c..7eec192a5 100644 --- a/test_regress/t/t_var_static.v +++ b/test_regress/t/t_var_static.v @@ -52,10 +52,17 @@ module t (/*AUTOARG*/ function automatic int f_au_au (); automatic int au = 2; au++; return au; endfunction + string plusarg = ""; + bit has_plusarg = |($value$plusargs("plusarg=%s", plusarg)); int v; initial begin + if (has_plusarg) begin + if (plusarg == "") begin + $fatal(1, "%m: +plusarg must not be empty"); + end + end v = f_no_no(); `checkh(v, 3); v = f_no_no(); `checkh(v, 4); v = f_no_st(); `checkh(v, 3); diff --git a/test_regress/t/t_vlt_timing.pl b/test_regress/t/t_vlt_timing.pl index 0b172f726..d816239d3 100755 --- a/test_regress/t/t_vlt_timing.pl +++ b/test_regress/t/t_vlt_timing.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2022 by Antmicro Ltd. This program is free software; you +# 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. @@ -12,19 +12,14 @@ scenarios(simulator => 1); top_filename("t/t_timing_off.v"); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - verilator_flags2 => ["--exe --main --timing t/t_vlt_timing.vlt"], - make_main => 0, - ); +compile( + verilator_flags2 => ["--exe --main --timing t/t_vlt_timing.vlt"], + make_main => 0, + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_wait_timing.pl b/test_regress/t/t_wait_timing.pl index 8c852569f..41fd8a2dd 100755 --- a/test_regress/t/t_wait_timing.pl +++ b/test_regress/t/t_wait_timing.pl @@ -10,21 +10,16 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - top_filename("t/t_wait.v"); +top_filename("t/t_wait.v"); - compile( - timing_loop => 1, - verilator_flags2 => ["--timing -Wno-WAITCONST"], - ); +compile( + timing_loop => 1, + verilator_flags2 => ["--timing -Wno-WAITCONST"], + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_while_timing_control.pl b/test_regress/t/t_while_timing_control.pl index 5f740b172..b6091c571 100755 --- a/test_regress/t/t_while_timing_control.pl +++ b/test_regress/t/t_while_timing_control.pl @@ -12,19 +12,14 @@ scenarios(simulator => 1); $Self->{main_time_multiplier} = 10e-7 / 10e-9; -if (!$Self->have_coroutines) { - skip("No coroutine support"); -} -else { - compile( - timing_loop => 1, - verilator_flags2 => ['--timing -Wno-ZERODLY'], - ); +compile( + timing_loop => 1, + verilator_flags2 => ['--timing -Wno-ZERODLY'], + ); - execute( - check_finished => 1, - ); -} +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_wire_beh1364_bad.out b/test_regress/t/t_wire_beh1364_bad.out index e9ff7ee3d..4667a07f9 100644 --- a/test_regress/t/t_wire_beh1364_bad.out +++ b/test_regress/t/t_wire_beh1364_bad.out @@ -1,22 +1,22 @@ -%Error-PROCASSWIRE: t/t_wire_beh1364_bad.v:25:7: Procedural assignment to wire, perhaps intended var (IEEE 1800-2017 6.5): 'w' +%Error-PROCASSWIRE: t/t_wire_beh1364_bad.v:26:7: Procedural assignment to wire, perhaps intended var (IEEE 1800-2017 6.5): 'w' : ... In instance t - 25 | w = '0; + 26 | w = 0; | ^ ... For error description see https://verilator.org/warn/PROCASSWIRE?v=latest -%Error-PROCASSWIRE: t/t_wire_beh1364_bad.v:26:7: Procedural assignment to wire, perhaps intended var (IEEE 1800-2017 6.5): 'o' +%Error-PROCASSWIRE: t/t_wire_beh1364_bad.v:27:7: Procedural assignment to wire, perhaps intended var (IEEE 1800-2017 6.5): 'o' : ... In instance t - 26 | o = '0; + 27 | o = 0; | ^ -%Error-PROCASSWIRE: t/t_wire_beh1364_bad.v:27:7: Procedural assignment to wire, perhaps intended var (IEEE 1800-2017 6.5): 'oa' +%Error-PROCASSWIRE: t/t_wire_beh1364_bad.v:28:7: Procedural assignment to wire, perhaps intended var (IEEE 1800-2017 6.5): 'oa' : ... In instance t - 27 | oa = '0; + 28 | oa = 0; | ^~ -%Error-PROCASSWIRE: t/t_wire_beh1364_bad.v:28:7: Procedural assignment to wire, perhaps intended var (IEEE 1800-2017 6.5): 'wo' +%Error-PROCASSWIRE: t/t_wire_beh1364_bad.v:29:7: Procedural assignment to wire, perhaps intended var (IEEE 1800-2017 6.5): 'wo' : ... In instance t - 28 | wo = '0; + 29 | wo = 0; | ^~ -%Error-PROCASSWIRE: t/t_wire_beh1364_bad.v:29:7: Procedural assignment to wire, perhaps intended var (IEEE 1800-2017 6.5): 'woa' +%Error-PROCASSWIRE: t/t_wire_beh1364_bad.v:30:7: Procedural assignment to wire, perhaps intended var (IEEE 1800-2017 6.5): 'woa' : ... In instance t - 29 | woa = '0; + 30 | woa = 0; | ^~~ %Error: Exiting due to diff --git a/test_regress/t/t_wire_beh1364_bad.v b/test_regress/t/t_wire_beh1364_bad.v index e8ca5744c..d983139f8 100644 --- a/test_regress/t/t_wire_beh1364_bad.v +++ b/test_regress/t/t_wire_beh1364_bad.v @@ -22,16 +22,18 @@ module t (/*AUTOARG*/ //output var [1:0] voa; initial begin - w = '0; // Error - o = '0; // Error - oa = '0; // Error - wo = '0; // Error - woa = '0; // Error - r = '0; // Not an error - ro = '0; // Not an error - roa = '0; // Not an error - //vo = '0; // Not an error - //voa = '0; // Not an error + // Error + w = 0; + o = 0; + oa = 0; + wo = 0; + woa = 0; + // Not an error + r = 0; + ro = 0; + roa = 0; + //vo = 0; + //voa = 0; end endmodule diff --git a/test_regress/t/t_wire_behp1364_bad.out b/test_regress/t/t_wire_behp1364_bad.out index 68b6db330..9fbbf0f6c 100644 --- a/test_regress/t/t_wire_behp1364_bad.out +++ b/test_regress/t/t_wire_behp1364_bad.out @@ -1,14 +1,14 @@ -%Error-PROCASSWIRE: t/t_wire_behp1364_bad.v:23:7: Procedural assignment to wire, perhaps intended var (IEEE 1800-2017 6.5): 'w' +%Error-PROCASSWIRE: t/t_wire_behp1364_bad.v:24:7: Procedural assignment to wire, perhaps intended var (IEEE 1800-2017 6.5): 'w' : ... In instance t - 23 | w = '0; + 24 | w = 0; | ^ ... For error description see https://verilator.org/warn/PROCASSWIRE?v=latest -%Error-PROCASSWIRE: t/t_wire_behp1364_bad.v:24:7: Procedural assignment to wire, perhaps intended var (IEEE 1800-2017 6.5): 'o' +%Error-PROCASSWIRE: t/t_wire_behp1364_bad.v:25:7: Procedural assignment to wire, perhaps intended var (IEEE 1800-2017 6.5): 'o' : ... In instance t - 24 | o = '0; + 25 | o = 0; | ^ -%Error-PROCASSWIRE: t/t_wire_behp1364_bad.v:25:7: Procedural assignment to wire, perhaps intended var (IEEE 1800-2017 6.5): 'oa' +%Error-PROCASSWIRE: t/t_wire_behp1364_bad.v:26:7: Procedural assignment to wire, perhaps intended var (IEEE 1800-2017 6.5): 'oa' : ... In instance t - 25 | oa = '0; + 26 | oa = 0; | ^~ %Error: Exiting due to diff --git a/test_regress/t/t_wire_behp1364_bad.v b/test_regress/t/t_wire_behp1364_bad.v index 42a67c340..e88ea744d 100644 --- a/test_regress/t/t_wire_behp1364_bad.v +++ b/test_regress/t/t_wire_behp1364_bad.v @@ -20,16 +20,18 @@ module t ( reg r; initial begin - w = '0; // Error - o = '0; // Error - oa = '0; // Error - wo = '0; // Error - woa = '0; // Error - r = '0; // Not an error - ro = '0; // Not an error - roa = '0; // Not an error - //vo = '0; // Not an error - //voa = '0; // Not an error + // Error + w = 0; + o = 0; + oa = 0; + wo = 0; + woa = 0; + // Not an error + r = 0; + ro = 0; + roa = 0; + //vo = 0; + //voa = 0; end endmodule