diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b069791d2..048e84fa1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,6 +1,6 @@ --- name: Bug report -about: Something isn't working as expected, and it isn't "Unsupported." (Note our contributor agreement at https://github.com/verilator/verilator/blob/master/docs/CONTRIBUTING.adoc) +about: Something isn't working as expected, and it isn't "Unsupported." (Note our contributor agreement at https://github.com/verilator/verilator/blob/master/docs/CONTRIBUTING.rst) title: '' labels: new assignees: '' diff --git a/.github/ISSUE_TEMPLATE/feature.md b/.github/ISSUE_TEMPLATE/feature.md index 1514b1287..3cc485eda 100644 --- a/.github/ISSUE_TEMPLATE/feature.md +++ b/.github/ISSUE_TEMPLATE/feature.md @@ -1,6 +1,6 @@ --- name: Feature Request -about: Request something should be supported, or a new feature added. (Note our contributor agreement at https://github.com/verilator/verilator/blob/master/docs/CONTRIBUTING.adoc) +about: Request something should be supported, or a new feature added. (Note our contributor agreement at https://github.com/verilator/verilator/blob/master/docs/CONTRIBUTING.rst) title: '' labels: new assignees: '' diff --git a/.github/ISSUE_TEMPLATE/questions.md b/.github/ISSUE_TEMPLATE/questions.md index 1d746589f..0571931ca 100644 --- a/.github/ISSUE_TEMPLATE/questions.md +++ b/.github/ISSUE_TEMPLATE/questions.md @@ -1,6 +1,6 @@ --- name: Q and A, or Other -about: Ask a question, not related to a specific bug or feature request. (Note our contributor agreement at https://github.com/verilator/verilator/blob/master/docs/CONTRIBUTING.adoc) +about: Ask a question, not related to a specific bug or feature request. (Note our contributor agreement at https://github.com/verilator/verilator/blob/master/docs/CONTRIBUTING.rst) title: '' labels: new assignees: '' diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a05a08e94..fd0304994 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1 +1 @@ -We appreciate your contributing to Verilator. If this is your first commit, please add your name to docs/CONTRIBUTORS, and read our contributing guidelines in docs/CONTRIBUTING.adoc. +We appreciate your contributing to Verilator. If this is your first commit, please add your name to docs/CONTRIBUTORS, and read our contributing guidelines in docs/CONTRIBUTING.rst. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 98ba13ed7..ca1b4f3f7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,5 +1,5 @@ # DESCRIPTION: Github actions config -# This name is key to badges in README.adoc, so we use the name build +# This name is key to badges in README.rst, so we use the name build # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 name: build diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 6cf4c5b4b..88a3c897c 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,5 +1,4 @@ # DESCRIPTION: Github actions config -# This name is key to badges in README.adoc, so we use the name build # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 name: clang-format diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 4e6f4567f..8fa0e88e8 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -66,6 +66,12 @@ jobs: - 1 - 2 - 3 + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 include: - { test: dist, num: '' } name: test-${{ matrix.test }}${{ matrix.num }} diff --git a/Changes b/Changes index cf1962931..e09133c42 100644 --- a/Changes +++ b/Changes @@ -2,6 +2,28 @@ Revision history for Verilator The contributors that suggested a given feature are shown in []. Thanks! +* Verilator 4.200 2021-03-12 + +** Add simulation context (VerilatedContext) to allow multiple fully independent + models to be in the same process. Please see the updated examples. (#2660) + +** Add context->time() and context->timeInc() API calls, to set simulation time. + These now are recommended in place of the legacy sc_time_stamp(). + +**** Converted Asciidoc documentation into reStructuredText (RST) format. + +**** --inhibit-sim is planned for deprecation, file a bug if this is still being used. + +**** Fix range inheritance on port without data type (#2753). [Embedded Go] + +**** Fix slice-assign overflow (#2803) (#2811). [David Turner] + +**** Fix interface array connection ordering broken in v4.110 (#2827). [Don Owen] + +**** Fix or-reduction on different scopes broken in 4.110 (#2828). [Yinan Xu] + +**** Fix MSVC++ compile error. (#2831) (#2833) [Drew Taussig] + * Verilator 4.110 2021-02-25 diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP index 6c8b4c4c0..298886f43 100644 --- a/MANIFEST.SKIP +++ b/MANIFEST.SKIP @@ -43,6 +43,7 @@ docs/clang-format.txt$ docs/doxygen-doc/.* examples/xml_py/copied/ examples/xml_py/graph.* +sonar-project.properties src/Makefile$ src/Makefile_obj$ include/verilated.mk$ diff --git a/Makefile.in b/Makefile.in index fdf0ef90e..de87a7a68 100644 --- a/Makefile.in +++ b/Makefile.in @@ -124,7 +124,7 @@ DISTFILES_INC = $(INFOS) .gitignore \ Changes \ LICENSE \ MANIFEST.SKIP \ - README.adoc \ + README.rst \ verilator-config.cmake.in \ verilator-config-version.cmake.in \ bin/verilator \ @@ -134,16 +134,16 @@ DISTFILES_INC = $(INFOS) .gitignore \ bin/verilator_includer \ bin/verilator_profcfunc \ docs/.gitignore \ - docs/CONTRIBUTING.adoc \ + docs/CONTRIBUTING.rst \ docs/CONTRIBUTORS \ docs/Makefile.in \ docs/TODO \ docs/doxygen-mainpage \ docs/doxygen.config \ - docs/install.adoc \ - docs/internals.adoc \ + docs/install.rst \ + docs/internals.rst \ docs/verilator_logo.png \ - docs/xml.adoc \ + docs/xml.rst \ install-sh configure *.pod \ include/*.[chv]* \ include/*.in \ @@ -474,9 +474,7 @@ clang-format: || echo "*** You are not using clang-format 10.0, indents may differ from master's ***" $(CLANGFORMAT) $(CLANGFORMAT_FLAGS) $(CLANGFORMAT_FILES) -YAPF = yapf3 -YAPF_FLAGS = -i -YAPF_FILES = \ +PY_PROGRAMS = \ examples/xml_py/vl_file_copy \ examples/xml_py/vl_hier_graph \ src/astgen \ @@ -486,14 +484,32 @@ YAPF_FILES = \ src/flexfix \ src/vlcovgen \ nodist/code_coverage \ - nodist/code_coverage.dat \ nodist/dot_importer \ nodist/fuzzer/actual_fail \ nodist/fuzzer/generate_dictionary \ nodist/install_test \ +PY_FILES = \ + $(PY_PROGRAMS) \ + nodist/code_coverage.dat \ + +YAPF = yapf3 +YAPF_FLAGS = -i + yapf: - $(YAPF) $(YAPF_FLAGS) $(YAPF_FILES) + $(YAPF) $(YAPF_FLAGS) $(PY_FILES) + +FLAKE8 = flake8 +FLAKE8_FLAGS = \ + --extend-exclude=fastcov.py \ + --ignore=E123,E129,E251,E501,W503,W504,E701 + +PYLINT = pylint +PYLINT_FLAGS = --disable=R0801 + +lint-py: + $(FLAKE8) $(FLAKE8_FLAGS) $(PY_PROGRAMS) + $(PYLINT) $(PYLINT_FLAGS) $(PY_PROGRAMS) format-pl-exec: -chmod a+x test_regress/t/*.pl diff --git a/README.adoc b/README.adoc deleted file mode 100644 index f97ba1f06..000000000 --- a/README.adoc +++ /dev/null @@ -1,154 +0,0 @@ -// Github doesn't render images unless absolute URL -:!toc: - -ifdef::env-github[] -image:https://img.shields.io/badge/License-LGPL%20v3-blue.svg[license LGPLv3,link=https://www.gnu.org/licenses/lgpl-3.0] -image:https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg[license Artistic-2.0,link=https://opensource.org/licenses/Artistic-2.0] -image:https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f[Code Quality,link=https://www.codacy.com/gh/verilator/verilator] -image:https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg[Coverage,link=https://codecov.io/gh/verilator/verilator] -image:https://github.com/verilator/verilator/workflows/build/badge.svg[Build Status (GitHub),link=https://github.com/verilator/verilator/actions?query=workflow%3Abuild] -endif::[] - -ifdef::env-github[] -:link_verilator_contributing: link:docs/CONTRIBUTING.adoc -:link_verilator_install: link:docs/install.adoc -endif::[] -ifndef::env-github[] -:link_verilator_contributing: https://github.com/verilator/verilator/blob/master/docs/CONTRIBUTING.adoc -:link_verilator_install: https://verilator.org/install -endif::[] -:link_verilator_commercial_support: https://verilator.org/verilator_commercial_support - -== Welcome to Verilator - -[cols="a,a",indent=0,frame="none",grid="rows"] -|=== -^.^| *Welcome to Verilator, the fastest Verilog/SystemVerilog simulator.* -+++
+++ • Accepts synthesizable Verilog or SystemVerilog -+++
+++ • Performs lint code-quality checks -+++
+++ • Compiles into multithreaded {cpp}, or SystemC -+++
+++ • Creates XML to front-end your own tools -<.^|image:https://www.veripool.org/img/verilator_256_200_min.png[Logo,256,200] - ->.^|image:https://www.veripool.org/img/verilator_multithreaded_performance_bg-min.png[,388,178] -^.^| *Fast* -+++
+++ • Outperforms many commercial simulators -+++
+++ • Single- and multi-threaded output models - -^.^| *Widely Used* -+++
+++ • Wide industry and academic deployment -+++
+++ • Out-of-the-box support from Arm, and RISC-V vendor IP -<.^|image:https://www.veripool.org/img/verilator_usage_400x200-min.png[,400,200] - ->.^|image:https://www.veripool.org/img/verilator_community_400x125-min.png[,400,125] -^.^| *Community Driven & Openly Licensed* -+++
+++ • Guided by the https://chipsalliance.org/[CHIPS Alliance] and https://www.linuxfoundation.org/[Linux Foundation] -+++
+++ • Open, and free as in both speech and beer -+++
+++ • More simulation for your verification budget - -^.^| *Commercial Support Available* -+++
+++ • Commercial support contracts -+++
+++ • Design support contracts -+++
+++ • Enhancement contracts -<.^|image:https://www.veripool.org/img/verilator_support_400x125-min.png[,400,125] - -|=== - -== What Verilator Does - -Verilator is invoked with parameters similar to GCC or Synopsys's VCS. It -"Verilates" the specified Verilog or SystemVerilog code by -reading it, performing lint checks, and optionally inserting assertion -checks and coverage-analysis points. It outputs single- or multi-threaded -.cpp and .h files, the "Verilated" code. - -The user writes a little {cpp}/SystemC wrapper file, which instantiates the -"Verilated" model of the user's top level module. These {cpp}/SystemC -files are then compiled by a {cpp} compiler (gcc/clang/MSVC++). The -resulting executable performs the design simulation. Verilator also -supports linking its generated libraries, optionally encrypted, into other -simulators. - -Verilator may not be the best choice if you are expecting a full featured -replacement for NC-Verilog, VCS or another commercial Verilog simulator, or -if you are looking for a behavioral Verilog simulator e.g. for a quick -class project (we recommend http://iverilog.icarus.com[Icarus Verilog] for -this.) However, if you are looking for a path to migrate SystemVerilog to -{cpp} or SystemC, or your team is comfortable writing just a touch of {cpp} -code, Verilator is the tool for you. - -== Performance - -Verilator does not simply convert Verilog HDL to {cpp} or SystemC. Rather, -Verilator compiles your code into a much faster optimized and optionally -thread-partitioned model, which is in turn wrapped inside a -{cpp}/SystemC module. The results are a compiled -Verilog model that executes even on a single-thread over 10x faster than -standalone SystemC, and on a single thread is about 100 times faster than -interpreted Verilog simulators such as http://iverilog.icarus.com[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, -Modelsim, 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. - -For more information on how Verilator stacks up to some of the other -closed-sourced and open-sourced Verilog simulators, see the -https://www.veripool.org/verilog_sim_benchmarks.html[Verilog Simulator -Benchmarks]. (If you benchmark Verilator, please see the notes in the -https://verilator.org/verilator_doc.pdf[Verilator manual (PDF)], and also -if possible post on the forums the results; there may be additional tweaks -possible.) - -== Installation & Documentation - -For more information: - -* {link_verilator_install}[Verilator installation and package directory - structure] - -* https://verilator.org/verilator_doc.html[Verilator manual (HTML)], -or https://verilator.org/verilator_doc.pdf[Verilator manual (PDF)] - -* https://github.com/verilator/verilator-announce[Subscribe to verilator announcements] - -* https://verilator.org/forum[Verilator forum] - -* https://verilator.org/issues[Verilator issues] - -== Support - -Verilator is a community project, guided by the -https://chipsalliance.org/[CHIPS Alliance] under the -https://www.linuxfoundation.org/[Linux Foundation]. - -We appreciate and welcome your contributions in whatever form; please see -{link_verilator_contributing}[Contributing to Verilator]. Thanks to our -https://verilator.org/verilator_doc.html#CONTRIBUTORS[Contributors and -Sponsors]. - -Verilator also supports and encourages commercial support models and -organizations; please see {link_verilator_commercial_support}[Verilator -Commercial Support]. - -== Related Projects - -* http://gtkwave.sourceforge.net/[GTKwave] - Waveform viewer for Verilator -traces. - -* http://iverilog.icarus.com[Icarus Verilog] - Icarus is a full featured -interpreted Verilog simulator. If Verilator does not support your needs, -perhaps Icarus may. - -== Open License - -Verilator is Copyright 2003-2021 by Wilson Snyder. (Report bugs to -https://verilator.org/issues[Verilator Issues].) - -Verilator 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. See the documentation for more -details. diff --git a/README.rst b/README.rst new file mode 100644 index 000000000..8591c7587 --- /dev/null +++ b/README.rst @@ -0,0 +1,160 @@ +.. Github doesn't render images unless absolute URL +.. Do not know of a conditional tag, "only: github" nor "github display" works + +.. image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg + :target: https://www.gnu.org/licenses/lgpl-3.0] +.. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg + :target: https://opensource.org/licenses/Artistic-2.0 +.. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f + :target: https://www.codacy.com/gh/verilator/verilator +.. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg + :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 + +Welcome to Verilator +==================== + +.. list-table:: + + * - **Welcome to Verilator, the fastest Verilog/SystemVerilog simulator.** + * Accepts synthesizable Verilog or SystemVerilog + * Performs lint code-quality checks + * Compiles into multithreaded C++, or SystemC + * Creates XML to front-end your own tools + - |Logo| + * - |verilator multithreaded performance bg min| + - **Fast** + * Outperforms many commercial simulators + * Single- and multi-threaded output models + * - **Widely Used** + * Wide industry and academic deployment + * Out-of-the-box support from Arm, and RISC-V vendor IP + - |verilator usage 400x200 min| + * - |verilator community 400x125 min| + - **Community Driven & Openly Licensed** + * Guided by the `CHIPS Alliance`_ and `Linux Foundation`_ + * Open, and free as in both speech and beer + * More simulation for your verification budget + * - **Commercial Support Available** + * Commercial support contracts + * Design support contracts + * Enhancement contracts + - |verilator support 400x125 min| + + +What Verilator Does +=================== + +Verilator is invoked with parameters similar to GCC or Synopsys's VCS. It +"Verilates" the specified Verilog or SystemVerilog code by reading it, +performing lint checks, and optionally inserting assertion checks and +coverage-analysis points. It outputs single- or multi-threaded .cpp and .h +files, the "Verilated" code. + +The user writes a little C++/SystemC wrapper file, which instantiates the +"Verilated" model of the user's top level module. These C++/SystemC files +are then compiled by a C++ compiler (gcc/clang/MSVC++). The resulting +executable performs the design simulation. Verilator also supports linking +its generated libraries, optionally encrypted, into other simulators. + +Verilator may not be the best choice if you are expecting a full featured +replacement for NC-Verilog, VCS or another commercial Verilog simulator, or +if you are looking for a behavioral Verilog simulator e.g. for a quick +class project (we recommend `Icarus Verilog`_ for this.) However, if you +are looking for a path to migrate SystemVerilog to C++ or SystemC, or your +team is comfortable writing just a touch of C++ code, Verilator is the tool +for you. + + +Performance +=========== + +Verilator does not simply convert Verilog HDL to C++ or SystemC. Rather, +Verilator compiles your code into a much faster optimized and optionally +thread-partitioned model, which is in turn wrapped inside a C++/SystemC +module. The results are a compiled Verilog model that executes even on a +single-thread over 10x faster than standalone SystemC, and on a single +thread is about 100 times faster than interpreted Verilog simulators such +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, +Modelsim, 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. + +For more information on how Verilator stacks up to some of the other +closed-sourced and open-sourced Verilog simulators, see the `Verilog +Simulator Benchmarks +`_. (If you +benchmark Verilator, please see the notes in the `Verilator manual (PDF) +`_, and also if possible post on +the forums the results; there may be additional tweaks possible.) + + +Installation & Documentation +============================ + +For more information: + +- `Verilator installation and package directory structure + `_ + +- `Verilator manual (HTML) `_, + or `Verilator manual (PDF) `_ + +- `Subscribe to verilator announcements + `_ + +- `Verilator forum `_ + +- `Verilator issues `_ + + +Support +======= + +Verilator is a community project, guided by the `CHIPS Alliance`_ under the +`Linux Foundation`_. + +We appreciate and welcome your contributions in whatever form; please see +`Contributing to Verilator +`_. +Thanks to our `Contributors and Sponsors +`_. + +Verilator also supports and encourages commercial support models and +organizations; please see `Verilator Commercial Support +`_. + + +Related Projects +================ + +- `GTKwave `_ - Waveform viewer for + Verilator traces. + +- `Icarus Verilog`_ - Icarus is a full featured interpreted Verilog + simulator. If Verilator does not support your needs, perhaps Icarus may. + + +Open License +============ + +Verilator is Copyright 2003-2021 by Wilson Snyder. (Report bugs to +`Verilator Issues `_.) + +Verilator 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. See the documentation for more details. + +.. _CHIPS Alliance: https://chipsalliance.org +.. _Icarus Verilog: http://iverilog.icarus.com +.. _Linux Foundation: https://www.linuxfoundation.org +.. |Logo| image:: https://www.veripool.org/img/verilator_256_200_min.png +.. |verilator multithreaded performance bg min| image:: https://www.veripool.org/img/verilator_multithreaded_performance_bg-min.png +.. |verilator usage 400x200 min| image:: https://www.veripool.org/img/verilator_usage_400x200-min.png +.. |verilator community 400x125 min| image:: https://www.veripool.org/img/verilator_community_400x125-min.png +.. |verilator support 400x125 min| image:: https://www.veripool.org/img/verilator_support_400x125-min.png diff --git a/bin/verilator b/bin/verilator index e6c16fe2c..d18eafd5c 100755 --- a/bin/verilator +++ b/bin/verilator @@ -1953,7 +1953,8 @@ Displays program version and exits. =head1 EXAMPLE C++ EXECUTION -We'll compile this example into C++. +We'll compile this example into C++. For an extended and commented version +of what this C++ code is doing, see examples/make_tracing_c/sim_main.cpp. mkdir test_our cd test_our @@ -1968,21 +1969,24 @@ We'll compile this example into C++. #include "Vour.h" #include "verilated.h" int main(int argc, char** argv, char** env) { - Verilated::commandArgs(argc, argv); - Vour* top = new Vour; - while (!Verilated::gotFinish()) { top->eval(); } + VerilatedContext* contextp = new VerilatedContext; + contextp->commandArgs(argc, argv); + Vour* top = new Vour{contextp}; + while (!contextp->gotFinish()) { top->eval(); } delete top; - exit(0); + delete contextp; + return 0; } EOF -See the README in the source kit for various ways to install or point to -Verilator binaries. In brief, if you installed Verilator using the package -manager of your operating system, or did a "make install" to place -Verilator into your default path, you do not need anything special in your -environment, and should not have VERILATOR_ROOT set. However, if you -installed Verilator from sources and want to run Verilator out of where you -compiled Verilator, you need to point to the kit: +Next we need Verilator installed. See the README in the source kit for +various ways to install or point to Verilator binaries. In brief, if you +installed Verilator using the package manager of your operating system, or +did a "make install" to place Verilator into your default path, you do not +need anything special in your environment, and should not have +VERILATOR_ROOT set. However, if you installed Verilator from sources and +want to run Verilator out of where you compiled Verilator, you need to +point to the kit: # See above; don't do this if using an OS-distributed Verilator export VERILATOR_ROOT=/path/to/where/verilator/was/installed @@ -2037,13 +2041,12 @@ This is an example similar to the above, but using SystemC. #include "Vour.h" int sc_main(int argc, char** argv) { Verilated::commandArgs(argc, argv); - sc_clock clk("clk", 10, SC_NS, 0.5, 3, SC_NS, true); - Vour* top; - top = new Vour("top"); + sc_clock clk{"clk", 10, SC_NS, 0.5, 3, SC_NS, true}; + Vour* top = new Vour{"top"}; top->clk(clk); while (!Verilated::gotFinish()) { sc_start(1, SC_NS); } delete top; - exit(0); + return 0; } EOF @@ -2101,15 +2104,15 @@ When using SystemC, evaluation of the Verilated model is managed by the SystemC kernel, and for the most part can be ignored. When using C++, the user must call eval(), or eval_step() and eval_end_step(). -1. When there is a single design instantiated at the C++ level that needs to -evaluate, just call designp->eval(). +1. When there is a single design instantiated at the C++ level that needs +to evaluate within a given context, just call designp->eval(). -2. When there are multiple designs instantiated at the C++ level that -need to evaluate, call first_designp->eval_step() then ->eval_step() on all -other designs. Then call ->eval_end_step() on the first design then all -other designs. If there is only a single design, you would call -eval_step() then eval_end_step(); in fact eval() described above is just a -wrapper which calls these two functions. +2. When there are multiple designs instantiated at the C++ level that need +to evaluate within a context, call first_designp->eval_step() then +->eval_step() on all other designs. Then call ->eval_end_step() on the +first design then all other designs. If there is only a single design, you +would call eval_step() then eval_end_step(); in fact eval() described above +is just a wrapper which calls these two functions. When eval() is called Verilator looks for changes in clock signals and evaluates related sequential always blocks, such as computing always_ff @ @@ -2124,7 +2127,7 @@ or all inputs go directly to always_ff statements, as is typical, then you can change non-clock inputs on the negative edge of the input clock, which will be faster as there will be fewer eval() calls. -For more information on evaluation, see docs/internals.adoc in the +For more information on evaluation, see docs/internals.rst in the distribution. @@ -2704,7 +2707,7 @@ accesses the above signal "readme" would be: read_and_check(); } delete top; - exit(0); + return 0; } EOF @@ -5127,6 +5130,10 @@ Verilator currently requires C++11 or newer compilers. Verilator will require C++14 or newer compilers for both compiling Verilator and compiling Verilated models no sooner than January 2022. +=item --inhibit-sim + +The --inhibit-sim option is planned for removal no sooner than July 2021. + =item Configuration File -msg The -msg argument to lint_off has been replaced with -rule. -msg is @@ -5414,8 +5421,14 @@ controlled by --assert are disabled. =item Why do I get "undefined reference to `sc_time_stamp()'"? -In C++ (non SystemC) code you need to define this function so that the -simulator knows the current time. See the "CONNECTING TO C++" examples. +In 4.200 and later, using the timeInc function is recommended instead. See +the "CONNECTING TO C++" examples. Some linkers (MSVC++) still require +sc_time_stamp() to be defined, either define this with ("double +sc_time_stamp() { return 0; }") or compile the Verilated code with +-DVL_TIME_CONTEXT. + +Prior to Verilator 4.200, the sc_time_stamp function needs to be defined in +C++ (non SystemC) to return the current simulation time. =item Why do I get "undefined reference to `VL_RAND_RESET_I' or `Verilated::...'"? @@ -5753,7 +5766,7 @@ L, L, L, L, L which is the source for this document, -and docs/internals.adoc in the distribution. +and docs/internals.rst in the distribution. =cut diff --git a/bin/verilator_coverage b/bin/verilator_coverage index bd0fee254..8093ad94a 100755 --- a/bin/verilator_coverage +++ b/bin/verilator_coverage @@ -158,22 +158,25 @@ verilator_coverage - Verilator coverage analyzer verilator_coverage -write-info merged.info -read ... -Verilator_coverage processes Verilator coverage reports. +Verilator_coverage processes Verilated model-generated coverage reports. -With --anotate, it reads the specified data file and generates annotated -source code with coverage metrics annotated. If multiple coverage points -exist on the same line, additional lines will be inserted to report the -additional points. +With --annotate, it reads the specified coverage data file and generates +annotated source code with coverage metrics annotated. If multiple +coverage points exist on the same source code line, additional lines will +be inserted to report the additional points. -Additional Verilog-standard arguments specify the search paths necessary to -find the source code that the coverage analysis was performed on. +Additional Verilog-XL-style standard arguments specify the search paths +necessary to find the source code that the coverage analysis was performed +on. -To get correct coverage percentages, you may wish to read logs/coverage.dat -into Emacs and do a M-x keep-lines to include only those statistics of -interest. +To filter those items to be included in coverage, you may read +logs/coverage.dat into an editor and do a M-x keep-lines to include only +those statistics of interest and save to a new .dat file. -For Verilog conditions that should never occur, you should add a $stop -statement. This will remove the coverage during the next build. +For Verilog conditions that should never occur, either add a $stop +statement to the appropriate statement block, or see the Verilator +documentation on "// verilator coverage_off" and "coverage_off". This will +remove the coverage points after the model is re-Verilated. =head1 ARGUMENTS @@ -181,12 +184,12 @@ statement. This will remove the coverage during the next build. =item I -Specify input data file, may be repeated to read multiple inputs. If no +Specify input data file. May be repeated to read multiple inputs. If no data file is specified, by default coverage.dat is read. =item --annotate I -Sprcifies the directory name that source files with annotated coverage data +Specifies the directory name that source files with annotated coverage data should be written to. =item --annotate-all diff --git a/bin/verilator_gantt b/bin/verilator_gantt index ac9683385..701a08788 100755 --- a/bin/verilator_gantt +++ b/bin/verilator_gantt @@ -544,9 +544,9 @@ scale printed, i.e. a certain about of time for each character width. The Y-axis shows threads, each thread's execution is shown on one line. That line shows "[" at the position in time when it executes. -Following the "[" is the cpu number the task executed on, followed by zero +Following the "[" is the CPU number the task executed on, followed by zero or more "-" to make the width of the characters match the scaled execution -time, followed by a "]". If the scale is too small, the cpu number and +time, followed by a "]". If the scale is too small, the CPU number and mtask number will not be printed. If the scale is very small, a "&" indicates multiple mtasks started at that time position. diff --git a/ci/ci-script.bash b/ci/ci-script.bash index eb3b828cf..c8a822d5c 100755 --- a/ci/ci-script.bash +++ b/ci/ci-script.bash @@ -103,28 +103,64 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then nodist/code_coverage --stages 1- --scenarios=--dist ;; coverage-vlt-0) - nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=0/4 + nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=0/10 ;; coverage-vlt-1) - nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=1/4 + nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=1/10 ;; coverage-vlt-2) - nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=2/4 + nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=2/10 ;; coverage-vlt-3) - nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=3/4 + nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=3/10 + ;; + coverage-vlt-4) + nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=4/10 + ;; + coverage-vlt-5) + nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=5/10 + ;; + coverage-vlt-6) + nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=6/10 + ;; + coverage-vlt-7) + nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=7/10 + ;; + coverage-vlt-8) + nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=8/10 + ;; + coverage-vlt-9) + nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=9/10 ;; coverage-vltmt-0) - nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=0/4 + nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=0/10 ;; coverage-vltmt-1) - nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=1/4 + nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=1/10 ;; coverage-vltmt-2) - nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=2/4 + nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=2/10 ;; coverage-vltmt-3) - nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=3/4 + nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=3/10 + ;; + coverage-vltmt-4) + nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=4/10 + ;; + coverage-vltmt-5) + nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=5/10 + ;; + coverage-vltmt-6) + nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=6/10 + ;; + coverage-vltmt-7) + nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=7/10 + ;; + coverage-vltmt-8) + nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=8/10 + ;; + coverage-vltmt-9) + nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=9/10 ;; *) fatal "Unknown test: $TESTS" diff --git a/ci/docker/buildenv/README.adoc b/ci/docker/buildenv/README.adoc deleted file mode 100644 index ed2270eed..000000000 --- a/ci/docker/buildenv/README.adoc +++ /dev/null @@ -1,49 +0,0 @@ -= Verilator Docker Build Environment - -This Verilator Build container is set up to compile and test a Verilator -build. It uses the following parameters: - -* Source repository (default: https://github.com/verilator/verilator) -* Source revision (default: master) -* Compiler (GCC 9.3.0, clang 10.0.0, default: 9.3.0) - -The container is published as `verilator/verilator-buildenv` on -https://hub.docker.com/repository/docker/verilator/verilator-buildenv[docker hub]. - -To run the basic build using the current Verilator master: - - docker run -ti verilator/verilator-buildenv - -To also run tests: - - docker run -ti verilator/verilator-buildenv test - -To change the compiler: - - docker run -ti -e CC=clang-10 -e CXX=clang++-10 verilator/verilator-buildenv test - -The tests that involve gdb are not working due to security restrictions. -To run those too: - -.... -docker run -ti -e CC=clang-10 -e CXX=clang++-10 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined verilator/verilator-buildenv test -.... - -Rather then building using a remote git repository you may prefer to use a -working copy on the local filesystem. Mount the local working copy path as -a volume and use that in place of git. When doing this be careful to have -all changes committed to the local git area. To build the current HEAD from -top of a repository: - -.... -docker run -ti -v ${PWD}:/tmp/repo -e REPO=/tmp/repo -e REV=`git rev-parse --short HEAD` --cap-add=SYS_PTRACE --security-opt seccomp=unconfined verilator/verilator-buildenv test -.... - -== Rebuilding - -To rebuild the Verilator-buildenv docker image, run: - - docker build . - -This will also build SystemC under all supported compiler variants to -reduce the SystemC testing time. diff --git a/ci/docker/buildenv/README.rst b/ci/docker/buildenv/README.rst new file mode 100644 index 000000000..2dd2c7215 --- /dev/null +++ b/ci/docker/buildenv/README.rst @@ -0,0 +1,64 @@ +================================ +Verilator Build Docker Container +================================ + +This Verilator Build Docker Container is set up to compile and test a +Verilator build. It uses the following parameters: + +- Source repository (default: https://github.com/verilator/verilator) + +- Source revision (default: master) + +- Compiler (GCC 9.3.0, clang 10.0.0, default: 9.3.0) + +The container is published as ``verilator/verilator-buildenv`` on `docker +hub +`__. + +To run the basic build using the current Verilator master: + +:: + + docker run -ti verilator/verilator-buildenv + +To also run tests: + +:: + + docker run -ti verilator/verilator-buildenv test + +To change the compiler: + +:: + + docker run -ti -e CC=clang-10 -e CXX=clang++-10 verilator/verilator-buildenv test + +The tests that involve gdb are not working due to security restrictions. +To run those too: + +:: + + docker run -ti -e CC=clang-10 -e CXX=clang++-10 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined verilator/verilator-buildenv test + +Rather then building using a remote git repository you may prefer to use a +working copy on the local filesystem. Mount the local working copy path as +a volume and use that in place of git. When doing this be careful to have +all changes committed to the local git area. To build the current HEAD from +top of a repository: + +:: + + docker run -ti -v ${PWD}:/tmp/repo -e REPO=/tmp/repo -e REV=`git rev-parse --short HEAD` --cap-add=SYS_PTRACE --security-opt seccomp=unconfined verilator/verilator-buildenv test + + +Rebuilding +========== + +To rebuild the Verilator-buildenv docker image, run: + +:: + + docker build . + +This will also build SystemC under all supported compiler variants to +reduce the SystemC testing time. diff --git a/ci/docker/run/README.adoc b/ci/docker/run/README.adoc deleted file mode 100644 index 84321ac06..000000000 --- a/ci/docker/run/README.adoc +++ /dev/null @@ -1,59 +0,0 @@ -= Verilator Executable Docker Container - -The Verilator Executable Docker Container allows you to run Verilator -easily as a docker image, e.g.: - - docker run -ti verilator/verilator:latest --version - -This will install the container, run the latest Verilator and print -Verilator's version. - -Containers are automatically built for all released versions, so you may -easily compare results across versions, e.g.: - - docker run -ti verilator/verilator:4.030 --version - -Verilator needs to read and write files on the local system. To simplify -this process, use the `verilator-docker` convenience script. This script -takes the version number, and all remaining arguments are passed through to -Verilator. e.g.: - - ./verilator-docker 4.030 --version - -or - - ./verilator-docker 4.030 --cc test.v - -If you prefer not to use `verilator-docker` you must give the container -access to your files as a volume with appropriate user rights. For example -to Verilate test.v: - -.... -docker run -ti -v ${PWD}:/work --user $(id -u):$(id -g) verilator/verilator:latest --cc test.v -.... - -This method can only access files below the current directory. An -alternative is setup the volume `-workdir`. - -You can also work in the container by setting the entrypoint -(don't forget to mount a volume if you want your work persistent): - - docker run -ti --entrypoint /bin/bash verilator/verilator:latest - -You can also use the container to build Verilator at a specific -commit: - - docker build --build-arg SOURCE_COMMIT= . - -== Internals - -The Dockerfile builds Verilator and removes the tree when completed to -reduce the image size. The entrypoint is set as a wrapper script -(`verilator-wrap.sh`). That script 1. calls Verilator, and 2. copies the -Verilated runtime files to the `obj_dir` or the `-Mdir` respectively. This -allows the user to have the files to they may later build the C++ output -with the matching runtime files. The wrapper also patches the Verilated -Makefile accordingly. - -There is also a hook defined that is run by docker hub via automated -builds. diff --git a/ci/docker/run/README.rst b/ci/docker/run/README.rst new file mode 100644 index 000000000..c635a9546 --- /dev/null +++ b/ci/docker/run/README.rst @@ -0,0 +1,74 @@ +===================================== +Verilator Executable Docker Container +===================================== + +The Verilator Executable Docker Container allows you to run Verilator +easily as a docker image, e.g.: + +:: + + docker run -ti verilator/verilator:latest --version + +This will install the container, run the latest Verilator and print +Verilator's version. + +Containers are automatically built for all released versions, so you may +easily compare results across versions, e.g.: + +:: + + docker run -ti verilator/verilator:4.030 --version + +Verilator needs to read and write files on the local system. To simplify +this process, use the ``verilator-docker`` convenience script. This script +takes the version number, and all remaining arguments are passed through to +Verilator. e.g.: + +:: + + ./verilator-docker 4.030 --version + +or + +:: + + ./verilator-docker 4.030 --cc test.v + +If you prefer not to use ``verilator-docker`` you must give the container +access to your files as a volume with appropriate user rights. For example +to Verilate test.v: + +:: + + docker run -ti -v ${PWD}:/work --user $(id -u):$(id -g) verilator/verilator:latest --cc test.v + +This method can only access files below the current directory. An +alternative is setup the volume ``-workdir``. + +You can also work in the container by setting the entrypoint (don't forget +to mount a volume if you want your work persistent): + +:: + + docker run -ti --entrypoint /bin/bash verilator/verilator:latest + +You can also use the container to build Verilator at a specific commit: + +:: + + docker build --build-arg SOURCE_COMMIT= . + + +Internals +========= + +The Dockerfile builds Verilator and removes the tree when completed to +reduce the image size. The entrypoint is set as a wrapper script +(``verilator-wrap.sh``). That script 1. calls Verilator, and 2. copies the +Verilated runtime files to the ``obj_dir`` or the ``-Mdir`` +respectively. This allows the user to have the files to they may later +build the C++ output with the matching runtime files. The wrapper also +patches the Verilated Makefile accordingly. + +There is also a hook defined that is run by docker hub via automated +builds. diff --git a/configure.ac b/configure.ac index 5cbb6ffe0..3d7c5ea55 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ #AC_INIT([Verilator],[#.### YYYY-MM-DD]) #AC_INIT([Verilator],[#.### devel]) -AC_INIT([Verilator],[4.110 2021-02-25], +AC_INIT([Verilator],[4.200 2021-03-12], [https://verilator.org], [verilator],[https://verilator.org]) # When releasing, also update header of Changes file diff --git a/docs/CONTRIBUTING.adoc b/docs/CONTRIBUTING.adoc deleted file mode 100644 index eda1108e6..000000000 --- a/docs/CONTRIBUTING.adoc +++ /dev/null @@ -1,82 +0,0 @@ -= How to contribute to Verilator - -Thanks for using Verilator! We welcome your contributions in whatever form. - -This contributing document contains some suggestions that may make -contributions flow more efficiently. - -==== Did you find a bug? - -* Please **Ensure the bug was not already reported** by searching - https://verilator.org/issues[Verilator Issues]. - -* If you're unable to find an open issue addressing the problem, - https://verilator.org/issues/new[open a new Verilator issue]. - -** Be sure to include a **code sample** or an **executable test case** - demonstrating the bug and expected behavior that is not occurring. - -** The ideal example works against other simulators, and is in the - test_regress/t test format, as described in - link:internals.adoc[docs/internals]. - -==== Did you write a patch that fixes a bug? - -* Please https://verilator.org/issues/new[Open a new issue]. - -* You may attach a patch to the issue, or (preferred) may request a GitHub - pull request. - -** Verilator uses GitHub Actions to provide continuous integration. You may - want to enable Actions on your GitHub branch to ensure your changes - keep the tests passing. See link:internals.adoc[docs/internals]. - -* Your source-code contributions must be certified as open source, under - the https://developercertificate.org/[Developer Certificate of - Origin]. On your first contribution, you must either: - -** Have your patch include the addition of your name to - link:CONTRIBUTORS[docs/CONTRIBUTORS] (preferred). - -** Use "git -s" as part of your commit. This adds a "signed-of-by" - attribute which will certify your contribution as described in the - https://github.com/wking/signed-off-by/blob/master/Documentation/SubmittingPatches[Signed-of-By - convention]. - -** Email, or post in an issue a statement that you certify your - contributions. - -** In any of these cases your name will be added to - link:CONTRIBUTORS[docs/CONTRIBUTORS] and you are agreeing all future - contributions are also certified. - -** We occasionally accept contributions where people do not want their name - published. Please email us; you must still privately certify your - contribution. - -* Your test contributions are generally considered released into the - Creative Commons Public Domain (CC0), unless you request otherwise or put - a GNU/Artistic license on your file. - -* Most important is we get your patch. If you'd like to clean up - indentation and related issues ahead of our feedback, that is - appreciated; please see the coding conventions in - link:internals.adoc[docs/internals]. - -==== Do you have questions? - -* Please see FAQ section and rest of the - https://verilator.org/verilator_doc.html[Verilator manual], - or https://verilator.org/verilator_doc.pdf[Verilator manual (PDF)]. - -* Ask any question in the - https://verilator.org/forum[Verilator forum]. - -==== Code of Conduct - -* Our contributors and participants pledge to make participation in our - project and our community a positive experience for everyone. We follow - the https://www.contributor-covenant.org/version/1/4/code-of-conduct.html[Contributor - Covenant version 1.4]. - -Thanks! diff --git a/docs/CONTRIBUTING.rst b/docs/CONTRIBUTING.rst new file mode 100644 index 000000000..a9ae58e42 --- /dev/null +++ b/docs/CONTRIBUTING.rst @@ -0,0 +1,93 @@ +========================= +Contributing to Verilator +========================= + +Thanks for using Verilator! We welcome your contributions in whatever form. + +This contributing document contains some suggestions that may make +contributions flow more efficiently. + + +Did you find a bug? +=================== + +- Please **Ensure the bug was not already reported** by searching + `Verilator Issues `__. + +- If you're unable to find an open issue addressing the problem, `open a + new Verilator issue `__. + + - Be sure to include a **code sample** or an **executable test case** + demonstrating the bug and expected behavior that is not occurring. + + - The ideal example works against other simulators, and is in the + test_regress/t test format, as described in `docs/internals + `__. + + +Did you write a patch that fixes a bug? +======================================= + +- Please `Open a new issue `__. + +- You may attach a patch to the issue, or (preferred) may request a + GitHub pull request. + + - Verilator uses GitHub Actions to provide continuous integration. You + may want to enable Actions on your GitHub branch to ensure your changes + keep the tests passing. See `docs/internals `__. + +- Your source-code contributions must be certified as open source, + under the `Developer Certificate of + Origin `__. On your first + contribution, you must either: + + - Have your patch include the addition of your name to `docs/CONTRIBUTORS + `__ (preferred). + + - Use "git -s" as part of your commit. This adds a "signed-of-by" + attribute which will certify your contribution as described in the + `Signed-of-By convention + `__. + + - Email, or post in an issue a statement that you certify your + contributions. + + - In any of these cases your name will be added to `docs/CONTRIBUTORS + `__ and you are agreeing all future contributions are + also certified. + + - We occasionally accept contributions where people do not want their + name published. Please email us; you must still privately certify your + contribution. + +- Your test contributions are generally considered released into the + Creative Commons Public Domain (CC0), unless you request otherwise or + put a GNU/Artistic license on your file. + +- Most important is we get your patch. If you’d like to clean up + indentation and related issues ahead of our feedback, that is + appreciated; please see the coding conventions in `docs/internals + `__. + + +Do you have questions? +====================== + +- Please see FAQ section and rest of the `Verilator + manual `__, or `Verilator + manual (PDF) `__. + +- Ask any question in the `Verilator forum + `__. + + +Code of Conduct +=============== + +- Our contributors and participants pledge to make participation in our + project and our community a positive experience for everyone. We follow + the `Contributor Covenant version 1.4 + `__. + +Thanks! diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index f3de9d893..9d35df7de 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -12,6 +12,7 @@ Conor McCullough Dan Petrisko David Horton David Stanford +David Turner Driss Hafdi Edgar E. Iglesias Eric Rippey @@ -89,3 +90,4 @@ Yuri Victorovich Yutetsu TAKATSUKASA Yves Mathieu HyungKi Jeong +Drew Taussig diff --git a/docs/Makefile.in b/docs/Makefile.in index 8c755ab43..609b67b19 100644 --- a/docs/Makefile.in +++ b/docs/Makefile.in @@ -19,8 +19,8 @@ #### Start of system configuration section. #### -ASCIIDOCTOR = asciidoctor DOXYGEN = doxygen +RST2HTML = rst2html #### End of system configuration section. #### ###################################################################### @@ -30,8 +30,8 @@ DOXYGEN = doxygen default: @echo "error: make not supported here, run 'make docs' from Verilator top-level" -%.html: %.adoc - $(ASCIIDOCTOR) $< -n -o $@ +%.html: %.rst + $(RST2HTML) $< $@ clean mostlyclean distclean maintainer-clean:: rm -f $(SCRIPTS) *.tmp diff --git a/docs/install.adoc b/docs/install.adoc deleted file mode 100644 index 2cf4498b6..000000000 --- a/docs/install.adoc +++ /dev/null @@ -1,303 +0,0 @@ -= Verilator Installation -:toc: right - -// Github doesn't render unless absolute URL -image::https://www.veripool.org/img/verilator_256_200_min.png[Logo,256,200,role="right"] - -== Introduction - -This discusses how to install Verilator. For more general information -please see https://verilator.org[verilator.org]. - -== Quick-start - -=== Install From a Package Manager - -Using a distribution's package manager is the easiest way to get -started. (Note packages are unlikely to have the most recent version, so -Git, below, maybe a better alternative.) To install as a package: - - apt-get install verilator - -If this works, skip down to <>. - -=== Docker - -Verilator is available in pre-built Docker containers. See -https://github.com/verilator/verilator/blob/master/ci/docker/run/README.adoc - -=== Git - -Installing Verilator with Git provides the most flexibility. -For additional options and details see the additional sections below. In -brief: - -// Also update README -.... -# Prerequisites: -#sudo apt-get install git make autoconf g++ flex bison -#sudo apt-get install libfl2 # Ubuntu only (ignore if gives error) -#sudo apt-get install libfl-dev # Ubuntu only (ignore if gives error) - -git clone https://github.com/verilator/verilator # Only first time -## Note the URL above is not a page you can see with a browser, it's for git only - -# Every time you need to build: -unsetenv VERILATOR_ROOT # For csh; ignore error if on bash -unset VERILATOR_ROOT # For bash -cd verilator -git pull # Make sure git repository is up-to-date -git tag # See what versions exist -#git checkout master # Use development branch (e.g. recent bug fixes) -#git checkout stable # Use most recent stable release -#git checkout v{version} # Switch to specified release version - -autoconf # Create ./configure script -./configure -make -sudo make install -# Now see "man verilator" or online verilator.pdf's for the example tutorials -.... - -If this works, skip down to <>. - -== Detailed Build Instructions - -This section describes details of the build process, and assumes you are -building from Git or a tarball. For using a pre-built binary for your -Linux distribution, see instead <>. - -=== OS Requirements - -Verilator is developed and has primary testing on Ubuntu, with additional -testing on FreeBSD and Apple OS-X. Versions have also built on Redhat -Linux, HPUX and Solaris. It should run with minor porting on any -GNU/Linux-ish platform. Verilator also works on Windows under Cygwin, and -Windows under MinGW (gcc -mno-cygwin). Verilated output (not Verilator -itself) compiles under all the options above, plus MSVC++. - -=== Install Prerequisites - -To build or run Verilator you need these standard packages: - - sudo apt-get install perl python3 make - sudo apt-get install g++ # Alternatively, clang - sudo apt-get install libgz # Non-Ubuntu (ignore if gives error) - sudo apt-get install libfl2 libfl-dev zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error) - -To build or run the following are optional but should be installed for -good performance: - - sudo apt-get install ccache # If present at build, needed for run - sudo apt-get install libgoogle-perftools-dev numactl perl-doc - -To build Verilator you will need to install these packages; these do not -need to be present to run Verilator: - - sudo apt-get install git autoconf flex bison - -Those developing Verilator itself may also want these (see internals.adoc): - - sudo apt-get install gdb asciidoctor graphviz cmake clang clang-format gprof lcov - cpan install Pod::Perldoc - cpan install Parallel::Forker - -==== Install SystemC - -If you will be using SystemC (vs straight C++ output), download -https://www.accellera.org/downloads/standards/systemc[SystemC]. -Follow their installation instructions. You will need to set `SYSTEMC_INCLUDE` -to point to the include directory with `systemc.h` in it, and `SYSTEMC_LIBDIR` -to points to the directory with `libsystemc.a` in it. (Older installations -may set `SYSTEMC` and `SYSTEMC_ARCH` instead.) - -==== Install GTKWave - -To make use of Verilator FST tracing you will want -http://gtkwave.sourceforge.net/[GTKwave] installed, however this is not -required at Verilator build time. - -=== Obtain Sources - -You may use Git or a tarball for the sources. Git is the supported option. -(If using a historical build that uses a tarball, tarballs are obtained -from https://www.veripool.org/projects/verilator/wiki/Download[Verilator -Downloads]; we presume you know how to use it, and is not described here.) - -Get the sources from the repository: (You need do this only once, ever.) - - git clone https://github.com/verilator/verilator # Only first time - ## Note the URL above is not a page you can see with a browser, it's for git only - -Enter the checkout and determine what version/branch to use: - - cd verilator - git pull # Make sure we're up-to-date - git tag # See what versions exist - #git checkout master # Use development branch (e.g. recent bug fix) - #git checkout stable # Use most recent release - #git checkout v{version} # Switch to specified release version - -=== Auto Configure - -Create the configuration script: - - autoconf # Create ./configure script - -=== Eventual Installation Options - -Before configuring the build, you have to decide how you're going to -eventually install the kit. Verilator will be compiling the current value -of `VERILATOR_ROOT`, `SYSTEMC_INCLUDE`, and `SYSTEMC_LIBDIR` as defaults -into the executable, so they must be correct before configuring. - -These are the options: - -==== 1. Run-in-Place from VERILATOR_ROOT - -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. - - export VERILATOR_ROOT=`pwd` # if your shell is bash - setenv VERILATOR_ROOT `pwd` # if your shell is csh - ./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 `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 CAD Disk - -You may eventually be installing onto a project/company-wide "CAD" tools -disk that may support multiple versions of every tool. Target the build to -a destination directory name that includes the Verilator version name: - - unset VERILATOR_ROOT # if your shell is bash - unsetenv VERILATOR_ROOT # if your shell is csh - # 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 -http://modules.sourceforge.net/[modulecmd], you'll want a module file like -the following: - -.modulecmd's verilator/version file ----- -set install_root /CAD_DISK/verilator/{version-number-used-above} -unsetenv VERILATOR_ROOT -prepend-path PATH $install_root/bin -prepend-path MANPATH $install_root/man -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 `$PATH`. - -==== 4. Install System Globally - -The final option is to eventually install Verilator globally, using the -normal system paths: - - unset VERILATOR_ROOT # if your shell is bash - unsetenv VERILATOR_ROOT # if your shell is csh - ./configure - -Then after installing (below) the binary directories should already be in -your `$PATH`. - -=== Configure - -The command to configure the package was described in the previous step. -Developers should configure to have more complete developer tests. -Additional packages may be required for these tests. - - export VERILATOR_AUTHOR_SITE=1 # Put in your .bashrc - ./configure --enable-longtests ...above options... - -=== Compile - -Compile Verilator: - - make -j - -=== Test - -Check the compilation by running self-tests: - - make test - -=== Install - -If you used any but the <<1. Run-in-Place from VERILATOR_ROOT>> scheme, -install to the OS-standard place: - - make install - -== Running Verilator - -To run Verilator, see the example sections in the -https://verilator.org/verilator_doc.html[Verilator manual (HTML)], -or https://verilator.org/verilator_doc.pdf[Verilator manual (PDF)]. - -Also see the `examples/` directory that is part of the kit, and is installed -(in a OS-specific place, often in e.g. `/usr/local/share/verilator/examples`). - - cd examples/make_hello_c - make - -Note if you did a `make install` above you should not have `VERILATOR_ROOT` -set in your environment; it is built into the executable. - -== Announcements - -To get notified of new releases, go to -https://github.com/verilator/verilator-announce[Verilator announcement -repository] and follow the instructions there. - -== Directory Structure - -Some relevant files and directories in this package are as follows: - - Changes => Version history - README.adoc => This document - bin/verilator => Compiler wrapper invoked to Verilate code - docs/ => Additional documentation - examples/make_hello_c => Example GNU-make simple Verilog->C++ conversion - examples/make_hello_sc => Example GNU-make simple Verilog->SystemC conversion - examples/make_tracing_c => Example GNU-make Verilog->C++ with tracing - examples/make_tracing_sc => Example GNU-make Verilog->SystemC with tracing - examples/make_protect_lib => Example using --protect-lib - examples/cmake_hello_c => Example building make_hello_c with CMake - examples/cmake_hello_sc => Example building make_hello_sc with CMake - examples/cmake_tracing_c => Example building make_tracing_c with CMake - examples/cmake_tracing_sc => Example building make_tracing_sc with CMake - examples/cmake_protect_lib => Example building make_protect_lib with CMake - include/ => Files that should be in your -I compiler path - include/verilated*.cpp => Global routines to link into your simulator - include/verilated*.h => Global headers - include/verilated.mk => Common Makefile - src/ => Translator source code - test_regress => Internal tests - -For files created after a design is Verilated, see the -https://verilator.org/verilator_doc.html[Verilator manual (HTML)], -or https://verilator.org/verilator_doc.pdf[Verilator manual (PDF)]. - -== License - -Copyright 2008-2021 by Wilson Snyder. Verilator 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 diff --git a/docs/install.rst b/docs/install.rst new file mode 100644 index 000000000..0a47b3822 --- /dev/null +++ b/docs/install.rst @@ -0,0 +1,397 @@ +|Logo| + +====================== +Verilator Installation +====================== + +.. contents:: + :depth: 3 + +Introduction +============ + +This discusses how to install Verilator. For more general information +please see `verilator.org `__. + + +Quick-start +=========== + + +Install From a Package Manager +------------------------------ + +Using a distribution's package manager is the easiest way to get +started. (Note packages are unlikely to have the most recent version, so +Git, below, maybe a better alternative.) To install as a package: + +:: + + apt-get install verilator + +If this works, skip down to `Running Verilator <#_running_verilator>`__. + + +Docker +------ + +Verilator is available in pre-built Docker containers. See +https://github.com/verilator/verilator/blob/master/ci/docker/run/README.rst + + +Git +--- + +Installing Verilator with Git provides the most flexibility. For +additional options and details see the additional sections below. In +brief: + +:: + + # Prerequisites: + #sudo apt-get install git make autoconf g++ flex bison + #sudo apt-get install libfl2 # Ubuntu only (ignore if gives error) + #sudo apt-get install libfl-dev # Ubuntu only (ignore if gives error) + + git clone https://github.com/verilator/verilator # Only first time + ## Note the URL above is not a page you can see with a browser, it's for git only + + # Every time you need to build: + unsetenv VERILATOR_ROOT # For csh; ignore error if on bash + unset VERILATOR_ROOT # For bash + cd verilator + git pull # Make sure git repository is up-to-date + git tag # See what versions exist + #git checkout master # Use development branch (e.g. recent bug fixes) + #git checkout stable # Use most recent stable release + #git checkout v{version} # Switch to specified release version + + autoconf # Create ./configure script + ./configure + make + sudo make install + # Now see "man verilator" or online verilator.pdf's for the example tutorials + +If this works, skip down to `Running Verilator <#_running_verilator>`__. + + +Detailed Build Instructions +=========================== + +This section describes details of the build process, and assumes you are +building from Git or a tarball. For using a pre-built binary for your +Linux distribution, see instead `Install From a Package +Manager <#_install_from_a_package_manager>`__. + + +OS Requirements +--------------- + +Verilator is developed and has primary testing on Ubuntu, with additional +testing on FreeBSD and Apple OS-X. Versions have also built on Redhat +Linux, HPUX and Solaris. It should run with minor porting on any +GNU/Linux-ish platform. Verilator also works on Windows under Cygwin, and +Windows under MinGW (gcc -mno-cygwin). Verilated output (not Verilator +itself) compiles under all the options above, plus MSVC++. + + +Install Prerequisites +--------------------- + +To build or run Verilator you need these standard packages: + +:: + + sudo apt-get install perl python3 make + sudo apt-get install g++ # Alternatively, clang + sudo apt-get install libgz # Non-Ubuntu (ignore if gives error) + sudo apt-get install libfl2 libfl-dev zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error) + +To build or run the following are optional but should be installed for good +performance: + +:: + + sudo apt-get install ccache # If present at build, needed for run + sudo apt-get install libgoogle-perftools-dev numactl perl-doc + +To build Verilator you will need to install these packages; these do not +need to be present to run Verilator: + +:: + + sudo apt-get install git autoconf flex bison + +Those developing Verilator itself may also want these (see internals.rst): + +:: + + sudo apt-get install gdb asciidoctor graphviz cmake clang clang-format gprof lcov + cpan install Pod::Perldoc + cpan install Parallel::Forker + + +Install SystemC +~~~~~~~~~~~~~~~ + +If you will be using SystemC (vs straight C++ output), download `SystemC +`__. Follow their +installation instructions. You will need to set ``SYSTEMC_INCLUDE`` to +point to the include directory with ``systemc.h`` in it, and +``SYSTEMC_LIBDIR`` to points to the directory with ``libsystemc.a`` in +it. (Older installations may set ``SYSTEMC`` and ``SYSTEMC_ARCH`` instead.) + + +Install GTKWave +~~~~~~~~~~~~~~~ + +To make use of Verilator FST tracing you will want `GTKwave +`__ installed, however this is not +required at Verilator build time. + + +Obtain Sources +-------------- + +You may use Git or a tarball for the sources. Git is the supported +option. (If using a historical build that uses a tarball, tarballs are +obtained from `Verilator Downloads +`__; we presume +you know how to use it, and is not described here.) + +Get the sources from the repository: (You need do this only once, ever.) + +:: + + git clone https://github.com/verilator/verilator # Only first time + ## Note the URL above is not a page you can see with a browser, it's for git only + +Enter the checkout and determine what version/branch to use: + +:: + + cd verilator + git pull # Make sure we're up-to-date + git tag # See what versions exist + #git checkout master # Use development branch (e.g. recent bug fix) + #git checkout stable # Use most recent release + #git checkout v{version} # Switch to specified release version + + +Auto Configure +-------------- + +Create the configuration script: + +:: + + autoconf # Create ./configure script + + +Eventual Installation Options +----------------------------- + +Before configuring the build, you have to decide how you're going to +eventually install the kit. Verilator will be compiling the current value +of ``VERILATOR_ROOT``, ``SYSTEMC_INCLUDE``, and ``SYSTEMC_LIBDIR`` as +defaults into the executable, so they must be correct before configuring. + +These are the options: + + +1. Run-in-Place from VERILATOR_ROOT +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +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. + +:: + + export VERILATOR_ROOT=`pwd` # if your shell is bash + setenv VERILATOR_ROOT `pwd` # if your shell is csh + ./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 ``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 CAD Disk +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You may eventually be installing onto a project/company-wide "CAD" tools +disk that may support multiple versions of every tool. Target the build to +a destination directory name that includes the Verilator version name: + +:: + + unset VERILATOR_ROOT # if your shell is bash + unsetenv VERILATOR_ROOT # if your shell is csh + # 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 +`__, you'll want a module file like the +following: + +**modulecmd's verilator/version file.** + +:: + + set install_root /CAD_DISK/verilator/{version-number-used-above} + unsetenv VERILATOR_ROOT + prepend-path PATH $install_root/bin + prepend-path MANPATH $install_root/man + 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 ``$PATH``. + + +4. Install System Globally +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The final option is to eventually install Verilator globally, using the +normal system paths: + +:: + + unset VERILATOR_ROOT # if your shell is bash + unsetenv VERILATOR_ROOT # if your shell is csh + ./configure + +Then after installing (below) the binary directories should already be +in your ``$PATH``. + + +Configure +--------- + +The command to configure the package was described in the previous step. +Developers should configure to have more complete developer tests. +Additional packages may be required for these tests. + +:: + + export VERILATOR_AUTHOR_SITE=1 # Put in your .bashrc + ./configure --enable-longtests ...above options... + + +Compile +------- + +Compile Verilator: + +:: + + make -j + + +Test +---- + +Check the compilation by running self-tests: + +:: + + make test + + +Install +------- + +If you used any but the `1. Run-in-Place from VERILATOR_ROOT +<#_1_run_in_place_from_verilator_root>`__ scheme, install to the +OS-standard place: + +:: + + make install + + +Running Verilator +================= + +To run Verilator, see the example sections in the `Verilator manual (HTML) +`__, or `Verilator manual (PDF) +`__. + +Also see the ``examples/`` directory that is part of the kit, and is +installed (in a OS-specific place, often in e.g. +``/usr/local/share/verilator/examples``). + +:: + + cd examples/make_hello_c + make + +Note if you did a ``make install`` above you should not have +``VERILATOR_ROOT`` set in your environment; it is built into the +executable. + + +Announcements +============= + +To get notified of new releases, go to `Verilator announcement repository +`__ and follow the +instructions there. + + +Directory Structure +=================== + +Some relevant files and directories in this package are as follows: + +:: + + Changes => Version history + README.rst => This document + bin/verilator => Compiler wrapper invoked to Verilate code + docs/ => Additional documentation + examples/make_hello_c => Example GNU-make simple Verilog->C++ conversion + examples/make_hello_sc => Example GNU-make simple Verilog->SystemC conversion + examples/make_tracing_c => Example GNU-make Verilog->C++ with tracing + examples/make_tracing_sc => Example GNU-make Verilog->SystemC with tracing + examples/make_protect_lib => Example using --protect-lib + examples/cmake_hello_c => Example building make_hello_c with CMake + examples/cmake_hello_sc => Example building make_hello_sc with CMake + examples/cmake_tracing_c => Example building make_tracing_c with CMake + examples/cmake_tracing_sc => Example building make_tracing_sc with CMake + examples/cmake_protect_lib => Example building make_protect_lib with CMake + include/ => Files that should be in your -I compiler path + include/verilated*.cpp => Global routines to link into your simulator + include/verilated*.h => Global headers + include/verilated.mk => Common Makefile + src/ => Translator source code + test_regress => Internal tests + +For files created after a design is Verilated, see the `Verilator manual +(HTML) `__, or `Verilator +manual (PDF) `__. + + +License +======= + +Copyright 2008-2021 by Wilson Snyder. Verilator 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. + +.. |Logo| image:: https://www.veripool.org/img/verilator_256_200_min.png diff --git a/docs/internals.adoc b/docs/internals.adoc deleted file mode 100644 index 0a2f64909..000000000 --- a/docs/internals.adoc +++ /dev/null @@ -1,1030 +0,0 @@ -= Verilator Internals Documentation -:toc: right - -// Github doesn't render unless absolute URL -image::https://www.veripool.org/img/verilator_256_200_min.png[Logo,256,200,role="right"] - -== Introduction - -This file discusses internal and programming details for Verilator. It's a -reference for developers and debugging problems. - -See also the Verilator internals presentation at https://www.veripool.org. - -== Code Flows - -=== Verilator Flow - -The main flow of Verilator can be followed by reading the Verilator.cpp -`process()` function: - -. First, the files specified on the command line are read. Reading -involves preprocessing, then lexical analysis with Flex and parsing with -Bison. This produces an abstract syntax tree (AST) representation of the -design, which is what is visible in the .tree files described below. - -. Verilator then makes a series of passes over the AST, progressively -refining and optimizing it. - -. Cells in the AST first linked, which will read and parse additional files -as above. - -. Functions, variable and other references are linked to their definitions. - -. Parameters are resolved and the design is elaborated. - -. Verilator then performs many additional edits and optimizations on the -hierarchical design. This includes coverage, assertions, X elimination, -inlining, constant propagation, and dead code elimination. - -. References in the design are then pseudo-flattened. Each module's -variables and functions get "Scope" references. A scope reference is an -occurrence of that un-flattened variable in the flattened hierarchy. A -module that occurs only once in the hierarchy will have a single scope and -single VarScope for each variable. A module that occurs twice will have a -scope for each occurrence, and two VarScopes for each variable. This -allows optimizations to proceed across the flattened design, while still -preserving the hierarchy. - -. Additional edits and optimizations proceed on the pseudo-flat design. -These include module references, function inlining, loop unrolling, -variable lifetime analysis, lookup table creation, always splitting, and -logic gate simplifications (pushing inverters, etc). - -. Verilator orders the code. Best case, this results in a single "eval" -function which has all always statements flowing from top to bottom with no -loops. - -. Verilator mostly removes the flattening, so that code may be shared -between multiple invocations of the same module. It localizes variables, -combines identical functions, expands macros to C primitives, adds branch -prediction hints, and performs additional constant propagation. - -. Verilator finally writes the C++ modules. - -=== Key Classes Used in the Verilator Flow - -==== `AstNode` - -The AST is represented at the top level by the class `AstNode`. This -abstract class has derived classes for the individual components -(e.g. `AstGenerate` for a generate block) or groups of components -(e.g. `AstNodeFTask` for functions and tasks, which in turn has `AstFunc` -and `AstTask` as derived classes). An important property of the `AstNode` -type hierarchy is that all non-final subclasses of `AstNode` (i.e.: those -which themselves have subclasses) must be abstract as well, and be named -with the prefix `AstNode*`. The `astgen` (see below) script relies on this. - -Each `AstNode` has pointers to up to four children, accessed by the `op1p` -through `op4p` methods. These methods are then abstracted in a specific -Ast* node class to a more specific name. For example with the `AstIf` node -(for `if` statements), `ifsp` calls `op2p` to give the pointer to the AST -for the "then" block, while `elsesp` calls `op3p` to give the pointer to -the AST for the "else" block, or NULL if there is not one. - -`AstNode` has the concept of a next and previous AST - for example the next -and previous statements in a block. Pointers to the AST for these -statements (if they exist) can be obtained using the `back` and `next` -methods. - -It is useful to remember that the derived class `AstNetlist` is at the top -of the tree, so checking for this class is the standard way to see if you -are at the top of the tree. - -By convention, each function/method uses the variable `nodep` as a pointer -to the `AstNode` currently being processed. - -==== `AstNVisitor` - -The passes are implemented by AST visitor classes. These are implemented -by subclasses of the abstract class, `AstNVisitor`. Each pass creates an -instance of the visitor class, which in turn implements a method to perform -the pass. - -==== `V3Graph` - -A number of passes use graph algorithms, and the class `V3Graph` is -provided to represent those graphs. Graphs are directed, and algorithms are -provided to manipulate the graphs and to output them in -https://www.graphviz.org[GraphViz] dot format. `V3Graph.h` provides -documentation of this class. - -==== `V3GraphVertex` - -`V3GraphVertex` is the base class for vertices in a graph. Vertices have -an associated `fanout`, `color` and `rank`, which may be used in algorithms -for ordering the graph. A generic `user`/`userp` member variable is also -provided. - -Virtual methods are provided to specify the name, color, shape and style to -be used in dot output. Typically users provide derived classes from -`V3GraphVertex` which will reimplement these methods. - -Iterators are provided to access in and out edges. Typically these are used -in the form: - - for (V3GraphEdge *edgep = vertexp->inBeginp(); - edgep; - edgep = edgep->inNextp()) { - -==== `V3GraphEdge` - -`V3GraphEdge` is the base class for directed edges between pairs of -vertices. Edges have an associated `weight` and may also be made -`cutable`. A generic `user`/`userp` member variable is also provided. - -Accessors, `fromp` and `top` return the "from" and "to" vertices -respectively. - -Virtual methods are provided to specify the label, color and style to be -used in dot output. Typically users provided derived classes from -`V3GraphEdge` which will reimplement these methods. - -==== `V3GraphAlg` - -This is the base class for graph algorithms. It implements a `bool` method, -`followEdge` which algorithms can use to decide whether an edge is -followed. This method returns true if the graph edge has weight greater -than one and a user function, `edgeFuncp` (supplied in the constructor) -returns `true`. - -A number of predefined derived algorithm classes and access methods are -provided and documented in `V3GraphAlg.cpp`. - -=== Multithreaded Mode - -In `--threads` mode, the frontend of the Verilator pipeline is the same as -serial mode, up until V3Order. - -`V3Order` builds a fine-grained, statement-level dependency graph that -governs the ordering of code within a single `eval()` call. In serial mode, -that dependency graph is used to order all statements into a total serial -order. In parallel mode, the same dependency graph is the starting point -for a partitioner (`V3Partition`). - -The partitioner's goal is to coarsen the fine-grained graph into a coarser -graph, while maintaining as much available parallelism as possible. Often -the partitioner can transform an input graph with millions of nodes into a -coarsened execution graph with a few dozen nodes, while maintaining enough -parallelism to take advantage of a modern multicore CPU. Runtime -synchronization cost is not prohibitive with so few nodes. - -==== Partitioning - -Our partitioner is similar to the one Vivek Sarkar described in his 1989 -paper _Partitioning and Scheduling Parallel Programs for Multiprocessors_. - -Let's define some terms: - -==== Par Factor - -The available parallelism or "par-factor" of a DAG is the total cost to -execute all nodes, divided by the cost to execute the longest critical path -through the graph. This is the speedup you would get from running the graph -in parallel, if given infinite CPU cores available and communication and -synchronization are zero. - -==== Macro Task - -When the partitioner coarsens the graph, it combines nodes together. Each -fine-grained node represents an atomic "task"; combined nodes in the -coarsened graph are "macro-tasks". This term comes from Sarkar. Each -macro-task executes from start to end on one processor, without any -synchronization to any other macro-task during its -execution. (Synchronization only happens before the macro-task begins or -after it ends.) - -==== Edge Contraction - -Verilator's partitioner, like Sarkar's, primarily relies on "edge -contraction" to coarsen the graph. It starts with one macro-task per atomic -task and iteratively combines pairs of edge-connected macro-tasks. - -==== Local Critical Path - -Each node in the graph has a "local" critical path. That's the critical -path from the start of the graph to the start of the node, plus the node's -cost, plus the critical path from the end of the node to the end of the -graph. - -Sarkar calls out an important trade-off: coarsening the graph reduces -runtime synchronization overhead among the macro-tasks, but it tends to -increase the critical path through the graph and thus reduces par-factor. - -Sarkar's partitioner, and ours, chooses pairs of macro-tasks to merge such -that the growth in critical path is minimized. Each candidate merge would -result in a new node, which would have some local critical path. We choose -the candidate that would produce the shortest local critical path. Repeat -until par-factor falls to a target threshold. It's a greedy algorithm, and -it's not guaranteed to produce the best partition (which Sarkar proves is -NP-hard). - -==== Estimating Logic Costs - -To compute the cost of any given path through the graph, Verilator -estimates an execution cost for each task. Each macro-task has an execution -cost which is simply the sum of its tasks' costs. We assume that -communication overhead and synchronization overhead are zero, so the cost -of any given path through the graph is simply the sum of macro-task -execution costs. Sarkar does almost the same thing, except that he has -nonzero estimates for synchronization costs. - -Verilator's cost estimates are assigned by `InstrCountCostVisitor`. This -class is perhaps the most fragile piece of the multithread implementation. -It's easy to have a bug where you count something cheap (eg. accessing one -element of a huge array) as if it were expensive (eg. by counting it as if -it were an access to the entire array.) Even without such gross bugs, the -estimates this produce are only loosely predictive of actual runtime cost. -Multithread performance would be better with better runtime costs -estimates. This is an area to improve. - -==== Scheduling Macro-Tasks at Runtime - -After coarsening the graph, we must schedule the macro-tasks for runtime. -Sarkar describes two options: you can dynamically schedule tasks at -runtime, with a runtime graph follower. Sarkar calls this the -"macro-dataflow model." Verilator does not support this; early experiments -with this approach had poor performance. - -The other option is to statically assign macro-tasks to threads, with each -thread running its macro-tasks in a static order. Sarkar describes this in -Chapter 5. Verilator takes this static approach. The only dynamic aspect is -that each macro task may block before starting, to wait until its -prerequisites on other threads have finished. - -The synchronization cost is cheap if the prereqs are done. If they're not, -fragmentation (idle CPU cores waiting) is possible. This is the major -source of overhead in this approach. The `--prof-threads` switch and the -`verilator_gantt` script can visualize the time lost to such fragmentation. - -==== Locating Variables for Best Spatial Locality - -After scheduling all code, we attempt to locate variables in memory such -that variables accessed by a single macro-task are close together in -memory. This provides "spatial locality" -- when we pull in a 64-byte -cache line to access a 2-byte variable, we want the other 62 bytes to be -ones we'll also likely access soon, for best cache performance. - -This turns out to be critical for performance. It should allow Verilator to -scale to very large models. We don't rely on our working set fitting in any -CPU cache; instead we essentially "stream" data into caches from -memory. It's not literally streaming, where the address increases -monotonically, but it should have similar performance characteristics, so -long as each macro-task's dataset fits in one core's local caches. - -To achieve spatial locality, we tag each variable with the set of -macro-tasks that access it. Let's call this set the "footprint" of that -variable. The variables in a given module have a set of footprints. We can -order those footprints to minimize the distance between them (distance is -the number of macro-tasks that are different across any two footprints) and -then emit all variables into the struct in ordered-footprint order. - -The footprint ordering is literally the traveling salesman problem, and we -use a TSP-approximation algorithm to get close to an optimal sort. - -This is an old idea. Simulators designed at DEC in the early 1990s used -similar techniques to optimize both single-thread and multi-thread modes. -(Verilator does not optimize variable placement for spatial locality in -serial mode; that is a possible area for improvement.) - -==== Improving Multithreaded Performance Further (a TODO list) - -===== Wave Scheduling - -To allow the Verilated model to run in parallel with the testbench, it -might be nice to support "wave" scheduling, in which work on a cycle begins -before `eval()` is called or continues after `eval()` returns. For now all -work on a cycle happens during the `eval()` call, leaving Verilator's -threads idle while the testbench (everything outside `eval()`) is -working. This would involve fundamental changes within the partitioner, -however, it's probably the best bet for hiding testbench latency. - -===== Efficient Dynamic Scheduling - -To scale to more than a few threads, we may revisit a fully dynamic -scheduler. For large (>16 core) systems it might make sense to dedicate an -entire core to scheduling, so that scheduler data structures would fit in -its L1 cache and thus the cost of traversing priority-ordered ready lists -would not be prohibitive. - -===== Static Scheduling with Runtime Repack - -We could modify the static scheduling approach by gathering actual -macro-task execution times at run time, and dynamically re-packing the -macro-tasks into the threads also at run time. Say, re-pack once every -10,000 cycles or something. This has the potential to do better than our -static estimates about macro-task run times. It could potentially react to -CPU cores that aren't performing equally, due to NUMA or thermal throttling -or nonuniform competing memory traffic or whatever. - -===== Clock Domain Balancing - -Right now Verilator makes no attempt to balance clock domains across -macro-tasks. For a multi-domain model, that could lead to bad gantt chart -fragmentation. This could be improved if it's a real problem in practice. - -===== Other Forms of MTask Balancing - -The largest source of runtime overhead is idle CPUs, which happens due to -variance between our predicted runtime for each MTask and its actual -runtime. That variance is magnified if MTasks are homogeneous, containing -similar repeating logic which was generally close together in source code -and which is still packed together even after going through Verilator's -digestive tract. - -If Verilator could avoid doing that, and instead would take source logic -that was close together and distribute it across MTasks, that would -increase the diversity of any given MTask, and this should reduce variance -in the cost estimates. - -One way to do that might be to make various "tie breaker" comparison -routines in the sources to rely more heavily on randomness, and generally -try harder not to keep input nodes together when we have the option to -scramble things. - -===== Performance Regression - -It would be nice if we had a regression of large designs, with some -diversity of design styles, to test on both single- and multi-threaded -modes. This would help to avoid performance regressions, and also to -evaluate the optimizations while minimizing the impact of parasitic noise. - -===== Per-Instance Classes - -If we have multiple instances of the same module, and they partition -differently (likely; we make no attempt to partition them the same) then -the variable sort will be suboptimal for either instance. A possible -improvement would be to emit a unique class for each instance of a module, -and sort its variables optimally for that instance's code stream. - -=== Verilated Flow - -The evaluation loop outputted by Verilator is designed to allow a single -function to perform evaluation under most situations. - -On the first evaluation, the Verilated code calls initial blocks, and then -"settles" the modules, by evaluating functions (from always statements) -until all signals are stable. - -On other evaluations, the Verilated code detects what input signals have -changes. If any are clocks, it calls the appropriate sequential functions -(from `always @ posedge` statements). Interspersed with sequential functions -it calls combo functions (from `always @*`). After this is complete, it -detects any changes due to combo loops or internally generated clocks, and -if one is found must reevaluate the model again. - -For SystemC code, the `eval()` function is wrapped in a SystemC -`SC_METHOD`, sensitive to all inputs. (Ideally it would only be sensitive -to clocks and combo inputs, but tracing requires all signals to cause -evaluation, and the performance difference is small.) - -If tracing is enabled, a callback examines all variables in the design for -changes, and writes the trace for each change. To accelerate this process -the evaluation process records a bitmask of variables that might have -changed; if clear, checking those signals for changes may be skipped. - -== Coding Conventions - -=== Compiler Version and C++11 - -Verilator requires C++11. Verilator does not require any newer versions, -but is maintained to build successfully with C++14/C++17/C++20. - -=== Indentation and Naming Style - -We will work with contributors to fix up indentation style issues, but it -is appreciated if you could match our style: - -* All files should contain the magic header to ensure standard indentation: -+ - // -*- mode: C++; c-file-style: "cc-mode" -*- -+ -This sets indentation to the `cc-mode` defaults. (Verilator predates a -CC-mode change of several years ago which overrides the defaults with GNU -style indentation; the `c-set-style` undoes that.) - -* Use "mixedCapsSymbols" instead of "underlined_symbols". - -* Uas a "p" suffix on variables that are pointers, e.g. "nodep". - -* Comment every member variable. - -Indentation is automatically maintained with "make format" using -clang-format version 10.0.0, and yapf for python, and is automatically -corrected in the CI actions. For those manually formatting C++ code: - -* Use 4 spaces per level, and no tabs. - -* Use 2 spaces between the end of source and the beginning of a comment. - -* Use 1 space after if/for/switch/while and similar keywords. - -* No spaces before semicolons, nor between a function's name and open -parenthesis (only applies to functions; if/else has a following space). - -=== The `astgen` Script - -Some of the code implementing passes is extremely repetitive, and must be -implemented for each sub-class of `AstNode`. However, while repetitive, -there is more variability than can be handled in C++ macros. - -In Verilator this is implemented by using a Perl script, `astgen` to -pre-process the C++ code. For example in `V3Const.cpp` this is used to -implement the `visit()` functions for each binary operation using the -`TREEOP` macro. - -The original C++ source code is transformed into C++ code in the `obj_opt` -and `obj_dbg` sub-directories (the former for the optimized version of -Verilator, the latter for the debug version). So for example `V3Const.cpp` -into `V3Const__gen.cpp`. - -=== Visitor Functions - -Verilator uses the "Visitor" design pattern to implement its refinement and -optimization passes. This allows separation of the pass algorithm from the -AST on which it operates. Wikipedia provides an introduction to the concept -at https://en.wikipedia.org/wiki/Visitor_pattern. - -As noted above, all visitors are derived classes of `AstNVisitor`. All -derived classes of `AstNode` implement the `accept` method, which takes as -argument a reference to an instance or a `AstNVisitor` derived class and -applies the visit method of the `AstNVisitor` to the invoking AstNode -instance (i.e. `this`). - -One possible difficulty is that a call to `accept` may perform an edit -which destroys the node it receives as argument. The -`acceptSubtreeReturnEdits` method of `AstNode` is provided to apply -`accept` and return the resulting node, even if the original node is -destroyed (if it is not destroyed it will just return the original node). - -The behavior of the visitor classes is achieved by overloading the `visit` -function for the different `AstNode` derived classes. If a specific -implementation is not found, the system will look in turn for overloaded -implementations up the inheritance hierarchy. For example calling `accept` -on `AstIf` will look in turn for: - - void visit(AstIf* nodep) - void visit(AstNodeIf* nodep) - void visit(AstNodeStmt* nodep) - void visit(AstNode* nodep) - -There are three ways data is passed between visitor functions. - -1. A visitor-class member variable. This is generally for passing "parent" -information down to children. `m_modp` is a common example. It's set to -NULL in the constructor, where that node (`AstModule` visitor) sets it, -then the children are iterated, then it's cleared. Children under an -`AstModule` will see it set, while nodes elsewhere will see it clear. If -there can be nested items (for example an `AstFor` under an `AstFor`) the -variable needs to be save-set-restored in the `AstFor` visitor, otherwise -exiting the lower for will lose the upper for's setting. - -2. User attributes. Each `AstNode` (*Note.* The AST node, not the visitor) -has five user attributes, which may be accessed as an integer using the -`user1()` through `user5()` methods, or as a pointer (of type `AstNUser`) -using the `user1p()` through `user5p()` methods (a common technique lifted -from graph traversal packages). -+ -A visitor first clears the one it wants to use by calling -`AstNode::user#ClearTree()`, then it can mark any node's `user#()` with whatever -data it wants. Readers just call `nodep->user()`, but may need to cast -appropriately, so you'll often see `VN_CAST(nodep->userp(), SOMETYPE)`. -At the top of each visitor are comments describing how the `user()` stuff -applies to that visitor class. For example: -+ - // NODE STATE - // Cleared entire netlist - // AstModule::user1p() // bool. True to inline this module -+ -This says that at the `AstNetlist` `user1ClearTree()` is called. Each -`AstModule`'s `user1()` is used to indicate if we're going to inline it. -+ -These comments are important to make sure a `user#()` on a given `AstNode` -type is never being used for two different purposes. -+ -Note that calling `user#ClearTree` is fast, it doesn't walk the tree, so -it's ok to call fairly often. For example, it's commonly called on every -module. - -3. Parameters can be passed between the visitors in close to the "normal" -function caller to callee way. This is the second `vup` parameter of type -`AstNUser` that is ignored on most of the visitor functions. V3Width does -this, but it proved more messy than the above and is deprecated. (V3Width -was nearly the first module written. Someday this scheme may be removed, -as it slows the program down to have to pass vup everywhere.) - -=== Iterators - -`AstNVisitor` provides a set of iterators to facilitate walking over the -tree. Each operates on the current `AstNVisitor` class (as this) and takes -an argument type `AstNode*`. - -`iterate`:: - -Applies the `accept` method of the `AstNode` to the visitor function. - -`iterateAndNextIgnoreEdit`:: - -Applies the `accept` method of each `AstNode` in a list (i.e. connected by -`nextp` and `backp` pointers). - -`iterateAndNextNull`:: - -Applies the `accept` method of each `AstNode` in a list, only if the -provided node is non-NULL. If a node is edited by the call to `accept`, -apply `accept` again, until the node does not change. - -`iterateListBackwards`:: - -Applies the `accept` method of each `AstNode` in a list, starting with the -last one. - -`iterateChildren`:: - -Applies the `iterateAndNextNull` method on each child `op1p` through `op4p` -in turn. - -`iterateChildrenBackwards`:: - -Applies the `iterateListBackwards` method on each child `op1p` through -`op4p` in turn. - -==== Caution on Using Iterators When Child Changes - -Visitors often replace one node with another node; V3Width and V3Const are -major examples. A visitor which is the parent of such a replacement needs -to be aware that calling iteration may cause the children to change. For -example: - - // nodep->lhsp() is 0x1234000 - iterateAndNextNull(nodep->lhsp()); // and under covers nodep->lhsp() changes - // nodep->lhsp() is 0x5678400 - iterateAndNextNull(nodep->lhsp()); - -Will work fine, as even if the first iterate causes a new node to take the -place of the `lhsp()`, that edit will update `nodep->lhsp()` and the second -call will correctly see the change. Alternatively: - - lp = nodep->lhsp(); - // nodep->lhsp() is 0x1234000, lp is 0x1234000 - iterateAndNextNull(lp); **lhsp=NULL;** // and under covers nodep->lhsp() changes - // nodep->lhsp() is 0x5678400, lp is 0x1234000 - iterateAndNextNull(lp); - -This will cause bugs or a core dump, as lp is a dangling pointer. Thus it -is advisable to set lhsp=NULL shown in the *'s above to make sure these -dangles are avoided. Another alternative used in special cases mostly in -V3Width is to use acceptSubtreeReturnEdits, which operates on a single node -and returns the new pointer if any. Note acceptSubtreeReturnEdits does not -follow `nextp()` links. - - lp = acceptSubtreeReturnEdits(lp) - -=== Identifying Derived Classes - -A common requirement is to identify the specific `AstNode` class we are -dealing with. For example a visitor might not implement separate `visit` -methods for `AstIf` and `AstGenIf`, but just a single method for the base -class: - - void visit(AstNodeIf* nodep) - -However that method might want to specify additional code if it is called -for `AstGenIf`. Verilator does this by providing a `VN_IS` method for each -possible node type, which returns true if the node is of that type (or -derived from that type). So our `visit` method could use: - - if (VN_IS(nodep, AstGenIf) { - - } - -Additionally the `VN_CAST` method converts pointers similar to C++ -`dynamic_cast`. This either returns a pointer to the object cast to that -type (if it is of class `SOMETYPE`, or a derived class of `SOMETYPE`) or -else NULL. (However, for true/false tests use `VN_IS` as that is faster.) - -== Testing - -For an overview of how to write a test see the BUGS section of the -Verilator primary manual. - -It is important to add tests for failures as well as success (for example -to check that an error message is correctly triggered). - -Tests that fail should by convention have the suffix `_bad` in their name, -and include `fails = 1` in either their `compile` or `execute` step as -appropriate. - -=== Preparing to Run Tests - -For all tests to pass you must install the following packages: - -* SystemC to compile the SystemC outputs, see http://systemc.org - -* Parallel::Forker from CPAN to run tests in parallel, you can install this -with e.g. "sudo cpan install Parallel::Forker". - -* vcddiff to find differences in VCD outputs. See the readme at -https://github.com/veripool/vcddiff - -* Cmake for build paths that use it. - -=== Controlling the Test Driver - -Test drivers are written in PERL. All invoke the main test driver script, -which can provide detailed help on all the features available when writing -a test driver. - - test_regress/driver.pl --help - -For convenience, a summary of the most commonly used features is provided -here. All drivers require a call to `compile` subroutine to compile the -test. For run-time tests, this is followed by a call to the `execute` -subroutine. Both of these functions can optionally be provided with a hash -table as argument specifying additional options. - -The test driver assumes by default that the source Verilog file name -matches the PERL driver name. So a test whose driver is `t/t_mytest.pl` -will expect a Verilog source file `t/t_mytest.v`. This can be changed -using the `top_filename` subroutine, for example - - top_filename("t/t_myothertest.v"); - -By default all tests will run with major simulators (Icarus Verilog, NC, -VCS, ModelSim, etc) as well as Verilator, to allow results to be -compared. However if you wish a test only to be used with Verilator, you -can use the following: - - scenarios(vlt => 1); - -Of the many options that can be set through arguments to `compiler` and -`execute`, the following are particularly useful: - -`verilator_flags2`:: - -A list of flags to be passed to verilator when compiling. - -`fails`:: - -Set to 1 to indicate that the compilation or execution is intended to fail. - -For example the following would specify that compilation requires two -defines and is expected to fail. - - compile( - verilator_flags2 => ["-DSMALL_CLOCK -DGATED_COMMENT"], - fails => 1, - ); - -=== Regression Testing for Developers - -Developers will also want to call ./configure with two extra flags: - -`--enable-ccwarn`:: - -Causes the build to stop on warnings as well as errors. A good way to -ensure no sloppy code gets added, however it can be painful when it comes -to testing, since third party code used in the tests (e.g. SystemC) may not -be warning free. - -`--enable-longtests`:: - -In addition to the standard C, SystemC examples, also run the tests in the -`test_regress` directory when using _make test_'. This is disabled by -default as SystemC installation problems would otherwise falsely indicate a -Verilator problem. - -When enabling the long tests, some additional PERL modules are needed, -which you can install using cpan. - - cpan install Parallel::Forker - -There are some traps to avoid when running regression tests - -* When checking the MANIFEST, the test will barf on unexpected code in the -Verilator tree. So make sure to keep any such code outside the tree. - -* Not all Linux systems install Perldoc by default. This is needed for the -`--help_' option to Verilator, and also for regression testing. This can be -installed using cpan: -+ - cpan install Pod::Perldoc -+ -Many Linux systems also offer a standard package for this. Red -Hat/Fedora/Centos offer _perl-Pod-Perldoc_', while Debian/Ubuntu/Linux Mint -offer `perl-doc'. - -* Running regression may exhaust resources on some Linux systems, -particularly file handles and user processes. Increase these to -respectively 16,384 and 4,096. The method of doing this is system -dependent, but on Fedora Linux it would require editing the -`/etc/security/limits.conf` file as root. - -=== Manual Test Execution - -A specific regression test can be executed manually. To start the "EXAMPLE" -test, run the following command. - - test_regress/t/t_EXAMPLE.pl - -=== Continuous Integration - -Verilator uses GitHub Actions which automatically tests the master branch -for test failures on new commits. It also runs a daily cron job to validate -all of the tests against different OS and compiler versions. - -Developers can enable Actions on their GitHub repository so that the CI -environment can check their branches too by enabling the build workflow: - -* On GitHub, navigate to the main page of the repository. -* Under your repository name, click Actions. -* In the left sidebar, click the workflow you want to enable ("build"). -* Click Enable workflow. - -=== Fuzzing - -There are scripts included to facilitate fuzzing of Verilator. These have -been successfully used to find a number of bugs in the frontend. - -The scripts are based on using http://lcamtuf.coredump.cx/afl/[American fuzzy lop] -on a Debian-like system. - -To get started, cd to "nodist/fuzzer/" and run "./all". A sudo password -may be required to setup the system for fuzzing. - -== Debugging - -=== Debug Levels - -The "UINFO" calls in the source indicate a debug level. Messages level 3 -and below are globally enabled with `--debug`. Higher levels may be -controlled with `--debugi `. An individual source file levels may -be controlled with `-debugi- `. For example `--debug ---debugi 5 --debugi-V3Width 9` will use the debug binary at default 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. - -=== .dot Output - -Dot files are dumps of internal graphs in -https://www.graphviz.org[Graphviz] dot format. When a dot file is dumped, -Verilator will also print a line on stdout that can be used to format the -output, for example: - - dot -Tps -o ~/a.ps obj_dir/Vtop_foo.dot - -You can then print a.ps. You may prefer gif format, which doesn't get -scaled so can be more useful with large graphs. - -For interactive graph viewing consider -https://github.com/jrfonseca/xdot.py[xdot] or -http://zvtm.sourceforge.net/zgrviewer.html[ZGRViewer]. If you know of -better viewers (especially for large graphs) please let us know. - -=== .tree Output - -Tree files are dumps of the AST Tree and are produced between every major -algorithmic stage. An example: - - NETLIST 0x90fb00 {a0ah} - 1: MODULE 0x912b20 {a8ah} top L2 [P] - *1:2: VAR 0x91a780 {a22ah} @dt=0xa2e640(w32) out_wide [O] WIRE - 1:2:1: BASICDTYPE 0xa2e640 {e24ah} @dt=this(sw32) integer kwd=integer range=[31:0] - -The following summarizes the above example dump, with more detail on each -field in the section below. - -[cols="20%,80%"] -|=== - -|`1:2:` | indicates the hierarchy of the `VAR` is the `op2p` pointer under -the `MODULE`, which in turn is the `op1p` pointer under the `NETLIST` - -|`VAR` | is the AstNodeType. - -| `0x91a780` | is the address of this node. - -| `` | means the 74th edit to the netlist was the last modification to -this node. - -| `{a22ah}` | indicates this node is related to the source filename "a", -where "a" is the first file read, "z" the 26th, and "aa" the 27th. Then -line 22 in that file, then column 8 (aa=0, az=25, ba=26, ...). - -| `@dt=0x...` | indicates the address of the data type this node contains. - -| `w32` | indicates the width is 32 bits. - -| `out_wide` | is the name of the node, in this case the name of the variable. - -| `[O]` | are flags which vary with the type of node, in this case it means the -variable is an output. - -|=== - -In more detail the following fields are dumped common to all nodes. They -are produced by the `AstNode::dump()` method: - -Tree Hierarchy:: - -The dump lines begin with numbers and colons to indicate the child node -hierarchy. As noted above, `AstNode` has lists of items at the same level -in the AST, connected by the `nextp()` and `prevp()` pointers. These appear -as nodes at the same level. For example after inlining: - - NETLIST 0x929c1c8 {a0} w0 - 1: MODULE 0x92bac80 {e14} w0 TOP_t L1 [P] - 1:1: CELLINLINE 0x92bab18 {e14} w0 v -> t - 1:1: CELLINLINE 0x92bc1d8 {e24} w0 v__DOT__i_test_gen -> test_gen - ... - 1: MODULE 0x92b9bb0 {e47} w0 test_gen L3 - ... - -AstNode type:: - -The textual name of this node AST type (always in capitals). Many of these -correspond directly to Verilog entities (for example `MODULE` and -`TASK`), but others are internal to Verilator (for example `NETLIST` and -`BASICDTYPE`). - -Address of the node:: - -A hexadecimal address of the node in memory. Useful for examining with the -debugger. If the actual address values are not important, then using the -`--dump-tree-addrids` option will convert address values to short identifiers -of the form `([A-Z]*)`, which is hopefully easier for the reader to cross -reference throughout the dump. - -Last edit number:: - -Of the form `` or `` , where `nnnn` is the number -of the last edit to modify this node. The trailing `#` indicates the node -has been edited since the last tree dump (which typically means in the last -refinement or optimization pass). GDB can watch for this, see << /Debugging >>. - -Source file and line:: - -Of the form `{xxnnnn}`, where C{xx} is the filename letter (or -letters) and `nnnn` is the line number within that file. The first file is -`a`, the 26th is `z`, the 27th is `aa` and so on. - -User pointers:: - -Shows the value of the node's user1p...user5p, if non-NULL. - -Data type:: - -Many nodes have an explicit data type. "@dt=0x..." indicates the address -of the data type (AstNodeDType) this node uses. -+ -If a data type is present and is numeric, it then prints the width of the -item. This field is a sequence of flag characters and width data as follows: -+ -* `s` if the node is signed. -* `d` if the node is a double (i.e a floating point entity). -* `w` always present, indicating this is the width field. -* `u` if the node is unsized. -* `/nnnn` if the node is unsized, where `nnnn` is the minimum width. - -Name of the entity represented by the node if it exists:: - -For example for a `VAR` it is the name of the variable. - -Many nodes follow these fields with additional node specific -information. Thus the `VARREF` node will print either `[LV]` or `[RV]` -to indicate a left value or right value, followed by the node of the -variable being referred to. For example: - - 1:2:1:1: VARREF 0x92c2598 {e24} w0 clk [RV] <- VAR 0x92a2e90 {e18} w0 clk [I] INPUT - -In general, examine the `dump()` method in `V3AstNodes.cpp` of the node -type in question to determine additional fields that may be printed. - -The `MODULE` has a list of `CELLINLINE` nodes referred to by its -`op1p()` pointer, connected by `nextp()` and `prevp()` pointers. - -Similarly the `NETLIST` has a list of modules referred to by its `op1p()` -pointer. - -=== Debugging with GDB - -The test_regress/driver.pl script accepts `--debug --gdb` to start -Verilator under gdb and break when an error is hit or the program is about -to exit. You can also use `--debug --gdbbt` to just backtrace and then -exit gdb. To debug the Verilated executable, use `--gdbsim`. - -If you wish to start Verilator under GDB (or another debugger), then you -can use `--debug` and look at the underlying invocation of -`verilator_dbg`. For example - - t/t_alw_dly.pl --debug - -shows it invokes the command: - - ../verilator_bin_dbg --prefix Vt_alw_dly --x-assign unique --debug - -cc -Mdir obj_dir/t_alw_dly --debug-check -f input.vc t/t_alw_dly.v - -Start GDB, then `start` with the remaining arguments. - - gdb ../verilator_bin_dbg - ... - (gdb) start --prefix Vt_alw_dly --x-assign unique --debug -cc -Mdir - obj_dir/t_alw_dly --debug-check -f input.vc t/t_alw_dly.v - > obj_dir/t_alw_dly/vlt_compile.log - ... - Temporary breakpoint 1, main (argc=13, argv=0xbfffefa4, env=0xbfffefdc) - at ../Verilator.cpp:615 - 615 ios::sync_with_stdio(); - (gdb) - -You can then continue execution with breakpoints as required. - -To break at a specific edit number which changed a node (presumably to find -what made a line in the tree dumps): - - watch AstNode::s_editCntGbl==#### - -Then, when the watch fires, to break at every following change to that node: - - watch m_editCount - -To print a node: - - pn nodep - # or: call dumpGdb(nodep) # aliased to "pn" in src/.gdbinit - pnt nodep - # or: call dumpTreeGdb(nodep) # aliased to "pnt" in src/.gdbinit - -When GDB halts, it is useful to understand that the backtrace will commonly -show the iterator functions between each invocation of `visit` in the -backtrace. You will typically see a frame sequence something like - - ... - visit() - iterateChildren() - iterateAndNext() - accept() - visit() - ... - -== Adding a New Feature - -Generally what would you do to add a new feature? - -. File an issue (if there isn't already) so others know what you're working on. - -. Make a testcase in the test_regress/t/t_EXAMPLE format, see << /TESTING >>. - -. If grammar changes are needed, look at the git version of VerilogPerl's -src/VParseGrammar.y, as this grammar supports the full SystemVerilog -language and has a lot of back-and-forth with Verilator's grammar. Copy -the appropriate rules to src/verilog.y and modify the productions. - -. If a new Ast type is needed, add it to V3AstNodes.h. Follow the convention -described above about the AstNode type hierarchy. - -. Now you can run "test_regress/t/t_{new testcase}.pl --debug" and it'll -probably fail but you'll see a test_regress/obj_dir/t_{newtestcase}/*.tree -file which you can examine to see if the parsing worked. See also the -sections above on debugging. - -. Modify the later visitor functions to process the new feature as needed. - -=== Adding a New Pass - -For more substantial changes you may need to add a new pass. The simplest -way to do this is to copy the `.cpp` and `.h` files from an existing -pass. You'll need to add a call into your pass from the `process()` -function in `src/verilator.cpp`. - -To get your pass to build you'll need to add its binary filename to the -list in `src/Makefile_obj.in` and reconfigure. - -=== "Never" features - -Verilator ideally would support all of IEEE, and has the goal to get close -to full support. However the following IEEE sections and features are not -anticipated to be ever implemented for the reasons indicated. - -[horizontal] -IEEE 1800-2017 3.3 recursive modules:: Little/no tool support, and arguably not a good practice. -IEEE 1800-2017 6.12 "shortreal":: Little/no tool support, and easily simply promoted to real. -IEEE 1800-2017 11.11 Min, typ, max:: No SDF support so will always use typical. -IEEE 1800-2017 11.12 "let":: Little/no tool support, makes difficult to implement parsers. -IEEE 1800-2017 20.15 Probabilistic functions:: Little industry use. -IEEE 1800-2017 20.16 Stochastic analysis:: Little industry use. -IEEE 1800-2017 20.17 PLA modeling:: Little industry use and outdated technology. -IEEE 1800-2017 31 Timing checks:: No longer relevant with static timing analysis tools. -IEEE 1800-2017 32 SDF annotation:: No longer relevant with static timing analysis tools. -IEEE 1800-2017 33 Config:: Little/no tool support or industry use. - -== Distribution - -Copyright 2008-2021 by Wilson Snyder. Verilator 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 diff --git a/docs/internals.rst b/docs/internals.rst new file mode 100644 index 000000000..496598fb4 --- /dev/null +++ b/docs/internals.rst @@ -0,0 +1,1213 @@ +|Logo| + +=================== +Verilator Internals +=================== + +.. contents:: + :depth: 3 + +Introduction +============ + +This file discusses internal and programming details for Verilator. It's +a reference for developers and debugging problems. + +See also the Verilator internals presentation at +https://www.veripool.org. + + +Code Flows +========== + + +Verilator Flow +-------------- + +The main flow of Verilator can be followed by reading the Verilator.cpp +``process()`` function: + +1. First, the files specified on the command line are read. Reading + involves preprocessing, then lexical analysis with Flex and parsing + with Bison. This produces an abstract syntax tree (AST) + representation of the design, which is what is visible in the .tree + files described below. + +2. Verilator then makes a series of passes over the AST, progressively + refining and optimizing it. + +3. Cells in the AST first linked, which will read and parse additional + files as above. + +4. Functions, variable and other references are linked to their + definitions. + +5. Parameters are resolved and the design is elaborated. + +6. Verilator then performs many additional edits and optimizations on + the hierarchical design. This includes coverage, assertions, X + elimination, inlining, constant propagation, and dead code + elimination. + +7. References in the design are then pseudo-flattened. Each module's + variables and functions get "Scope" references. A scope reference is + an occurrence of that un-flattened variable in the flattened + hierarchy. A module that occurs only once in the hierarchy will have + a single scope and single VarScope for each variable. A module that + occurs twice will have a scope for each occurrence, and two + VarScopes for each variable. This allows optimizations to proceed + across the flattened design, while still preserving the hierarchy. + +8. Additional edits and optimizations proceed on the pseudo-flat + design. These include module references, function inlining, loop + unrolling, variable lifetime analysis, lookup table creation, always + splitting, and logic gate simplifications (pushing inverters, etc). + +9. Verilator orders the code. Best case, this results in a single + "eval" function which has all always statements flowing from top to + bottom with no loops. + +10. Verilator mostly removes the flattening, so that code may be shared + between multiple invocations of the same module. It localizes + variables, combines identical functions, expands macros to C + primitives, adds branch prediction hints, and performs additional + constant propagation. + +11. Verilator finally writes the C++ modules. + + +Key Classes Used in the Verilator Flow +-------------------------------------- + + +``AstNode`` +~~~~~~~~~~~ + +The AST is represented at the top level by the class ``AstNode``. This +abstract class has derived classes for the individual components (e.g. +``AstGenerate`` for a generate block) or groups of components (e.g. +``AstNodeFTask`` for functions and tasks, which in turn has ``AstFunc`` and +``AstTask`` as derived classes). An important property of the ``AstNode`` +type hierarchy is that all non-final subclasses of ``AstNode`` (i.e.: those +which themselves have subclasses) must be abstract as well, and be named +with the prefix ``AstNode*``. The ``astgen`` (see below) script relies on +this. + +Each ``AstNode`` has pointers to up to four children, accessed by the +``op1p`` through ``op4p`` methods. These methods are then abstracted in a +specific Ast\* node class to a more specific name. For example with the +``AstIf`` node (for ``if`` statements), ``ifsp`` calls ``op2p`` to give the +pointer to the AST for the "then" block, while ``elsesp`` calls ``op3p`` to +give the pointer to the AST for the "else" block, or NULL if there is not +one. + +``AstNode`` has the concept of a next and previous AST - for example the +next and previous statements in a block. Pointers to the AST for these +statements (if they exist) can be obtained using the ``back`` and ``next`` +methods. + +It is useful to remember that the derived class ``AstNetlist`` is at the +top of the tree, so checking for this class is the standard way to see if +you are at the top of the tree. + +By convention, each function/method uses the variable ``nodep`` as a +pointer to the ``AstNode`` currently being processed. + + +``AstNVisitor`` +~~~~~~~~~~~~~~~ + +The passes are implemented by AST visitor classes. These are implemented by +subclasses of the abstract class, ``AstNVisitor``. Each pass creates an +instance of the visitor class, which in turn implements a method to perform +the pass. + + +``V3Graph`` +~~~~~~~~~~~ + +A number of passes use graph algorithms, and the class ``V3Graph`` is +provided to represent those graphs. Graphs are directed, and algorithms are +provided to manipulate the graphs and to output them in `GraphViz +`__ dot format. ``V3Graph.h`` provides +documentation of this class. + + +``V3GraphVertex`` +~~~~~~~~~~~~~~~~~ + +``V3GraphVertex`` is the base class for vertices in a graph. Vertices have +an associated ``fanout``, ``color`` and ``rank``, which may be used in +algorithms for ordering the graph. A generic ``user``/``userp`` member +variable is also provided. + +Virtual methods are provided to specify the name, color, shape and style to +be used in dot output. Typically users provide derived classes from +``V3GraphVertex`` which will reimplement these methods. + +Iterators are provided to access in and out edges. Typically these are used +in the form: + +:: + + for (V3GraphEdge *edgep = vertexp->inBeginp(); + edgep; + edgep = edgep->inNextp()) { + + +``V3GraphEdge`` +~~~~~~~~~~~~~~~ + +``V3GraphEdge`` is the base class for directed edges between pairs of +vertices. Edges have an associated ``weight`` and may also be made +``cutable``. A generic ``user``/``userp`` member variable is also provided. + +Accessors, ``fromp`` and ``top`` return the "from" and "to" vertices +respectively. + +Virtual methods are provided to specify the label, color and style to be +used in dot output. Typically users provided derived classes from +``V3GraphEdge`` which will reimplement these methods. + + +``V3GraphAlg`` +~~~~~~~~~~~~~~ + +This is the base class for graph algorithms. It implements a ``bool`` +method, ``followEdge`` which algorithms can use to decide whether an edge +is followed. This method returns true if the graph edge has weight greater +than one and a user function, ``edgeFuncp`` (supplied in the constructor) +returns ``true``. + +A number of predefined derived algorithm classes and access methods are +provided and documented in ``V3GraphAlg.cpp``. + + +Multithreaded Mode +------------------ + +In ``--threads`` mode, the frontend of the Verilator pipeline is the same +as serial mode, up until V3Order. + +``V3Order`` builds a fine-grained, statement-level dependency graph that +governs the ordering of code within a single ``eval()`` call. In serial +mode, that dependency graph is used to order all statements into a total +serial order. In parallel mode, the same dependency graph is the starting +point for a partitioner (``V3Partition``). + +The partitioner's goal is to coarsen the fine-grained graph into a coarser +graph, while maintaining as much available parallelism as possible. Often +the partitioner can transform an input graph with millions of nodes into a +coarsened execution graph with a few dozen nodes, while maintaining enough +parallelism to take advantage of a modern multicore CPU. Runtime +synchronization cost is not prohibitive with so few nodes. + + +Partitioning +~~~~~~~~~~~~ + +Our partitioner is similar to the one Vivek Sarkar described in his 1989 +paper *Partitioning and Scheduling Parallel Programs for Multiprocessors*. + +Let's define some terms: + + +Par Factor +~~~~~~~~~~ + +The available parallelism or "par-factor" of a DAG is the total cost to +execute all nodes, divided by the cost to execute the longest critical path +through the graph. This is the speedup you would get from running the graph +in parallel, if given infinite CPU cores available and communication and +synchronization are zero. + + +Macro Task +~~~~~~~~~~ + +When the partitioner coarsens the graph, it combines nodes together. Each +fine-grained node represents an atomic "task"; combined nodes in the +coarsened graph are "macro-tasks". This term comes from Sarkar. Each +macro-task executes from start to end on one processor, without any +synchronization to any other macro-task during its execution. +(Synchronization only happens before the macro-task begins or after it +ends.) + + +Edge Contraction +~~~~~~~~~~~~~~~~ + +Verilator's partitioner, like Sarkar's, primarily relies on "edge +contraction" to coarsen the graph. It starts with one macro-task per atomic +task and iteratively combines pairs of edge-connected macro-tasks. + + +Local Critical Path +~~~~~~~~~~~~~~~~~~~ + +Each node in the graph has a "local" critical path. That's the critical +path from the start of the graph to the start of the node, plus the node's +cost, plus the critical path from the end of the node to the end of the +graph. + +Sarkar calls out an important trade-off: coarsening the graph reduces +runtime synchronization overhead among the macro-tasks, but it tends to +increase the critical path through the graph and thus reduces par-factor. + +Sarkar's partitioner, and ours, chooses pairs of macro-tasks to merge such +that the growth in critical path is minimized. Each candidate merge would +result in a new node, which would have some local critical path. We choose +the candidate that would produce the shortest local critical path. Repeat +until par-factor falls to a target threshold. It's a greedy algorithm, and +it's not guaranteed to produce the best partition (which Sarkar proves is +NP-hard). + + +Estimating Logic Costs ~~~~~~~~~~~~~~~~~~~~~~ + +To compute the cost of any given path through the graph, Verilator +estimates an execution cost for each task. Each macro-task has an execution +cost which is simply the sum of its tasks' costs. We assume that +communication overhead and synchronization overhead are zero, so the cost +of any given path through the graph is simply the sum of macro-task +execution costs. Sarkar does almost the same thing, except that he has +nonzero estimates for synchronization costs. + +Verilator's cost estimates are assigned by ``InstrCountCostVisitor``. This +class is perhaps the most fragile piece of the multithread +implementation. It's easy to have a bug where you count something cheap +(eg. accessing one element of a huge array) as if it were expensive (eg. +by counting it as if it were an access to the entire array.) Even without +such gross bugs, the estimates this produce are only loosely predictive of +actual runtime cost. Multithread performance would be better with better +runtime costs estimates. This is an area to improve. + + +Scheduling Macro-Tasks at Runtime +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +After coarsening the graph, we must schedule the macro-tasks for +runtime. Sarkar describes two options: you can dynamically schedule tasks +at runtime, with a runtime graph follower. Sarkar calls this the +"macro-dataflow model." Verilator does not support this; early experiments +with this approach had poor performance. + +The other option is to statically assign macro-tasks to threads, with each +thread running its macro-tasks in a static order. Sarkar describes this in +Chapter 5. Verilator takes this static approach. The only dynamic aspect is +that each macro task may block before starting, to wait until its +prerequisites on other threads have finished. + +The synchronization cost is cheap if the prereqs are done. If they're not, +fragmentation (idle CPU cores waiting) is possible. This is the major +source of overhead in this approach. The ``--prof-threads`` switch and the +``verilator_gantt`` script can visualize the time lost to such +fragmentation. + + +Locating Variables for Best Spatial Locality +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +After scheduling all code, we attempt to locate variables in memory such +that variables accessed by a single macro-task are close together in +memory. This provides "spatial locality" - when we pull in a 64-byte cache +line to access a 2-byte variable, we want the other 62 bytes to be ones +we'll also likely access soon, for best cache performance. + +This turns out to be critical for performance. It should allow Verilator +to scale to very large models. We don't rely on our working set fitting +in any CPU cache; instead we essentially "stream" data into caches from +memory. It's not literally streaming, where the address increases +monotonically, but it should have similar performance characteristics, +so long as each macro-task's dataset fits in one core's local caches. + +To achieve spatial locality, we tag each variable with the set of +macro-tasks that access it. Let's call this set the "footprint" of that +variable. The variables in a given module have a set of footprints. We +can order those footprints to minimize the distance between them +(distance is the number of macro-tasks that are different across any two +footprints) and then emit all variables into the struct in +ordered-footprint order. + +The footprint ordering is literally the traveling salesman problem, and +we use a TSP-approximation algorithm to get close to an optimal sort. + +This is an old idea. Simulators designed at DEC in the early 1990s used +similar techniques to optimize both single-thread and multi-thread +modes. (Verilator does not optimize variable placement for spatial +locality in serial mode; that is a possible area for improvement.) + + +Improving Multithreaded Performance Further (a TODO list) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +Wave Scheduling +^^^^^^^^^^^^^^^ + +To allow the Verilated model to run in parallel with the testbench, it +might be nice to support "wave" scheduling, in which work on a cycle begins +before ``eval()`` is called or continues after ``eval()`` returns. For now +all work on a cycle happens during the ``eval()`` call, leaving Verilator's +threads idle while the testbench (everything outside ``eval()``) is +working. This would involve fundamental changes within the partitioner, +however, it's probably the best bet for hiding testbench latency. + + +Efficient Dynamic Scheduling +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To scale to more than a few threads, we may revisit a fully dynamic +scheduler. For large (>16 core) systems it might make sense to dedicate an +entire core to scheduling, so that scheduler data structures would fit in +its L1 cache and thus the cost of traversing priority-ordered ready lists +would not be prohibitive. + + +Static Scheduling with Runtime Repack +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We could modify the static scheduling approach by gathering actual +macro-task execution times at run time, and dynamically re-packing the +macro-tasks into the threads also at run time. Say, re-pack once every +10,000 cycles or something. This has the potential to do better than our +static estimates about macro-task run times. It could potentially react to +CPU cores that aren't performing equally, due to NUMA or thermal throttling +or nonuniform competing memory traffic or whatever. + + +Clock Domain Balancing +^^^^^^^^^^^^^^^^^^^^^^ + +Right now Verilator makes no attempt to balance clock domains across +macro-tasks. For a multi-domain model, that could lead to bad gantt chart +fragmentation. This could be improved if it's a real problem in practice. + + +Other Forms of MTask Balancing +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The largest source of runtime overhead is idle CPUs, which happens due to +variance between our predicted runtime for each MTask and its actual +runtime. That variance is magnified if MTasks are homogeneous, containing +similar repeating logic which was generally close together in source code +and which is still packed together even after going through Verilator's +digestive tract. + +If Verilator could avoid doing that, and instead would take source logic +that was close together and distribute it across MTasks, that would +increase the diversity of any given MTask, and this should reduce variance +in the cost estimates. + +One way to do that might be to make various "tie breaker" comparison +routines in the sources to rely more heavily on randomness, and +generally try harder not to keep input nodes together when we have the +option to scramble things. + + +Performance Regression +^^^^^^^^^^^^^^^^^^^^^^ + +It would be nice if we had a regression of large designs, with some +diversity of design styles, to test on both single- and multi-threaded +modes. This would help to avoid performance regressions, and also to +evaluate the optimizations while minimizing the impact of parasitic noise. + + +Per-Instance Classes +^^^^^^^^^^^^^^^^^^^^ + +If we have multiple instances of the same module, and they partition +differently (likely; we make no attempt to partition them the same) then +the variable sort will be suboptimal for either instance. A possible +improvement would be to emit a unique class for each instance of a module, +and sort its variables optimally for that instance's code stream. + + +Verilated Flow +-------------- + +The evaluation loop outputted by Verilator is designed to allow a single +function to perform evaluation under most situations. + +On the first evaluation, the Verilated code calls initial blocks, and then +"settles" the modules, by evaluating functions (from always statements) +until all signals are stable. + +On other evaluations, the Verilated code detects what input signals have +changes. If any are clocks, it calls the appropriate sequential functions +(from ``always @ posedge`` statements). Interspersed with sequential +functions it calls combo functions (from ``always @*``). After this is +complete, it detects any changes due to combo loops or internally generated +clocks, and if one is found must reevaluate the model again. + +For SystemC code, the ``eval()`` function is wrapped in a SystemC +``SC_METHOD``, sensitive to all inputs. (Ideally it would only be sensitive +to clocks and combo inputs, but tracing requires all signals to cause +evaluation, and the performance difference is small.) + +If tracing is enabled, a callback examines all variables in the design for +changes, and writes the trace for each change. To accelerate this process +the evaluation process records a bitmask of variables that might have +changed; if clear, checking those signals for changes may be skipped. + + +Coding Conventions +================== + + +Compiler Version and C++11 +-------------------------- + +Verilator requires C11. Verilator does not require any newer versions, but +is maintained to build successfully with C14/C17/C20. + + +Indentation and Naming Style +---------------------------- + +We will work with contributors to fix up indentation style issues, but it +is appreciated if you could match our style: + +- Use "mixedCapsSymbols" instead of "underlined_symbols". + +- Uas a "p" suffix on variables that are pointers, e.g. "nodep". + +- Comment every member variable. + +- In the include directory, use /// to document functions the user + calls. (This convention has not been applied retroactively.) + +C and Python indentation is automatically maintained with "make format" +using clang-format version 10.0.0, and yapf for python, and is +automatically corrected in the CI actions. For those manually formatting C +code: + +- Use 4 spaces per level, and no tabs. + +- Use 2 spaces between the end of source and the beginning of a + comment. + +- Use 1 space after if/for/switch/while and similar keywords. + +- No spaces before semicolons, nor between a function's name and open + parenthesis (only applies to functions; if/else has a following space). + + +The ``astgen`` Script +--------------------- + +Some of the code implementing passes is extremely repetitive, and must be +implemented for each sub-class of ``AstNode``. However, while repetitive, +there is more variability than can be handled in C++ macros. + +In Verilator this is implemented by using a Perl script, ``astgen`` to +pre-process the C++ code. For example in ``V3Const.cpp`` this is used to +implement the ``visit()`` functions for each binary operation using the +``TREEOP`` macro. + +The original C source code is transformed into C code in the ``obj_opt`` +and ``obj_dbg`` sub-directories (the former for the optimized version of +Verilator, the latter for the debug version). So for example +``V3Const.cpp`` into ``V3Const__gen.cpp``. + + +Visitor Functions ----------------- + +Verilator uses the "Visitor" design pattern to implement its refinement and +optimization passes. This allows separation of the pass algorithm from the +AST on which it operates. Wikipedia provides an introduction to the concept +at https://en.wikipedia.org/wiki/Visitor_pattern. + +As noted above, all visitors are derived classes of ``AstNVisitor``. All +derived classes of ``AstNode`` implement the ``accept`` method, which takes +as argument a reference to an instance or a ``AstNVisitor`` derived class +and applies the visit method of the ``AstNVisitor`` to the invoking AstNode +instance (i.e. ``this``). + +One possible difficulty is that a call to ``accept`` may perform an edit +which destroys the node it receives as argument. The +``acceptSubtreeReturnEdits`` method of ``AstNode`` is provided to apply +``accept`` and return the resulting node, even if the original node is +destroyed (if it is not destroyed it will just return the original node). + +The behavior of the visitor classes is achieved by overloading the +``visit`` function for the different ``AstNode`` derived classes. If a +specific implementation is not found, the system will look in turn for +overloaded implementations up the inheritance hierarchy. For example +calling ``accept`` on ``AstIf`` will look in turn for: + +:: + + void visit(AstIf* nodep) + void visit(AstNodeIf* nodep) + void visit(AstNodeStmt* nodep) + void visit(AstNode* nodep) + +There are three ways data is passed between visitor functions. + +1. A visitor-class member variable. This is generally for passing + "parent" information down to children. ``m_modp`` is a common + example. It's set to NULL in the constructor, where that node + (``AstModule`` visitor) sets it, then the children are iterated, then + it's cleared. Children under an ``AstModule`` will see it set, while + nodes elsewhere will see it clear. If there can be nested items (for + example an ``AstFor`` under an ``AstFor``) the variable needs to be + save-set-restored in the ``AstFor`` visitor, otherwise exiting the + lower for will lose the upper for's setting. + +2. User attributes. Each ``AstNode`` (**Note.** The AST node, not the + visitor) has five user attributes, which may be accessed as an + integer using the ``user1()`` through ``user5()`` methods, or as a + pointer (of type ``AstNUser``) using the ``user1p()`` through + ``user5p()`` methods (a common technique lifted from graph traversal + packages). + + A visitor first clears the one it wants to use by calling + ``AstNode::user#ClearTree()``, then it can mark any node's + ``user#()`` with whatever data it wants. Readers just call + ``nodep->user()``, but may need to cast appropriately, so you'll often + see ``VN_CAST(nodep->userp(), SOMETYPE)``. At the top of each visitor + are comments describing how the ``user()`` stuff applies to that + visitor class. For example: + + :: + + // NODE STATE + // Cleared entire netlist + // AstModule::user1p() // bool. True to inline this module + + This says that at the ``AstNetlist`` ``user1ClearTree()`` is called. + Each :literal:`AstModule's `user1()` is used to indicate if we're + going to inline it. + + These comments are important to make sure a ``user#()`` on a given + ``AstNode`` type is never being used for two different purposes. + + Note that calling ``user#ClearTree`` is fast, it doesn't walk the + tree, so it's ok to call fairly often. For example, it's commonly + called on every module. + +3. Parameters can be passed between the visitors in close to the + "normal" function caller to callee way. This is the second ``vup`` + parameter of type ``AstNUser`` that is ignored on most of the visitor + functions. V3Width does this, but it proved more messy than the above + and is deprecated. (V3Width was nearly the first module written. + Someday this scheme may be removed, as it slows the program down to + have to pass vup everywhere.) + + +Iterators +--------- + +``AstNVisitor`` provides a set of iterators to facilitate walking over +the tree. Each operates on the current ``AstNVisitor`` class (as this) +and takes an argument type ``AstNode*``. + +``iterate`` + Applies the ``accept`` method of the ``AstNode`` to the visitor + function. + +``iterateAndNextIgnoreEdit`` + Applies the ``accept`` method of each ``AstNode`` in a list (i.e. + connected by ``nextp`` and ``backp`` pointers). + +``iterateAndNextNull`` + Applies the ``accept`` method of each ``AstNode`` in a list, only if + the provided node is non-NULL. If a node is edited by the call to + ``accept``, apply ``accept`` again, until the node does not change. + +``iterateListBackwards`` + Applies the ``accept`` method of each ``AstNode`` in a list, starting + with the last one. + +``iterateChildren`` + Applies the ``iterateAndNextNull`` method on each child ``op1p`` + through ``op4p`` in turn. + +``iterateChildrenBackwards`` + Applies the ``iterateListBackwards`` method on each child ``op1p`` + through ``op4p`` in turn. + + +Caution on Using Iterators When Child Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Visitors often replace one node with another node; V3Width and V3Const +are major examples. A visitor which is the parent of such a replacement +needs to be aware that calling iteration may cause the children to +change. For example: + +:: + + // nodep->lhsp() is 0x1234000 + iterateAndNextNull(nodep->lhsp()); // and under covers nodep->lhsp() changes + // nodep->lhsp() is 0x5678400 + iterateAndNextNull(nodep->lhsp()); + +Will work fine, as even if the first iterate causes a new node to take +the place of the ``lhsp()``, that edit will update ``nodep->lhsp()`` and +the second call will correctly see the change. Alternatively: + +:: + + lp = nodep->lhsp(); + // nodep->lhsp() is 0x1234000, lp is 0x1234000 + iterateAndNextNull(lp); **lhsp=NULL;** // and under covers nodep->lhsp() changes + // nodep->lhsp() is 0x5678400, lp is 0x1234000 + iterateAndNextNull(lp); + +This will cause bugs or a core dump, as lp is a dangling pointer. Thus +it is advisable to set lhsp=NULL shown in the \*'s above to make sure +these dangles are avoided. Another alternative used in special cases +mostly in V3Width is to use acceptSubtreeReturnEdits, which operates on +a single node and returns the new pointer if any. Note +acceptSubtreeReturnEdits does not follow ``nextp()`` links. + +:: + + lp = acceptSubtreeReturnEdits(lp) + + +Identifying Derived Classes +--------------------------- + +A common requirement is to identify the specific ``AstNode`` class we +are dealing with. For example a visitor might not implement separate +``visit`` methods for ``AstIf`` and ``AstGenIf``, but just a single +method for the base class: + +:: + + void visit(AstNodeIf* nodep) + +However that method might want to specify additional code if it is +called for ``AstGenIf``. Verilator does this by providing a ``VN_IS`` +method for each possible node type, which returns true if the node is of +that type (or derived from that type). So our ``visit`` method could +use: + +:: + + if (VN_IS(nodep, AstGenIf) { + + } + +Additionally the ``VN_CAST`` method converts pointers similar to C++ +``dynamic_cast``. This either returns a pointer to the object cast to +that type (if it is of class ``SOMETYPE``, or a derived class of +``SOMETYPE``) or else NULL. (However, for true/false tests use ``VN_IS`` +as that is faster.) + + +.. _Testing + +Testing +======= + +For an overview of how to write a test see the BUGS section of the +Verilator primary manual. + +It is important to add tests for failures as well as success (for +example to check that an error message is correctly triggered). + +Tests that fail should by convention have the suffix ``_bad`` in their +name, and include ``fails = 1`` in either their ``compile`` or +``execute`` step as appropriate. + + +Preparing to Run Tests +---------------------- + +For all tests to pass you must install the following packages: + +- SystemC to compile the SystemC outputs, see http://systemc.org + +- Parallel::Forker from CPAN to run tests in parallel, you can install + this with e.g. "sudo cpan install Parallel::Forker". + +- vcddiff to find differences in VCD outputs. See the readme at + https://github.com/veripool/vcddiff + +- Cmake for build paths that use it. + + +Controlling the Test Driver +--------------------------- + +Test drivers are written in PERL. All invoke the main test driver script, +which can provide detailed help on all the features available when writing +a test driver. + +:: + + test_regress/driver.pl --help + +For convenience, a summary of the most commonly used features is provided +here. All drivers require a call to ``compile`` subroutine to compile the +test. For run-time tests, this is followed by a call to the ``execute`` +subroutine. Both of these functions can optionally be provided with a hash +table as argument specifying additional options. + +The test driver assumes by default that the source Verilog file name +matches the PERL driver name. So a test whose driver is +``t/t_mytest.pl`` will expect a Verilog source file ``t/t_mytest.v``. +This can be changed using the ``top_filename`` subroutine, for example + +:: + + top_filename("t/t_myothertest.v"); + +By default all tests will run with major simulators (Icarus Verilog, NC, +VCS, ModelSim, etc) as well as Verilator, to allow results to be +compared. However if you wish a test only to be used with Verilator, you +can use the following: + +:: + + scenarios(vlt => 1); + +Of the many options that can be set through arguments to ``compiler`` and +``execute``, the following are particularly useful: + +``verilator_flags2`` + A list of flags to be passed to verilator when compiling. + +``fails`` + Set to 1 to indicate that the compilation or execution is intended to fail. + +For example the following would specify that compilation requires two +defines and is expected to fail. + +:: + + compile( + verilator_flags2 => ["-DSMALL_CLOCK -DGATED_COMMENT"], + fails => 1, + ); + + +Regression Testing for Developers +--------------------------------- + +Developers will also want to call ./configure with two extra flags: + +``--enable-ccwarn`` + Causes the build to stop on warnings as well as errors. A good way to + ensure no sloppy code gets added, however it can be painful when it + comes to testing, since third party code used in the tests (e.g. + SystemC) may not be warning free. + +``--enable-longtests`` + In addition to the standard C, SystemC examples, also run the tests + in the ``test_regress`` directory when using *make test*'. This is + disabled by default as SystemC installation problems would otherwise + falsely indicate a Verilator problem. + +When enabling the long tests, some additional PERL modules are needed, +which you can install using cpan. + +:: + + cpan install Parallel::Forker + +There are some traps to avoid when running regression tests + +- When checking the MANIFEST, the test will barf on unexpected code in the + Verilator tree. So make sure to keep any such code outside the tree. + +- Not all Linux systems install Perldoc by default. This is needed for the + ``--help`` option to Verilator, and also for regression testing. This + can be installed using cpan: + + :: + + cpan install Pod::Perldoc + + Many Linux systems also offer a standard package for this. Red + Hat/Fedora/Centos offer *perl-Pod-Perldoc*', while + Debian/Ubuntu/Linux Mint offer \`perl-doc'. + +- Running regression may exhaust resources on some Linux systems, + particularly file handles and user processes. Increase these to + respectively 16,384 and 4,096. The method of doing this is system + dependent, but on Fedora Linux it would require editing the + ``/etc/security/limits.conf`` file as root. + + +Manual Test Execution +--------------------- + +A specific regression test can be executed manually. To start the +"EXAMPLE" test, run the following command. + +:: + + test_regress/t/t_EXAMPLE.pl + + +Continuous Integration +---------------------- + +Verilator uses GitHub Actions which automatically tests the master branch +for test failures on new commits. It also runs a daily cron job to validate +all of the tests against different OS and compiler versions. + +Developers can enable Actions on their GitHub repository so that the CI +environment can check their branches too by enabling the build workflow: + +- On GitHub, navigate to the main page of the repository. + +- Under your repository name, click Actions. + +- In the left sidebar, click the workflow you want to enable ("build"). + +- Click Enable workflow. + + +Fuzzing +------- + +There are scripts included to facilitate fuzzing of Verilator. These +have been successfully used to find a number of bugs in the frontend. + +The scripts are based on using `American fuzzy +lop `__ on a Debian-like system. + +To get started, cd to "nodist/fuzzer/" and run "./all". A sudo password may +be required to setup the system for fuzzing. + + +Debugging +========= + + +Debug Levels +------------ + +The "UINFO" calls in the source indicate a debug level. Messages level 3 +and below are globally enabled with ``--debug``. Higher levels may be +controlled with ``--debugi ``. An individual source file levels may +be controlled with ``-debugi- ``. For example ``--debug +--debugi 5 --debugi-V3Width 9`` will use the debug binary at default +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. + + +.dot Output +----------- + +Dot files are dumps of internal graphs in `Graphviz +`__ dot format. When a dot file is dumped, +Verilator will also print a line on stdout that can be used to format the +output, for example: + +:: + + dot -Tps -o ~/a.ps obj_dir/Vtop_foo.dot + +You can then print a.ps. You may prefer gif format, which doesn't get +scaled so can be more useful with large graphs. + +For interactive graph viewing consider `xdot +`__ or `ZGRViewer +`__. If you know of better +viewers (especially for large graphs) please let us know. + + +.tree Output +------------ + +Tree files are dumps of the AST Tree and are produced between every major +algorithmic stage. An example: + +:: + + NETLIST 0x90fb00 {a0ah} + 1: MODULE 0x912b20 {a8ah} top L2 [P] + *1:2: VAR 0x91a780 {a22ah} @dt=0xa2e640(w32) out_wide [O] WIRE + 1:2:1: BASICDTYPE 0xa2e640 {e24ah} @dt=this(sw32) integer kwd=integer range=[31:0] + +The following summarizes the above example dump, with more detail on each +field in the section below. + ++---------------+--------------------------------------------------------+ +| ``1:2:`` | The hierarchy of the ``VAR`` is the ``op2p`` | +| | pointer under the ``MODULE``, which in turn is the | +| | ``op1p`` pointer under the ``NETLIST`` | ++---------------+--------------------------------------------------------+ +| ``VAR`` | The AstNodeType (e.g. ``AstVar``). | ++---------------+--------------------------------------------------------+ +| ``0x91a780`` | Address of this node. | ++---------------+--------------------------------------------------------+ +| ```` | The 74th edit to the netlist was the last | +| | modification to this node. | ++---------------+--------------------------------------------------------+ +| ``{a22ah}`` | This node is related to the source filename | +| | "a", where "a" is the first file read, "z" the 26th, | +| | and "aa" the 27th. Then line 22 in that file, then | +| | column 8 (aa=0, az=25, ba=26, ...). | ++---------------+--------------------------------------------------------+ +| ``@dt=0x...`` | The address of the data type this node contains. | ++---------------+--------------------------------------------------------+ +| ``w32`` | The data-type width() is 32 bits. | ++---------------+--------------------------------------------------------+ +| ``out_wide`` | The name() of the node, in this case the name of the | +| | variable. | ++---------------+--------------------------------------------------------+ +| ``[O]`` | Flags which vary with the type of node, in this | +| | case it means the variable is an output. | ++---------------+--------------------------------------------------------+ + +In more detail the following fields are dumped common to all nodes. They +are produced by the ``AstNode::dump()`` method: + +Tree Hierarchy + The dump lines begin with numbers and colons to indicate the child + node hierarchy. As noted above, ``AstNode`` has lists of items at the + same level in the AST, connected by the ``nextp()`` and ``prevp()`` + pointers. These appear as nodes at the same level. For example after + inlining: + + :: + + NETLIST 0x929c1c8 {a0} w0 + 1: MODULE 0x92bac80 {e14} w0 TOP_t L1 [P] + 1:1: CELLINLINE 0x92bab18 {e14} w0 v -> t + 1:1: CELLINLINE 0x92bc1d8 {e24} w0 v__DOT__i_test_gen -> test_gen + ... + 1: MODULE 0x92b9bb0 {e47} w0 test_gen L3 + ... + +AstNode type + The textual name of this node AST type (always in capitals). Many of + these correspond directly to Verilog entities (for example ``MODULE`` + and ``TASK``), but others are internal to Verilator (for example + ``NETLIST`` and ``BASICDTYPE``). + +Address of the node + A hexadecimal address of the node in memory. Useful for examining + with the debugger. If the actual address values are not important, + then using the ``--dump-tree-addrids`` option will convert address + values to short identifiers of the form ``([A-Z]*)``, which is + hopefully easier for the reader to cross reference throughout the + dump. + +Last edit number + Of the form ```` or ```` , where ``nnnn`` is the + number of the last edit to modify this node. The trailing ``#`` + indicates the node has been edited since the last tree dump (which + typically means in the last refinement or optimization pass). GDB can + watch for this, see << /Debugging >>. + +Source file and line + Of the form ``{xxnnnn}``, where C{xx} is the filename letter (or + letters) and ``nnnn`` is the line number within that file. The first + file is ``a``, the 26th is ``z``, the 27th is ``aa`` and so on. + +User pointers + Shows the value of the node's user1p...user5p, if non-NULL. + +Data type + Many nodes have an explicit data type. "@dt=0x..." indicates the + address of the data type (AstNodeDType) this node uses. + + If a data type is present and is numeric, it then prints the width of + the item. This field is a sequence of flag characters and width data + as follows: + + - ``s`` if the node is signed. + + - ``d`` if the node is a double (i.e a floating point entity). + + - ``w`` always present, indicating this is the width field. + + - ``u`` if the node is unsized. + + - ``/nnnn`` if the node is unsized, where ``nnnn`` is the minimum + width. + +Name of the entity represented by the node if it exists + For example for a ``VAR`` it is the name of the variable. + +Many nodes follow these fields with additional node specific +information. Thus the ``VARREF`` node will print either ``[LV]`` or +``[RV]`` to indicate a left value or right value, followed by the node +of the variable being referred to. For example: + +:: + + 1:2:1:1: VARREF 0x92c2598 {e24} w0 clk [RV] <- VAR 0x92a2e90 {e18} w0 clk [I] INPUT + +In general, examine the ``dump()`` method in ``V3AstNodes.cpp`` of the node +type in question to determine additional fields that may be printed. + +The ``MODULE`` has a list of ``CELLINLINE`` nodes referred to by its +``op1p()`` pointer, connected by ``nextp()`` and ``prevp()`` pointers. + +Similarly the ``NETLIST`` has a list of modules referred to by its +``op1p()`` pointer. + + +Debugging with GDB +------------------ + +The test_regress/driver.pl script accepts ``--debug --gdb`` to start +Verilator under gdb and break when an error is hit or the program is about +to exit. You can also use ``--debug --gdbbt`` to just backtrace and then +exit gdb. To debug the Verilated executable, use ``--gdbsim``. + +If you wish to start Verilator under GDB (or another debugger), then you +can use ``--debug`` and look at the underlying invocation of +``verilator_dbg``. For example + +:: + + t/t_alw_dly.pl --debug + +shows it invokes the command: + +:: + + ../verilator_bin_dbg --prefix Vt_alw_dly --x-assign unique --debug + -cc -Mdir obj_dir/t_alw_dly --debug-check -f input.vc t/t_alw_dly.v + +Start GDB, then ``start`` with the remaining arguments. + +:: + + gdb ../verilator_bin_dbg + ... + (gdb) start --prefix Vt_alw_dly --x-assign unique --debug -cc -Mdir + obj_dir/t_alw_dly --debug-check -f input.vc t/t_alw_dly.v + > obj_dir/t_alw_dly/vlt_compile.log + ... + Temporary breakpoint 1, main (argc=13, argv=0xbfffefa4, env=0xbfffefdc) + at ../Verilator.cpp:615 + 615 ios::sync_with_stdio(); + (gdb) + +You can then continue execution with breakpoints as required. + +To break at a specific edit number which changed a node (presumably to +find what made a line in the tree dumps): + +:: + + watch AstNode::s_editCntGbl==#### + +Then, when the watch fires, to break at every following change to that +node: + +:: + + watch m_editCount + +To print a node: + +:: + + pn nodep + # or: call dumpGdb(nodep) # aliased to "pn" in src/.gdbinit + pnt nodep + # or: call dumpTreeGdb(nodep) # aliased to "pnt" in src/.gdbinit + +When GDB halts, it is useful to understand that the backtrace will commonly +show the iterator functions between each invocation of ``visit`` in the +backtrace. You will typically see a frame sequence something like: + +:: + + ... + visit() + iterateChildren() + iterateAndNext() + accept() + visit() + ... + + +Adding a New Feature +==================== + +Generally what would you do to add a new feature? + +1. File an issue (if there isn't already) so others know what you're + working on. + +2. Make a testcase in the test_regress/t/t_EXAMPLE format, see + :ref:`Testing`. + +3. If grammar changes are needed, look at the git version of VerilogPerl's + src/VParseGrammar.y, as this grammar supports the full SystemVerilog + language and has a lot of back-and-forth with Verilator's grammar. Copy + the appropriate rules to src/verilog.y and modify the productions. + +4. If a new Ast type is needed, add it to V3AstNodes.h. Follow the + convention described above about the AstNode type hierarchy. + +5. Now you can run "test_regress/t/t_{new testcase}.pl --debug" and it'll + probably fail but you'll see a + test_regress/obj_dir/t_{newtestcase}/*.tree file which you can examine + to see if the parsing worked. See also the sections above on debugging. + +6. Modify the later visitor functions to process the new feature as needed. + + +Adding a New Pass +----------------- + +For more substantial changes you may need to add a new pass. The simplest +way to do this is to copy the ``.cpp`` and ``.h`` files from an existing +pass. You'll need to add a call into your pass from the ``process()`` +function in ``src/verilator.cpp``. + +To get your pass to build you'll need to add its binary filename to the +list in ``src/Makefile_obj.in`` and reconfigure. + + +"Never" features +---------------- + +Verilator ideally would support all of IEEE, and has the goal to get close +to full support. However the following IEEE sections and features are not +anticipated to be ever implemented for the reasons indicated. + +.. list-table:: + +IEEE 1800-2017 3.3 recursive modules + Little/no tool support, and arguably not a good practice. +IEEE 1800-2017 6.12 "shortreal" + Little/no tool support, and easily simply promoted to real. +IEEE 1800-2017 11.11 Min, typ, max + No SDF support so will always use typical. +IEEE 1800-2017 11.12 "let" + Little/no tool support, makes difficult to implement parsers. +IEEE 1800-2017 20.15 Probabilistic functions + Little industry use. +IEEE 1800-2017 20.16 Stochastic analysis + Little industry use. +IEEE 1800-2017 20.17 PLA modeling + Little industry use and outdated technology. +IEEE 1800-2017 31 Timing checks + No longer relevant with static timing analysis tools. +IEEE 1800-2017 32 SDF annotation + No longer relevant with static timing analysis tools. +IEEE 1800-2017 33 Config + Little/no tool support or industry use. + + +Distribution +============ + +Copyright 2008-2021 by Wilson Snyder. Verilator 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. + +.. |Logo| image:: https://www.veripool.org/img/verilator_256_200_min.png diff --git a/docs/xml.adoc b/docs/xml.adoc deleted file mode 100644 index b200e5de3..000000000 --- a/docs/xml.adoc +++ /dev/null @@ -1,72 +0,0 @@ -= Verilator XML Output -:toc: right - -// Github doesn't render unless absolute URL -image::https://www.veripool.org/img/verilator_256_200_min.png[Logo,256,200,role="right"] - -== Introduction - -This document describes Verilator's XML output. For more general information -please see https://verilator.org[verilator.org]. - -== General - -Verilator's XML output is enabled with the `--xml-only` flag. It contains -limited information about the elaborated design including files, modules, -instance hierarchy, logic and data types. There is no formal schema since part -of the structure of the XML document matches the compiled code which would -require the schema to describe legal SystemVerilog structure. The intended -usage is to enable other downstream tools to take advantage of Verilator's -parser. - -== Structure - -The XML document consists of 4 sections within the top level `verilator_xml` -element: - -``...``:: - -This section contains a list of all design files read, including the -built-in constructs and the command line as their own entries. Each -`` has an attribute `id` which is a short ASCII string unique to that -file. Other elements' `loc` attributes use this id to refer to a particular -file. - -``...``:: - -All files containing Verilog module definitions are listed in this section. -This element's contents is a subset of the `` element's contents. - -``...``:: - -The cells section of the XML document contains the design instance -hierarchy. Each instance is represented with the `` element with the -following attributes: - -* `fl` (deprecated): The file id and line number where the module was - instanced. Use `loc` instead. - -* `loc`: The file id, first line number, last line number, first column - number and last column number of the identifier where the module was - instanced, separated by commas. - -* `name`: The instance name. - -* `submodname`: The module name uniquified with particular parameter values (if any). - -* `hier`: The full hierarchy path. - -``...``:: - -The netlist section contains a number of ``...`` elements, -each describing the contents of that module, and a single ``... -`` element which lists all used types used within the -modules. Each type has a numeric `id` attribute that is referred to by -elements in the `` elements using the `dtype_id` attribute. - -== Distribution - -Copyright 2020-2021 by Wilson Snyder. Verilator 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 diff --git a/docs/xml.rst b/docs/xml.rst new file mode 100644 index 000000000..6231c8273 --- /dev/null +++ b/docs/xml.rst @@ -0,0 +1,81 @@ +|Logo| + +=========================== +Verilator XML Output Format +=========================== + +Introduction +============ + +This document describes Verilator's XML output. For more general +information please see `verilator.org `__. + + +General +======= + +Verilator's XML output is enabled with the ``--xml-only`` flag. It contains +limited information about the elaborated design including files, modules, +instance hierarchy, logic and data types. There is no formal schema since +part of the structure of the XML document matches the compiled code which +would require the schema to describe legal SystemVerilog structure. The +intended usage is to enable other downstream tools to take advantage of +Verilator's parser. + + +Structure +========= + +The XML document consists of 4 sections within the top level +``verilator_xml`` element: + +````\ ... ```` + This section contains a list of all design files read, including the + built-in constructs and the command line as their own entries. Each + ```` has an attribute ``id`` which is a short ASCII string + unique to that file. Other elements' ``loc`` attributes use this id + to refer to a particular file. + +````\ ... ```` + All files containing Verilog module definitions are listed in this + section. This element's contents is a subset of the ```` + element's contents. + +````\ ... ```` + The cells section of the XML document contains the design instance + hierarchy. Each instance is represented with the ```` element + with the following attributes: + + - ``fl`` (deprecated): The file id and line number where the module + was instanced. Use ``loc`` instead. + + - ``loc``: The file id, first line number, last line number, first + column number and last column number of the identifier where the + module was instanced, separated by commas. + + - ``name``: The instance name. + + - ``submodname``: The module name uniquified with particular + parameter values (if any). + + - ``hier``: The full hierarchy path. + +````\ ... ```` + The netlist section contains a number of + ````\ ... ```` elements, each describing the + contents of that module, and a single ````\ ... + ```` element which lists all used types used within the + modules. Each type has a numeric ``id`` attribute that is referred to + by elements in the ```` elements using the ``dtype_id`` + attribute. + +.. __distribution: + +Distribution +============ + +Copyright 2020-2021 by Wilson Snyder. Verilator 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. + +.. |Logo| image:: https://www.veripool.org/img/verilator_256_200_min.png diff --git a/examples/make_hello_sc/sc_main.cpp b/examples/make_hello_sc/sc_main.cpp index 6fef7ca31..587010712 100644 --- a/examples/make_hello_sc/sc_main.cpp +++ b/examples/make_hello_sc/sc_main.cpp @@ -26,7 +26,7 @@ int sc_main(int argc, char* argv[]) { if (false && argc && argv) {} // Construct the Verilated model, from Vtop.h generated from Verilating "top.v" - Vtop* top = new Vtop("top"); + Vtop* top = new Vtop{"top"}; // Initialize SC model sc_start(1, SC_NS); diff --git a/examples/make_protect_lib/secret_impl.v b/examples/make_protect_lib/secret_impl.v index 1e39257f5..2a12a272c 100644 --- a/examples/make_protect_lib/secret_impl.v +++ b/examples/make_protect_lib/secret_impl.v @@ -17,7 +17,7 @@ module secret_impl logic [31:0] accum_q = 0; logic [31:0] secret_value = 9; - initial $display("%m: initialized"); + initial $display("[%0t] %m: initialized", $time); always @(posedge clk) begin accum_q <= accum_q + a; diff --git a/examples/make_protect_lib/sim_main.cpp b/examples/make_protect_lib/sim_main.cpp index 4d15ea360..b3096b4c5 100644 --- a/examples/make_protect_lib/sim_main.cpp +++ b/examples/make_protect_lib/sim_main.cpp @@ -16,25 +16,24 @@ #include #endif -vluint64_t main_time = 0; -double sc_time_stamp() { return main_time; } - int main(int argc, char** argv, char** env) { if (false && argc && argv && env) {} - Verilated::debug(0); - Verilated::randReset(2); - Verilated::commandArgs(argc, argv); + // Construct context to hold simulation time, etc + VerilatedContext* contextp = new VerilatedContext; + contextp->debug(0); + contextp->randReset(2); + contextp->commandArgs(argc, argv); // Construct the Verilated model, including the secret module - Vtop* top = new Vtop; + Vtop* top = new Vtop{contextp}; #if VM_TRACE // When tracing, the contents of the secret module will not be seen VerilatedVcdC* tfp = nullptr; - const char* flag = Verilated::commandArgsPlusMatch("trace"); + const char* flag = contextp->commandArgsPlusMatch("trace"); if (flag && 0 == strcmp(flag, "+trace")) { - Verilated::traceEverOn(true); + contextp->traceEverOn(true); VL_PRINTF("Enabling waves into logs/vlt_dump.vcd...\n"); tfp = new VerilatedVcdC; top->trace(tfp, 99); @@ -46,12 +45,12 @@ int main(int argc, char** argv, char** env) { top->clk = 0; // Simulate until $finish - while (!Verilated::gotFinish()) { - main_time++; + while (!contextp->gotFinish()) { + contextp->timeInc(1); top->clk = ~top->clk & 0x1; top->eval(); #if VM_TRACE - if (tfp) tfp->dump(main_time); + if (tfp) tfp->dump(contextp->time()); #endif } @@ -69,6 +68,8 @@ int main(int argc, char** argv, char** env) { // Destroy model delete top; top = nullptr; + delete contextp; + contextp = nullptr; // Return good completion status // Don't use exit() or destructor won't get called diff --git a/examples/make_tracing_c/sim_main.cpp b/examples/make_tracing_c/sim_main.cpp index 08fa60098..edd786a14 100644 --- a/examples/make_tracing_c/sim_main.cpp +++ b/examples/make_tracing_c/sim_main.cpp @@ -14,42 +14,45 @@ // Include model header, generated from Verilating "top.v" #include "Vtop.h" -// Current simulation time (64-bit unsigned) -vluint64_t main_time = 0; -// Called by $time in Verilog -double sc_time_stamp() { - return main_time; // Note does conversion to real, to match SystemC -} - int main(int argc, char** argv, char** env) { // This is a more complicated example, please also see the simpler examples/make_hello_c. // Prevent unused variable warnings if (false && argc && argv && env) {} - // Set debug level, 0 is off, 9 is highest presently used - // May be overridden by commandArgs - Verilated::debug(0); - - // Randomization reset policy - // May be overridden by commandArgs - Verilated::randReset(2); - - // Verilator must compute traced signals - Verilated::traceEverOn(true); - - // Pass arguments so Verilated code can see them, e.g. $value$plusargs - // This needs to be called before you create any model - Verilated::commandArgs(argc, argv); - // Create logs/ directory in case we have traces to put under it Verilated::mkdir("logs"); - // Construct the Verilated model, from Vtop.h generated from Verilating "top.v" - // Using unique_ptr is similar to "Vtop* top = new Vtop" then deleting at end - const std::unique_ptr top{new Vtop}; + // Construct a VerilatedContext to hold simulation time, etc. + // Multiple modules (made later below with Vtop) may share the same + // context to share time, or modules may have different contexts if + // they should be independent from each other. - // Set some inputs + // Using unique_ptr is similar to + // "VerilatedContext* contextp = new VerilatedContext" then deleting at end. + const std::unique_ptr contextp{new VerilatedContext}; + + // Set debug level, 0 is off, 9 is highest presently used + // May be overridden by commandArgs argument parsing + contextp->debug(0); + + // Randomization reset policy + // May be overridden by commandArgs argument parsing + contextp->randReset(2); + + // Verilator must compute traced signals + contextp->traceEverOn(true); + + // Pass arguments so Verilated code can see them, e.g. $value$plusargs + // This needs to be called before you create any model + contextp->commandArgs(argc, argv); + + // Construct the Verilated model, from Vtop.h generated from Verilating "top.v". + // Using unique_ptr is similar to "Vtop* top = new Vtop" then deleting at end. + // "TOP" will be the hierarchical name of the module. + const std::unique_ptr top{new Vtop{contextp.get(), "TOP"}}; + + // Set Vtop's input signals top->reset_l = !0; top->clk = 0; top->in_small = 1; @@ -59,8 +62,19 @@ int main(int argc, char** argv, char** env) { top->in_wide[2] = 0x3; // Simulate until $finish - while (!Verilated::gotFinish()) { - main_time++; // Time passes... + while (!contextp->gotFinish()) { + // Historical note, before Verilator 4.200 Verilated::gotFinish() + // was used above in place of contextp->gotFinish(). + // Most of the contextp-> calls can use Verilated:: calls instead; + // the Verilated:: versions simply assume there's a single context + // being used (per thread). It's faster and clearer to use the + // newer contextp-> versions. + + contextp->timeInc(1); // 1 timeprecision period passes... + // Historical note, before Verilator 4.200 a sc_time_stamp() + // function was required instead of using timeInc. Once timeInc() + // is called (with non-zero), the Verilated libraries assume the + // new API, and sc_time_stamp() will no longer work. // Toggle a fast (time/2 period) clock top->clk = !top->clk; @@ -70,7 +84,7 @@ int main(int argc, char** argv, char** env) { // this only on a negedge of clk, because we know // reset is not sampled there. if (!top->clk) { - if (main_time > 1 && main_time < 10) { + if (contextp->time() > 1 && contextp->time() < 10) { top->reset_l = !1; // Assert reset } else { top->reset_l = !0; // Deassert reset @@ -82,23 +96,23 @@ int main(int argc, char** argv, char** env) { // Evaluate model // (If you have multiple models being simulated in the same // timestep then instead of eval(), call eval_step() on each, then - // eval_end_step() on each.) + // eval_end_step() on each. See the manual.) top->eval(); // Read outputs VL_PRINTF("[%" VL_PRI64 "d] clk=%x rstl=%x iquad=%" VL_PRI64 "x" " -> oquad=%" VL_PRI64 "x owide=%x_%08x_%08x\n", - main_time, top->clk, top->reset_l, top->in_quad, top->out_quad, top->out_wide[2], - top->out_wide[1], top->out_wide[0]); + contextp->time(), top->clk, top->reset_l, top->in_quad, top->out_quad, + top->out_wide[2], top->out_wide[1], top->out_wide[0]); } // Final model cleanup top->final(); - // Coverage analysis (since test passed) + // Coverage analysis (calling write only after the test is known to pass) #if VM_COVERAGE Verilated::mkdir("logs"); - VerilatedCov::write("logs/coverage.dat"); + contextp->coveragep()->write("logs/coverage.dat"); #endif // Return good completion status diff --git a/examples/make_tracing_sc/sc_main.cpp b/examples/make_tracing_sc/sc_main.cpp index 4f7628113..ee42a0aa6 100644 --- a/examples/make_tracing_sc/sc_main.cpp +++ b/examples/make_tracing_sc/sc_main.cpp @@ -29,21 +29,26 @@ int sc_main(int argc, char* argv[]) { // Prevent unused variable warnings if (false && argc && argv) {} + // Create logs/ directory in case we have traces to put under it + Verilated::mkdir("logs"); + // Set debug level, 0 is off, 9 is highest presently used - // May be overridden by commandArgs + // May be overridden by commandArgs argument parsing Verilated::debug(0); // Randomization reset policy - // May be overridden by commandArgs + // May be overridden by commandArgs argument parsing Verilated::randReset(2); +#if VM_TRACE + // Before any evaluation, need to know to calculate those signals only used for tracing + Verilated::traceEverOn(true); +#endif + // Pass arguments so Verilated code can see them, e.g. $value$plusargs // This needs to be called before you create any model Verilated::commandArgs(argc, argv); - // Create logs/ directory in case we have traces to put under it - Verilated::mkdir("logs"); - // General logfile ios::sync_with_stdio(); @@ -64,7 +69,7 @@ int sc_main(int argc, char* argv[]) { // Using unique_ptr is similar to "Vtop* top = new Vtop" then deleting at end const std::unique_ptr top{new Vtop{"top"}}; - // Attach signals to the model + // Attach Vtop's signals to this upper model top->clk(clk); top->fastclk(fastclk); top->reset_l(reset_l); @@ -75,11 +80,6 @@ int sc_main(int argc, char* argv[]) { top->out_quad(out_quad); top->out_wide(out_wide); -#if VM_TRACE - // Before any evaluation, need to know to calculate those signals only used for tracing - Verilated::traceEverOn(true); -#endif - // You must do one evaluation before enabling waves, in order to allow // SystemC to interconnect everything for testing. sc_start(1, SC_NS); @@ -128,7 +128,7 @@ int sc_main(int argc, char* argv[]) { } #endif - // Coverage analysis (since test passed) + // Coverage analysis (calling write only after the test is known to pass) #if VM_COVERAGE Verilated::mkdir("logs"); VerilatedCov::write("logs/coverage.dat"); diff --git a/examples/xml_py/vl_file_copy b/examples/xml_py/vl_file_copy index d3f46629d..5eae927ed 100755 --- a/examples/xml_py/vl_file_copy +++ b/examples/xml_py/vl_file_copy @@ -1,16 +1,16 @@ #!/usr/bin/env python3 # -*- Python -*- See copyright, etc below +# pylint: disable=C0114,C0115,R0903 ###################################################################### import argparse import os import re import subprocess -import sys import tempfile import xml.etree.ElementTree as ET from shutil import copy2 -from pprint import pprint, pformat +# from pprint import pprint, pformat ####################################################################### @@ -22,19 +22,20 @@ class VlFileCopy: # ideally this script would check against options mentioned in help debug=0, output_dir='copied'): # directory name we output file uses + self.debug = debug xml_temp = tempfile.NamedTemporaryFile() - args = [ + vargs = [ '--xml-output', xml_temp.name, '--bbox-sys', # Parse some stuff can't translate '--bbox-unsup', '--prefix vlxml' ] # So we know name of .xml output - args += verilator_args - self.run_verilator(args) + vargs += verilator_args + self.run_verilator(vargs) self.tree = ET.parse(xml_temp.name) os.makedirs(output_dir, 0o777, True) @@ -49,13 +50,13 @@ class VlFileCopy: print("\tcp %s %s" % (filename, output_dir)) copy2(filename, output_dir) - def run_verilator(self, args): + def run_verilator(self, vargs): """Run Verilator command, check errors""" if os.getenv("VERILATOR_ROOT"): command = os.getenv("VERILATOR_ROOT") + "/bin/verilator" else: command = "verilator" - command += ' ' + ' '.join(args) + command += ' ' + ' '.join(vargs) if self.debug: print("\t%s " % command) status = subprocess.call(command, shell=True) @@ -119,6 +120,6 @@ SPDX-License-Identifier: CC0-1.0 fc = VlFileCopy(output_dir=args.odir, debug=args.debug, verilator_args=rem) ###################################################################### -### Local Variables: -### compile-command: "./vl_file_copy -h ; VERILATOR_ROOT=$V4 ./vl_file_copy +define+thru top.v" -### End: +# Local Variables: +# compile-command: "./vl_file_copy -h ; VERILATOR_ROOT=$V4 ./vl_file_copy +define+thru top.v" +# End: diff --git a/examples/xml_py/vl_hier_graph b/examples/xml_py/vl_hier_graph index af9f83023..359bd5da2 100755 --- a/examples/xml_py/vl_hier_graph +++ b/examples/xml_py/vl_hier_graph @@ -1,16 +1,14 @@ #!/usr/bin/env python3 # -*- Python -*- See copyright, etc below +# pylint: disable=C0103,C0114,C0115,C0115,C0116,R0914 ###################################################################### import argparse import os -import re import subprocess -import sys import tempfile import xml.etree.ElementTree as ET -from shutil import copy2 -from pprint import pprint, pformat +# from pprint import pprint, pformat ####################################################################### @@ -28,15 +26,15 @@ class VlHierGraph: xml_temp = tempfile.NamedTemporaryFile() - args = [ + vargs = [ '--xml-output', xml_temp.name, '--bbox-sys', # Parse some stuff can't translate '--bbox-unsup', '--prefix vlxml' ] # So we know name of .xml output - args += verilator_args - self.run_verilator(args) + vargs += verilator_args + self.run_verilator(vargs) self.tree = ET.parse(xml_temp.name) with open(output_filename, "w") as fh: @@ -70,18 +68,18 @@ class VlHierGraph: fh.write("}\n") def name_to_vertex_number(self, name): - if not name in self.name_to_number: + if name not in self.name_to_number: self.next_vertex_number += 1 self.name_to_number[name] = self.next_vertex_number return self.name_to_number[name] - def run_verilator(self, args): + def run_verilator(self, vargs): """Run Verilator command, check errors""" if os.getenv("VERILATOR_ROOT"): command = os.getenv("VERILATOR_ROOT") + "/bin/verilator" else: command = "verilator" - command += ' ' + ' '.join(args) + command += ' ' + ' '.join(vargs) if self.debug: print("\t%s " % command) status = subprocess.call(command, shell=True) @@ -146,6 +144,6 @@ SPDX-License-Identifier: CC0-1.0 verilator_args=rem) ###################################################################### -### Local Variables: -### compile-command: "./vl_hier_graph -h ; VERILATOR_ROOT=$V4 ./vl_hier_graph +define+thru top.v" -### End: +# Local Variables: +# compile-command: "./vl_hier_graph -h ; VERILATOR_ROOT=$V4 ./vl_hier_graph +define+thru top.v" +# End: diff --git a/include/verilated.cpp b/include/verilated.cpp index 17949a17c..0421b0438 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -15,11 +15,35 @@ /// This file must be compiled and linked against all objects /// created from Verilator. /// +/// Those macro/function/variable starting or ending in _ are internal, +/// however many of the other function/macros here are also internal. +/// /// Code available from: https://verilator.org /// //========================================================================= +// Internal note: +// +// verilated.o may exist both in protect-lib (incrementally linked .a/.so) +// and the main module. Both refer the same instance of static +// variables/VL_THREAD_LOCAL in verilated.o such as Verilated, or +// VerilatedImpData. This is important to share that state, but the +// sharing may cause a double-free error when shutting down because the +// loader will insert a constructor/destructor at each reference to +// verilated.o, resulting in at runtime constructors/destructors being +// called multiple times. +// +// To avoid the trouble: +// * Statics declared inside functions. The compiler will wrap +// the construction in must-be-one-time checks. +// * Or, use only C++20 constinit types. (TODO: Make a VL_CONSTINIT). +// * Or, use types that are multi-constructor safe. +// * Or, the static should be of a union, which will avoid compiler +// construction, and appropriately check for duplicate construction. +// * Or, code is not linked in protected library. e.g. the VPI +// and DPI libraries are not needed there. +//========================================================================= -#define _VERILATED_CPP_ +#define VERILATOR_VERILATED_CPP_ #include "verilatedos.h" #include "verilated_imp.h" @@ -54,41 +78,16 @@ static_assert(sizeof(vluint64_t) == 8, "vluint8_t is missized"); //=========================================================================== // Global variables +// Internal note: Globals may multi-construct, see verilated.cpp top. -// Slow path variables -VerilatedMutex Verilated::s_mutex; +// Fast path, keep together +int Verilated::s_debug = 0; +VerilatedContext* Verilated::s_lastContextp = nullptr; // Keep below together in one cache line -Verilated::Serialized Verilated::s_s; -Verilated::NonSerialized Verilated::s_ns; +// Internal note: Globals may multi-construct, see verilated.cpp top. VL_THREAD_LOCAL Verilated::ThreadLocal Verilated::t_s; -Verilated::CommandArgValues Verilated::s_args; - -VerilatedImp::VerilatedImpU VerilatedImp::s_s; - -// Guarantees to call setup() and teardown() just once. -struct VerilatedInitializer { - VerilatedInitializer() { setup(); } - ~VerilatedInitializer() { teardown(); } - void setup() { - static bool done = false; - if (!done) { - VerilatedImp::setup(); - Verilated::s_ns.setup(); - done = true; - } - } - void teardown() { - static bool done = false; - if (!done) { - VerilatedImp::teardown(); - Verilated::s_ns.teardown(); - done = true; - } - } -} s_VerilatedInitializer; - //=========================================================================== // User definable functions // Note a TODO is a future version of the API will pass a structure so that @@ -99,29 +98,41 @@ void vl_finish(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE if (false && hier) {} VL_PRINTF( // Not VL_PRINTF_MT, already on main thread "- %s:%d: Verilog $finish\n", filename, linenum); - if (Verilated::gotFinish()) { + if (Verilated::threadContextp()->gotFinish()) { VL_PRINTF( // Not VL_PRINTF_MT, already on main thread "- %s:%d: Second verilog $finish, exiting\n", filename, linenum); Verilated::runFlushCallbacks(); Verilated::runExitCallbacks(); exit(0); } - Verilated::gotFinish(true); + Verilated::threadContextp()->gotFinish(true); } #endif #ifndef VL_USER_STOP ///< Define this to override this function void vl_stop(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE { - Verilated::gotFinish(true); - Verilated::runFlushCallbacks(); - vl_fatal(filename, linenum, hier, "Verilog $stop"); + const char* const msg = "Verilog $stop"; + Verilated::threadContextp()->gotError(true); + Verilated::threadContextp()->gotFinish(true); + if (Verilated::threadContextp()->fatalOnError()) { + vl_fatal(filename, linenum, hier, msg); + } else { + if (filename && filename[0]) { + // Not VL_PRINTF_MT, already on main thread + VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg); + } else { + VL_PRINTF("%%Error: %s\n", msg); + } + Verilated::runFlushCallbacks(); + } } #endif #ifndef VL_USER_FATAL ///< Define this to override this function void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_UNSAFE { if (false && hier) {} - Verilated::gotFinish(true); + Verilated::threadContextp()->gotError(true); + Verilated::threadContextp()->gotFinish(true); if (filename && filename[0]) { // Not VL_PRINTF_MT, already on main thread VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg); @@ -143,8 +154,9 @@ void vl_fatal(const char* filename, int linenum, const char* hier, const char* m #ifndef VL_USER_STOP_MAYBE ///< Define this to override this function void vl_stop_maybe(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_UNSAFE { - Verilated::errorCountInc(); - if (maybe && Verilated::errorCount() < Verilated::errorLimit()) { + Verilated::threadContextp()->errorCountInc(); + if (maybe + && Verilated::threadContextp()->errorCount() < Verilated::threadContextp()->errorLimit()) { VL_PRINTF( // Not VL_PRINTF_MT, already on main thread "-Info: %s:%d: %s\n", filename, linenum, "Verilog $stop, ignored due to +verilator+error+limit"); @@ -255,34 +267,6 @@ void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE { } #endif -//=========================================================================== -// Overall class init - -Verilated::Serialized::Serialized() { - s_calcUnusedSigs = false; - s_gotFinish = false; - s_assertOn = true; - s_fatalOnVpiError = true; // retains old default behaviour - s_errorCount = 0; - s_errorLimit = 1; - s_randReset = 0; - s_randSeed = 0; - s_randSeedEpoch = 1; - s_timeunit = VL_TIME_UNIT; // Initial value until overriden by _Vconfigure - s_timeprecision = VL_TIME_PRECISION; // Initial value until overriden by _Vconfigure -} - -void Verilated::NonSerialized::setup() { s_profThreadsFilenamep = strdup("profile_threads.dat"); } -void Verilated::NonSerialized::teardown() { - if (s_profThreadsFilenamep) { - VL_DO_CLEAR(free(const_cast(s_profThreadsFilenamep)), - s_profThreadsFilenamep = nullptr); - } -} - -size_t Verilated::serialized2Size() VL_PURE { return sizeof(VerilatedImp::s_s.v.m_ser); } -void* Verilated::serialized2Ptr() VL_MT_UNSAFE { return &VerilatedImp::s_s.v.m_ser; } - //=========================================================================== // Random -- Mostly called at init time, so not inline. @@ -300,13 +284,14 @@ static vluint32_t vl_sys_rand32() VL_MT_UNSAFE { } vluint64_t vl_rand64() VL_MT_SAFE { - static VL_THREAD_LOCAL vluint32_t t_seedEpoch = 0; static VL_THREAD_LOCAL vluint64_t t_state[2]; + static VL_THREAD_LOCAL vluint32_t t_seedEpoch = 0; // For speed, we use a thread-local epoch number to know when to reseed - if (VL_UNLIKELY(t_seedEpoch != Verilated::randSeedEpoch())) { - // Set epoch before state, in case races with new seeding - t_seedEpoch = Verilated::randSeedEpoch(); - t_state[0] = Verilated::randSeedDefault64(); + // A thread always belongs to a single context, so this works out ok + if (VL_UNLIKELY(t_seedEpoch != VerilatedContextImp::randSeedEpoch())) { + // Set epoch before state, to avoid race case with new seeding + t_seedEpoch = VerilatedContextImp::randSeedEpoch(); + t_state[0] = Verilated::threadContextp()->impp()->randSeedDefault64(); t_state[1] = t_state[0]; // Fix state as algorithm is slow to randomize if many zeros // This causes a loss of ~ 1 bit of seed entropy, no big deal @@ -333,23 +318,23 @@ WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE { #endif IData VL_RANDOM_SEEDED_II(int obits, IData seed) VL_MT_SAFE { - Verilated::randSeed(static_cast(seed)); + Verilated::threadContextp()->randSeed(static_cast(seed)); return VL_RANDOM_I(obits); } IData VL_RAND_RESET_I(int obits) VL_MT_SAFE { - if (Verilated::randReset() == 0) return 0; + if (Verilated::threadContextp()->randReset() == 0) return 0; IData data = ~0; - if (Verilated::randReset() != 1) { // if 2, randomize + if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize data = VL_RANDOM_I(obits); } data &= VL_MASK_I(obits); return data; } QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE { - if (Verilated::randReset() == 0) return 0; + if (Verilated::threadContextp()->randReset() == 0) return 0; QData data = ~0ULL; - if (Verilated::randReset() != 1) { // if 2, randomize + if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize data = VL_RANDOM_Q(obits); } data &= VL_MASK_Q(obits); @@ -369,7 +354,7 @@ WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE { //=========================================================================== // Debug -void _VL_DEBUG_PRINT_W(int lbits, WDataInP iwp) VL_MT_SAFE { +void _vl_debug_print_w(int lbits, WDataInP iwp) VL_MT_SAFE { VL_PRINTF_MT(" Data: w%d: ", lbits); for (int i = VL_WORDS_I(lbits) - 1; i >= 0; --i) VL_PRINTF_MT("%08x ", iwp[i]); VL_PRINTF_MT("\n"); @@ -604,7 +589,7 @@ double VL_ISTOR_D_W(int lbits, WDataInP lwp) VL_PURE { if (!VL_SIGN_W(lbits, lwp)) return VL_ITOR_D_W(lbits, lwp); vluint32_t pos[VL_MULS_MAX_WORDS + 1]; // Fixed size, as MSVC++ doesn't allow [words] here VL_NEGATE_W(VL_WORDS_I(lbits), pos, lwp); - _VL_CLEAN_INPLACE_W(lbits, pos); + _vl_clean_inplace_w(lbits, pos); return -VL_ITOR_D_W(lbits, pos); } @@ -652,10 +637,10 @@ std::string VL_DECIMAL_NW(int width, WDataInP lwp) VL_MT_SAFE { std::string _vl_vsformat_time(char* tmp, double ld, bool left, size_t width) { // Double may lose precision, but sc_time_stamp has similar limit - std::string suffix = VerilatedImp::timeFormatSuffix(); - int userUnits = VerilatedImp::timeFormatUnits(); // 0..-15 - int fracDigits = VerilatedImp::timeFormatPrecision(); // 0..N - int prec = Verilated::timeprecision(); // 0..-15 + std::string suffix = Verilated::threadContextp()->impp()->timeFormatSuffix(); + int userUnits = Verilated::threadContextp()->impp()->timeFormatUnits(); // 0..-15 + int fracDigits = Verilated::threadContextp()->impp()->timeFormatPrecision(); // 0..N + int prec = Verilated::threadContextp()->timeprecision(); // 0..-15 int shift = prec - userUnits + fracDigits; // 0..-15 double shiftd = vl_time_multiplier(shift); double scaled = ld * shiftd; @@ -664,10 +649,11 @@ std::string _vl_vsformat_time(char* tmp, double ld, bool left, size_t width) { QData fraction = static_cast(scaled) % fracDiv; int digits = 0; if (!fracDigits) { - digits = sprintf(tmp, "%" VL_PRI64 "u%s", whole, suffix.c_str()); + digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "u%s", whole, + suffix.c_str()); } else { - digits = sprintf(tmp, "%" VL_PRI64 "u.%0*" VL_PRI64 "u%s", whole, fracDigits, fraction, - suffix.c_str()); + digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "u.%0*" VL_PRI64 "u%s", + whole, fracDigits, fraction, suffix.c_str()); } int needmore = width - digits; std::string padding; @@ -676,7 +662,7 @@ std::string _vl_vsformat_time(char* tmp, double ld, bool left, size_t width) { } // Do a va_arg returning a quad, assuming input argument is anything less than wide -#define _VL_VA_ARG_Q(ap, bits) (((bits) <= VL_IDATASIZE) ? va_arg(ap, IData) : va_arg(ap, QData)) +#define VL_VA_ARG_Q_(ap, bits) (((bits) <= VL_IDATASIZE) ? va_arg(ap, IData) : va_arg(ap, QData)) void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SAFE { // Format a Verilog $write style format into the output list @@ -763,11 +749,11 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA double d = va_arg(ap, double); if (lbits) {} // UNUSED - always 64 if (fmt == '^') { // Realtime - if (!widthSet) width = VerilatedImp::timeFormatWidth(); + if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth(); output += _vl_vsformat_time(t_tmp, d, left, width); } else { std::string fmts(pctp, pos - pctp + 1); - sprintf(t_tmp, fmts.c_str(), d); + VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, fmts.c_str(), d); output += t_tmp; } break; @@ -779,7 +765,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA WData qlwp[VL_WQ_WORDS_E]; WDataInP lwp = nullptr; if (lbits <= VL_QUADSIZE) { - ld = _VL_VA_ARG_Q(ap, lbits); + ld = VL_VA_ARG_Q_(ap, lbits); VL_SET_WQ(qlwp, ld); lwp = qlwp; } else { @@ -812,8 +798,9 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA int digits = 0; std::string append; if (lbits <= VL_QUADSIZE) { - digits = sprintf(t_tmp, "%" VL_PRI64 "d", - static_cast(VL_EXTENDS_QQ(lbits, lbits, ld))); + digits = VL_SNPRINTF( + t_tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "d", + static_cast(VL_EXTENDS_QQ(lbits, lbits, ld))); append = t_tmp; } else { if (VL_SIGN_E(lbits, lwp[VL_WORDS_I(lbits) - 1])) { @@ -841,7 +828,8 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA int digits = 0; std::string append; if (lbits <= VL_QUADSIZE) { - digits = sprintf(t_tmp, "%" VL_PRI64 "u", ld); + digits + = VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "u", ld); append = t_tmp; } else { append = VL_DECIMAL_NW(lbits, lwp); @@ -860,7 +848,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA break; } case 't': { // Time - if (!widthSet) width = VerilatedImp::timeFormatWidth(); + if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth(); output += _vl_vsformat_time(t_tmp, static_cast(ld), left, width); break; } @@ -1234,10 +1222,10 @@ done: FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE { // Expected non-MCD case; returns null on MCD descriptors. - return VerilatedImp::fdToFp(lhs); + return Verilated::threadContextp()->impp()->fdToFp(lhs); } -void _VL_VINT_TO_STRING(int obits, char* destoutp, WDataInP sourcep) VL_MT_SAFE { +void _vl_vint_to_string(int obits, char* destoutp, WDataInP sourcep) VL_MT_SAFE { // See also VL_DATA_TO_STRING_NW int lsb = obits - 1; bool start = true; @@ -1256,7 +1244,7 @@ void _VL_VINT_TO_STRING(int obits, char* destoutp, WDataInP sourcep) VL_MT_SAFE } } -void _VL_STRING_TO_VINT(int obits, void* destp, size_t srclen, const char* srcp) VL_MT_SAFE { +void _vl_string_to_vint(int obits, void* destp, size_t srclen, const char* srcp) VL_MT_SAFE { // Convert C string to Verilog format size_t bytes = VL_BYTES_I(obits); char* op = reinterpret_cast(destp); @@ -1295,7 +1283,7 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE { VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: fgets buffer overrun"); // LCOV_EXCL_LINE } - _VL_STRING_TO_VINT(obits, destp, got, str.data()); + _vl_string_to_vint(obits, destp, got, str.data()); return got; } @@ -1312,20 +1300,20 @@ IData VL_FERROR_IN(IData, std::string& outputr) VL_MT_SAFE { } IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) { - return VerilatedImp::fdNew(filename.c_str(), mode.c_str()); + return Verilated::threadContextp()->impp()->fdNew(filename.c_str(), mode.c_str()); } IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE { - return VerilatedImp::fdNewMcd(filename.c_str()); + return Verilated::threadContextp()->impp()->fdNewMcd(filename.c_str()); } -void VL_FFLUSH_I(IData fdi) VL_MT_SAFE { VerilatedImp::fdFlush(fdi); } +void VL_FFLUSH_I(IData fdi) VL_MT_SAFE { Verilated::threadContextp()->impp()->fdFlush(fdi); } IData VL_FSEEK_I(IData fdi, IData offset, IData origin) VL_MT_SAFE { - return VerilatedImp::fdSeek(fdi, offset, origin); + return Verilated::threadContextp()->impp()->fdSeek(fdi, offset, origin); } -IData VL_FTELL_I(IData fdi) VL_MT_SAFE { return VerilatedImp::fdTell(fdi); } +IData VL_FTELL_I(IData fdi) VL_MT_SAFE { return Verilated::threadContextp()->impp()->fdTell(fdi); } void VL_FCLOSE_I(IData fdi) VL_MT_SAFE { // While threadsafe, each thread can only access different file handles - VerilatedImp::fdClose(fdi); + Verilated::threadContextp()->impp()->fdClose(fdi); } void VL_SFORMAT_X(int obits, CData& destr, const char* formatp, ...) VL_MT_SAFE { @@ -1336,7 +1324,7 @@ void VL_SFORMAT_X(int obits, CData& destr, const char* formatp, ...) VL_MT_SAFE _vl_vsformat(t_output, formatp, ap); va_end(ap); - _VL_STRING_TO_VINT(obits, &destr, t_output.length(), t_output.c_str()); + _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str()); } void VL_SFORMAT_X(int obits, SData& destr, const char* formatp, ...) VL_MT_SAFE { @@ -1347,7 +1335,7 @@ void VL_SFORMAT_X(int obits, SData& destr, const char* formatp, ...) VL_MT_SAFE _vl_vsformat(t_output, formatp, ap); va_end(ap); - _VL_STRING_TO_VINT(obits, &destr, t_output.length(), t_output.c_str()); + _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str()); } void VL_SFORMAT_X(int obits, IData& destr, const char* formatp, ...) VL_MT_SAFE { @@ -1358,7 +1346,7 @@ void VL_SFORMAT_X(int obits, IData& destr, const char* formatp, ...) VL_MT_SAFE _vl_vsformat(t_output, formatp, ap); va_end(ap); - _VL_STRING_TO_VINT(obits, &destr, t_output.length(), t_output.c_str()); + _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str()); } void VL_SFORMAT_X(int obits, QData& destr, const char* formatp, ...) VL_MT_SAFE { @@ -1369,7 +1357,7 @@ void VL_SFORMAT_X(int obits, QData& destr, const char* formatp, ...) VL_MT_SAFE _vl_vsformat(t_output, formatp, ap); va_end(ap); - _VL_STRING_TO_VINT(obits, &destr, t_output.length(), t_output.c_str()); + _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str()); } void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...) VL_MT_SAFE { @@ -1380,7 +1368,7 @@ void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...) VL_MT_SAFE { _vl_vsformat(t_output, formatp, ap); va_end(ap); - _VL_STRING_TO_VINT(obits, destp, t_output.length(), t_output.c_str()); + _vl_string_to_vint(obits, destp, t_output.length(), t_output.c_str()); } void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp, ...) VL_MT_SAFE { @@ -1424,7 +1412,7 @@ void VL_FWRITEF(IData fpi, const char* formatp, ...) VL_MT_SAFE { _vl_vsformat(t_output, formatp, ap); va_end(ap); - VerilatedImp::fdWrite(fpi, t_output); + Verilated::threadContextp()->impp()->fdWrite(fpi, t_output); } IData VL_FSCANF_IX(IData fpi, const char* formatp, ...) VL_MT_SAFE { @@ -1533,13 +1521,13 @@ IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE { } IData VL_SYSTEM_IW(int lhswords, WDataInP lhsp) VL_MT_SAFE { char filenamez[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1]; - _VL_VINT_TO_STRING(lhswords * VL_EDATASIZE, filenamez, lhsp); + _vl_vint_to_string(lhswords * VL_EDATASIZE, filenamez, lhsp); int code = system(filenamez); // Yes, system() is threadsafe return code >> 8; // Want exit status } IData VL_TESTPLUSARGS_I(const char* formatp) VL_MT_SAFE { - const std::string& match = VerilatedImp::argPlusMatch(formatp); + const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(formatp); return match.empty() ? 0 : 1; } @@ -1567,7 +1555,7 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M } } - const std::string& match = VerilatedImp::argPlusMatch(prefix.c_str()); + const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str()); const char* dp = match.c_str() + 1 /*leading + */ + prefix.length(); if (match.empty()) return 0; @@ -1612,7 +1600,7 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M default: // Other simulators simply return 0 in these cases and don't error out return 0; } - _VL_CLEAN_INPLACE_W(rbits, rwp); + _vl_clean_inplace_w(rbits, rwp); return 1; } IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE { @@ -1636,7 +1624,7 @@ IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_S } } } - const std::string& match = VerilatedImp::argPlusMatch(prefix.c_str()); + const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str()); const char* dp = match.c_str() + 1 /*leading + */ + prefix.length(); if (match.empty()) return 0; rdr = std::string(dp); @@ -1644,12 +1632,14 @@ IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_S } const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE { - const std::string& match = VerilatedImp::argPlusMatch(prefixp); + const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefixp); static VL_THREAD_LOCAL char t_outstr[VL_VALUE_STRING_MAX_WIDTH]; if (match.empty()) return nullptr; - t_outstr[0] = '\0'; - strncat(t_outstr, match.c_str() + strlen(prefixp) + 1, // +1 to skip the "+" - VL_VALUE_STRING_MAX_WIDTH - 1); + char* dp = t_outstr; + for (const char* sp = match.c_str() + strlen(prefixp) + 1; // +1 to skip the "+" + *sp && (dp - t_outstr) < (VL_VALUE_STRING_MAX_WIDTH - 2);) + *dp++ = *sp++; + *dp++ = '\0'; return t_outstr; } @@ -1676,7 +1666,7 @@ std::string VL_TOUPPER_NN(const std::string& ld) VL_MT_SAFE { } std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) VL_MT_SAFE { - // See also _VL_VINT_TO_STRING + // See also _vl_vint_to_string char destout[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1]; int obits = lwords * VL_EDATASIZE; int lsb = obits - 1; @@ -1731,25 +1721,6 @@ IData VL_ATOI_N(const std::string& str, int base) VL_PURE { return static_cast(v); } -//=========================================================================== -// Dumping - -const char* vl_dumpctl_filenamep(bool setit, const std::string& filename) VL_MT_SAFE { - // This function performs both accessing and setting so it's easy to make an in-function static - static VL_THREAD_LOCAL std::string t_filename; - if (setit) { - t_filename = filename; - } else { - static VL_THREAD_LOCAL bool t_warned = false; - if (VL_UNLIKELY(t_filename.empty() && !t_warned)) { - t_warned = true; - VL_PRINTF_MT("%%Warning: $dumpvar ignored as not proceeded by $dumpfile\n"); - return ""; - } - } - return t_filename.c_str(); -} - //=========================================================================== // Readmem/writemem @@ -1912,7 +1883,7 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) { } else { WDataOutP datap = reinterpret_cast(valuep); if (!innum) VL_ZERO_RESET_W(m_bits, datap); - _VL_SHIFTL_INPLACE_W(m_bits, datap, static_cast(shift)); + _vl_shiftl_inplace_w(m_bits, datap, static_cast(shift)); datap[0] |= value; } innum = true; @@ -2181,99 +2152,119 @@ double vl_time_multiplier(int scale) VL_PURE { return pow10[scale]; } } -const char* Verilated::timeunitString() VL_MT_SAFE { return vl_time_str(timeunit()); } -const char* Verilated::timeprecisionString() VL_MT_SAFE { return vl_time_str(timeprecision()); } -void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp) VL_MT_SAFE { +void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp, + const VerilatedContext* contextp) VL_MT_SAFE { VL_PRINTF_MT("Time scale of %s is %s / %s\n", namep, timeunitp, - Verilated::timeprecisionString()); + contextp->timeprecisionString()); } -void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, - int width) VL_MT_SAFE { - VerilatedImp::timeFormatUnits(units); - VerilatedImp::timeFormatPrecision(precision); - VerilatedImp::timeFormatSuffix(suffix); - VerilatedImp::timeFormatWidth(width); +void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, int width, + VerilatedContext* contextp) VL_MT_SAFE { + contextp->impp()->timeFormatUnits(units); + contextp->impp()->timeFormatPrecision(precision); + contextp->impp()->timeFormatSuffix(suffix); + contextp->impp()->timeFormatWidth(width); } -//=========================================================================== -// Verilated:: Methods +//====================================================================== +// VerilatedContext:: Methods -void Verilated::debug(int level) VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - s_ns.s_debug = level; - if (level) { -#ifdef VL_DEBUG - VL_DEBUG_IF(VL_DBG_MSGF("- Verilated::debug is on." - " Message prefix indicates {,}.\n");); -#else - VL_PRINTF_MT("- Verilated::debug attempted," - " but compiled without VL_DEBUG, so messages suppressed.\n" - "- Suggest remake using 'make ... CPPFLAGS=-DVL_DEBUG'\n"); -#endif +VerilatedContext::VerilatedContext() + : m_impdatap{new VerilatedContextImpData} { + Verilated::lastContextp(this); + Verilated::threadContextp(this); + m_ns.m_profThreadsFilename = "profile_threads.dat"; + m_fdps.resize(31); + std::fill(m_fdps.begin(), m_fdps.end(), (FILE*)0); + m_fdFreeMct.resize(30); + for (std::size_t i = 0, id = 1; i < m_fdFreeMct.size(); ++i, ++id) m_fdFreeMct[i] = id; +} + +// Must declare here not in interface, as otherwise forward declarations not known +VerilatedContext::~VerilatedContext() {} + +VerilatedContext::Serialized::Serialized() { + m_timeunit = VL_TIME_UNIT; // Initial value until overriden by _Vconfigure + m_timeprecision = VL_TIME_PRECISION; // Initial value until overriden by _Vconfigure +} + +void VerilatedContext::assertOn(bool flag) VL_MT_SAFE { + const VerilatedLockGuard lock(m_mutex); + m_s.m_assertOn = flag; +} +void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE { + const VerilatedLockGuard lock(m_mutex); + m_s.m_calcUnusedSigs = flag; +} +void VerilatedContext::dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { + const VerilatedLockGuard lock(m_timeDumpMutex); + m_dumpfile = flag; +} +std::string VerilatedContext::dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { + const VerilatedLockGuard lock(m_timeDumpMutex); + if (VL_UNLIKELY(m_dumpfile.empty())) { + VL_PRINTF_MT("%%Warning: $dumpvar ignored as not proceeded by $dumpfile\n"); + return ""; } + return m_dumpfile; } -void Verilated::randReset(int val) VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - s_s.s_randReset = val; +void VerilatedContext::errorCount(int val) VL_MT_SAFE { + const VerilatedLockGuard lock(m_mutex); + m_s.m_errorCount = val; } -void Verilated::randSeed(int val) VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - s_s.s_randSeed = val; - vluint64_t newEpoch = s_s.s_randSeedEpoch + 1; - if (VL_UNLIKELY(newEpoch == 0)) newEpoch = 1; - // Obververs must see new epoch AFTER seed updated -#ifdef VL_THREADED - std::atomic_signal_fence(std::memory_order_release); -#endif - s_s.s_randSeedEpoch = newEpoch; +void VerilatedContext::errorCountInc() VL_MT_SAFE { + const VerilatedLockGuard lock(m_mutex); + ++m_s.m_errorCount; } -vluint64_t Verilated::randSeedDefault64() VL_MT_SAFE { - if (Verilated::randSeed() != 0) { - return ((static_cast(Verilated::randSeed()) << 32) - ^ (static_cast(Verilated::randSeed()))); - } else { - return ((static_cast(vl_sys_rand32()) << 32) - ^ (static_cast(vl_sys_rand32()))); - } +void VerilatedContext::errorLimit(int val) VL_MT_SAFE { + const VerilatedLockGuard lock(m_mutex); + m_s.m_errorLimit = val; } -void Verilated::calcUnusedSigs(bool flag) VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - s_s.s_calcUnusedSigs = flag; +void VerilatedContext::fatalOnError(bool flag) VL_MT_SAFE { + const VerilatedLockGuard lock(m_mutex); + m_s.m_fatalOnError = flag; } -void Verilated::errorCount(int val) VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - s_s.s_errorCount = val; +void VerilatedContext::fatalOnVpiError(bool flag) VL_MT_SAFE { + const VerilatedLockGuard lock(m_mutex); + m_s.m_fatalOnVpiError = flag; } -void Verilated::errorCountInc() VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - ++s_s.s_errorCount; +void VerilatedContext::gotError(bool flag) VL_MT_SAFE { + const VerilatedLockGuard lock(m_mutex); + m_s.m_gotError = flag; } -void Verilated::errorLimit(int val) VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - s_s.s_errorLimit = val; +void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE { + const VerilatedLockGuard lock(m_mutex); + m_s.m_gotFinish = flag; } -void Verilated::gotFinish(bool flag) VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - s_s.s_gotFinish = flag; +void VerilatedContext::profThreadsStart(vluint64_t flag) VL_MT_SAFE { + const VerilatedLockGuard lock(m_mutex); + m_ns.m_profThreadsStart = flag; } -void Verilated::assertOn(bool flag) VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - s_s.s_assertOn = flag; +void VerilatedContext::profThreadsWindow(vluint64_t flag) VL_MT_SAFE { + const VerilatedLockGuard lock(m_mutex); + m_ns.m_profThreadsWindow = flag; } -void Verilated::fatalOnVpiError(bool flag) VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - s_s.s_fatalOnVpiError = flag; +void VerilatedContext::profThreadsFilename(const std::string& flag) VL_MT_SAFE { + const VerilatedLockGuard lock(m_mutex); + m_ns.m_profThreadsFilename = flag; } -void Verilated::timeunit(int value) VL_MT_SAFE { +std::string VerilatedContext::profThreadsFilename() const VL_MT_SAFE { + const VerilatedLockGuard lock(m_mutex); + return m_ns.m_profThreadsFilename; +} +void VerilatedContext::randReset(int val) VL_MT_SAFE { + const VerilatedLockGuard lock(m_mutex); + m_s.m_randReset = val; +} +void VerilatedContext::timeunit(int value) VL_MT_SAFE { if (value < 0) value = -value; // Stored as 0..15 - const VerilatedLockGuard lock(s_mutex); - s_s.s_timeunit = value; + const VerilatedLockGuard lock(m_mutex); + m_s.m_timeunit = value; } -void Verilated::timeprecision(int value) VL_MT_SAFE { +void VerilatedContext::timeprecision(int value) VL_MT_SAFE { if (value < 0) value = -value; // Stored as 0..15 - const VerilatedLockGuard lock(s_mutex); - s_s.s_timeprecision = value; + const VerilatedLockGuard lock(m_mutex); + m_s.m_timeprecision = value; #ifdef SYSTEMC_VERSION sc_time sc_res = sc_get_time_resolution(); int sc_prec = 99; @@ -2302,18 +2293,243 @@ void Verilated::timeprecision(int value) VL_MT_SAFE { } #endif } -void Verilated::profThreadsStart(vluint64_t flag) VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - s_ns.s_profThreadsStart = flag; +const char* VerilatedContext::timeunitString() const VL_MT_SAFE { return vl_time_str(timeunit()); } +const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE { + return vl_time_str(timeprecision()); } -void Verilated::profThreadsWindow(vluint64_t flag) VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - s_ns.s_profThreadsWindow = flag; + +void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) { + const VerilatedLockGuard lock(m_argMutex); + m_args.m_argVec.clear(); // Empty first, then add + impp()->commandArgsAddGuts(argc, argv); } -void Verilated::profThreadsFilenamep(const char* flagp) VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - if (s_ns.s_profThreadsFilenamep) free(const_cast(s_ns.s_profThreadsFilenamep)); - s_ns.s_profThreadsFilenamep = strdup(flagp); +void VerilatedContext::commandArgsAdd(int argc, const char** argv) + VL_MT_SAFE_EXCLUDES(m_argMutex) { + const VerilatedLockGuard lock(m_argMutex); + impp()->commandArgsAddGuts(argc, argv); +} +const char* VerilatedContext::commandArgsPlusMatch(const char* prefixp) + VL_MT_SAFE_EXCLUDES(m_argMutex) { + const std::string& match = impp()->argPlusMatch(prefixp); + static VL_THREAD_LOCAL char t_outstr[VL_VALUE_STRING_MAX_WIDTH]; + if (match.empty()) return ""; + char* dp = t_outstr; + for (const char* sp = match.c_str(); *sp && (dp - t_outstr) < (VL_VALUE_STRING_MAX_WIDTH - 2);) + *dp++ = *sp++; + *dp++ = '\0'; + return t_outstr; +} +void VerilatedContext::internalsDump() const VL_MT_SAFE { + VL_PRINTF_MT("internalsDump:\n"); + VerilatedImp::versionDump(); + impp()->commandArgDump(); + impp()->scopesDump(); + VerilatedImp::exportsDump(); + VerilatedImp::userDump(); +} + +//====================================================================== +// VerilatedContextImp:: Methods - command line + +void VerilatedContextImp::commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(m_argMutex) { + if (!m_args.m_argVecLoaded) m_args.m_argVec.clear(); + for (int i = 0; i < argc; ++i) { + m_args.m_argVec.push_back(argv[i]); + commandArgVl(argv[i]); + } + m_args.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok +} +void VerilatedContextImp::commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex) { + const VerilatedLockGuard lock(m_argMutex); + VL_PRINTF_MT(" Argv:"); + for (const auto& i : m_args.m_argVec) VL_PRINTF_MT(" %s", i.c_str()); + VL_PRINTF_MT("\n"); +} +std::string VerilatedContextImp::argPlusMatch(const char* prefixp) + VL_MT_SAFE_EXCLUDES(m_argMutex) { + const VerilatedLockGuard lock(m_argMutex); + // Note prefixp does not include the leading "+" + size_t len = strlen(prefixp); + if (VL_UNLIKELY(!m_args.m_argVecLoaded)) { + m_args.m_argVecLoaded = true; // Complain only once + VL_FATAL_MT("unknown", 0, "", + "%Error: Verilog called $test$plusargs or $value$plusargs without" + " testbench C first calling Verilated::commandArgs(argc,argv)."); + } + for (const auto& i : m_args.m_argVec) { + if (i[0] == '+') { + if (0 == strncmp(prefixp, i.c_str() + 1, len)) return i; + } + } + return ""; +} +// Return string representing current argv +// Only used by VPI so uses static storage, only supports most recent called context +std::pair VerilatedContextImp::argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex) { + const VerilatedLockGuard lock(m_argMutex); + static bool s_loaded = false; + static int s_argc = 0; + static char** s_argvp = nullptr; + if (VL_UNLIKELY(!s_loaded)) { + s_loaded = true; + s_argc = m_args.m_argVec.size(); + s_argvp = new char*[s_argc + 1]; + int in = 0; + for (const auto& i : m_args.m_argVec) { + s_argvp[in] = new char[i.length() + 1]; + strcpy(s_argvp[in], i.c_str()); + ++in; + } + s_argvp[s_argc] = nullptr; + } + return std::make_pair(s_argc, s_argvp); +} + +void VerilatedContextImp::commandArgVl(const std::string& arg) { + if (0 == strncmp(arg.c_str(), "+verilator+", strlen("+verilator+"))) { + std::string value; + if (arg == "+verilator+debug") { + Verilated::debug(4); + } else if (commandArgVlValue(arg, "+verilator+debugi+", value /*ref*/)) { + Verilated::debug(atoi(value.c_str())); + } else if (commandArgVlValue(arg, "+verilator+error+limit+", value /*ref*/)) { + errorLimit(atoi(value.c_str())); + } else if (arg == "+verilator+help") { + VerilatedImp::versionDump(); + VL_PRINTF_MT("For help, please see 'verilator --help'\n"); + VL_FATAL_MT("COMMAND_LINE", 0, "", + "Exiting due to command line argument (not an error)"); + } else if (commandArgVlValue(arg, "+verilator+prof+threads+start+", value /*ref*/)) { + profThreadsStart(atoll(value.c_str())); + } else if (commandArgVlValue(arg, "+verilator+prof+threads+window+", value /*ref*/)) { + profThreadsWindow(atol(value.c_str())); + } else if (commandArgVlValue(arg, "+verilator+prof+threads+file+", value /*ref*/)) { + profThreadsFilename(value); + } else if (commandArgVlValue(arg, "+verilator+rand+reset+", value /*ref*/)) { + randReset(atoi(value.c_str())); + } else if (commandArgVlValue(arg, "+verilator+seed+", value /*ref*/)) { + randSeed(atoi(value.c_str())); + } else if (arg == "+verilator+noassert") { + assertOn(false); + } else if (arg == "+verilator+V") { + VerilatedImp::versionDump(); // Someday more info too + VL_FATAL_MT("COMMAND_LINE", 0, "", + "Exiting due to command line argument (not an error)"); + } else if (arg == "+verilator+version") { + VerilatedImp::versionDump(); + VL_FATAL_MT("COMMAND_LINE", 0, "", + "Exiting due to command line argument (not an error)"); + } else { + VL_PRINTF_MT("%%Warning: Unknown +verilator runtime argument: '%s'\n", arg.c_str()); + } + } +} +bool VerilatedContextImp::commandArgVlValue(const std::string& arg, const std::string& prefix, + std::string& valuer) { + size_t len = prefix.length(); + if (0 == strncmp(prefix.c_str(), arg.c_str(), len)) { + valuer = arg.substr(len); + return true; + } else { + return false; + } +} + +//====================================================================== +// VerilatedContext:: + VerilatedContextImp:: Methods - random + +void VerilatedContext::randSeed(int val) VL_MT_SAFE { + // As we have per-thread state, the epoch must be static, + // and so the rand seed's mutex must also be static + const VerilatedLockGuard lock(VerilatedContextImp::s().s_randMutex); + m_s.m_randSeed = val; + vluint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1; + // Obververs must see new epoch AFTER seed updated +#ifdef VL_THREADED + std::atomic_signal_fence(std::memory_order_release); +#endif + VerilatedContextImp::s().s_randSeedEpoch = newEpoch; +} +vluint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE { + if (randSeed() != 0) { + return ((static_cast(randSeed()) << 32) + ^ (static_cast(randSeed()))); + } else { + return ((static_cast(vl_sys_rand32()) << 32) + ^ (static_cast(vl_sys_rand32()))); + } +} + +//====================================================================== +// VerilatedContext:: Methods - scopes + +void VerilatedContext::scopesDump() const VL_MT_SAFE { + const VerilatedLockGuard lock(m_impdatap->m_nameMutex); + VL_PRINTF_MT(" scopesDump:\n"); + for (const auto& i : m_impdatap->m_nameMap) { + const VerilatedScope* scopep = i.second; + scopep->scopeDump(); + } + VL_PRINTF_MT("\n"); +} + +void VerilatedContextImp::scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE { + // Slow ok - called once/scope at construction + const VerilatedLockGuard lock(m_impdatap->m_nameMutex); + const auto it = m_impdatap->m_nameMap.find(scopep->name()); + if (it == m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.emplace(scopep->name(), scopep); +} +void VerilatedContextImp::scopeErase(const VerilatedScope* scopep) VL_MT_SAFE { + // Slow ok - called once/scope at destruction + const VerilatedLockGuard lock(m_impdatap->m_nameMutex); + VerilatedImp::userEraseScope(scopep); + const auto it = m_impdatap->m_nameMap.find(scopep->name()); + if (it != m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.erase(it); +} +const VerilatedScope* VerilatedContext::scopeFind(const char* namep) const VL_MT_SAFE { + // Thread save only assuming this is called only after model construction completed + const VerilatedLockGuard lock(m_impdatap->m_nameMutex); + // If too slow, can assume this is only VL_MT_SAFE_POSINIT + const auto& it = m_impdatap->m_nameMap.find(namep); + if (VL_UNLIKELY(it == m_impdatap->m_nameMap.end())) return nullptr; + return it->second; +} +const VerilatedScopeNameMap* VerilatedContext::scopeNameMap() VL_MT_SAFE { + return &(impp()->m_impdatap->m_nameMap); +} + +//====================================================================== +// VerilatedSyms:: Methods + +VerilatedSyms::VerilatedSyms(VerilatedContext* contextp) + : _vm_contextp__(contextp ? contextp : Verilated::threadContextp()) { + Verilated::threadContextp(_vm_contextp__); +#ifdef VL_THREADED + __Vm_evalMsgQp = new VerilatedEvalMsgQueue; +#endif +} + +VerilatedSyms::~VerilatedSyms() { +#ifdef VL_THREADED + delete __Vm_evalMsgQp; +#endif +} + +//=========================================================================== +// Verilated:: Methods + +void Verilated::debug(int level) VL_MT_SAFE { + s_debug = level; + if (level) { +#ifdef VL_DEBUG + VL_DEBUG_IF(VL_DBG_MSGF("- Verilated::debug is on." + " Message prefix indicates {,}.\n");); +#else + VL_PRINTF_MT("- Verilated::debug attempted," + " but compiled without VL_DEBUG, so messages suppressed.\n" + "- Suggest remake using 'make ... CPPFLAGS=-DVL_DEBUG'\n"); +#endif + } } const char* Verilated::catName(const char* n1, const char* n2, const char* delimiter) VL_MT_SAFE { @@ -2322,14 +2538,16 @@ const char* Verilated::catName(const char* n1, const char* n2, const char* delim static VL_THREAD_LOCAL char* t_strp = nullptr; static VL_THREAD_LOCAL size_t t_len = 0; size_t newlen = strlen(n1) + strlen(n2) + strlen(delimiter) + 1; - if (!t_strp || newlen > t_len) { + if (VL_UNLIKELY(!t_strp || newlen > t_len)) { if (t_strp) delete[] t_strp; t_strp = new char[newlen]; t_len = newlen; } - strcpy(t_strp, n1); - if (*n1) strcat(t_strp, delimiter); - strcat(t_strp, n2); + char* dp = t_strp; + for (const char* sp = n1; *sp;) *dp++ = *sp++; + for (const char* sp = delimiter; *sp;) *dp++ = *sp++; + for (const char* sp = n2; *sp;) *dp++ = *sp++; + *dp++ = '\0'; return t_strp; } @@ -2339,8 +2557,12 @@ const char* Verilated::catName(const char* n1, const char* n2, const char* delim // Keeping these out of class Verilated to avoid having to include // in verilated.h (for compilation speed) typedef std::list> VoidPCbList; -static VoidPCbList s_flushCbs; -static VoidPCbList s_exitCbs; +static struct { + VerilatedMutex s_flushMutex; + VoidPCbList s_flushCbs VL_GUARDED_BY(s_flushMutex); + VerilatedMutex s_exitMutex; + VoidPCbList s_exitCbs VL_GUARDED_BY(s_exitMutex); +} VlCbStatic; static void addCb(Verilated::VoidPCb cb, void* datap, VoidPCbList& cbs) { std::pair pair(cb, datap); @@ -2356,16 +2578,25 @@ static void runCallbacks(const VoidPCbList& cbs) VL_MT_SAFE { } void Verilated::addFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - addCb(cb, datap, s_flushCbs); + const VerilatedLockGuard lock(VlCbStatic.s_flushMutex); + addCb(cb, datap, VlCbStatic.s_flushCbs); } void Verilated::removeFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - removeCb(cb, datap, s_flushCbs); + const VerilatedLockGuard lock(VlCbStatic.s_flushMutex); + removeCb(cb, datap, VlCbStatic.s_flushCbs); } void Verilated::runFlushCallbacks() VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - runCallbacks(s_flushCbs); + // Flush routines may call flush, so avoid mutex deadlock +#ifdef VL_THREADED + static std::atomic s_recursing; +#else + static int s_recursing = 0; +#endif + if (!s_recursing++) { + const VerilatedLockGuard lock(VlCbStatic.s_flushMutex); + runCallbacks(VlCbStatic.s_flushCbs); + } + --s_recursing; fflush(stderr); fflush(stdout); // When running internal code coverage (gcc --coverage, as opposed to @@ -2375,37 +2606,29 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE { } void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - addCb(cb, datap, s_exitCbs); + const VerilatedLockGuard lock(VlCbStatic.s_exitMutex); + addCb(cb, datap, VlCbStatic.s_exitCbs); } void Verilated::removeExitCb(VoidPCb cb, void* datap) VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - removeCb(cb, datap, s_exitCbs); + const VerilatedLockGuard lock(VlCbStatic.s_exitMutex); + removeCb(cb, datap, VlCbStatic.s_exitCbs); } void Verilated::runExitCallbacks() VL_MT_SAFE { - const VerilatedLockGuard lock(s_mutex); - runCallbacks(s_exitCbs); +#ifdef VL_THREADED + static std::atomic s_recursing; +#else + static int s_recursing = 0; +#endif + if (!s_recursing++) { + const VerilatedLockGuard lock(VlCbStatic.s_exitMutex); + runCallbacks(VlCbStatic.s_exitCbs); + } + --s_recursing; } const char* Verilated::productName() VL_PURE { return VERILATOR_PRODUCT; } const char* Verilated::productVersion() VL_PURE { return VERILATOR_VERSION; } -void Verilated::commandArgs(int argc, const char** argv) VL_MT_SAFE { - const VerilatedLockGuard lock(s_args.m_argMutex); - s_args.argc = argc; - s_args.argv = argv; - VerilatedImp::commandArgs(argc, argv); -} - -const char* Verilated::commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE { - const std::string& match = VerilatedImp::argPlusMatch(prefixp); - static VL_THREAD_LOCAL char t_outstr[VL_VALUE_STRING_MAX_WIDTH]; - if (match.empty()) return ""; - t_outstr[0] = '\0'; - strncat(t_outstr, match.c_str(), VL_VALUE_STRING_MAX_WIDTH - 1); - return t_outstr; -} - void Verilated::nullPointerError(const char* filename, int linenum) VL_MT_SAFE { // Slowpath - Called only on error VL_FATAL_MT(filename, linenum, "", "Null pointer dereferenced"); @@ -2435,22 +2658,10 @@ void Verilated::quiesce() VL_MT_SAFE { #endif } -void Verilated::internalsDump() VL_MT_SAFE { VerilatedImp::internalsDump(); } - -void Verilated::scopesDump() VL_MT_SAFE { VerilatedImp::scopesDump(); } - -const VerilatedScope* Verilated::scopeFind(const char* namep) VL_MT_SAFE { - return VerilatedImp::scopeFind(namep); -} - int Verilated::exportFuncNum(const char* namep) VL_MT_SAFE { return VerilatedImp::exportFind(namep); } -const VerilatedScopeNameMap* Verilated::scopeNameMap() VL_MT_SAFE { - return VerilatedImp::scopeNameMap(); -} - #ifdef VL_THREADED void Verilated::endOfThreadMTaskGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE { VL_DEBUG_IF(VL_DBG_MSGF("End of thread mtask\n");); @@ -2467,138 +2678,13 @@ void Verilated::endOfEval(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE { } #endif -//=========================================================================== -// VerilatedImp:: Constructors - -// verilated.o may exist both in protect-lib and main module. -// Both the main module and the protect-lib refer the same instance of -// static variables such as Verilated or VerilatedImplData. -// This is important to share the state such as Verilated::gotFinish. -// But the sharing may cause double-free error when shutting down because destructors -// are called twice. -// 1st time:From protect-lib shared object on the way of unloading after exiting main() -// 2nd time:From main executable. -// -// To avoid the trouble, all member variables are enclosed in VerilatedImpU union. -// ctor nor dtor of members are not called automatically. -// VerilatedInitializer::setup() and teardown() guarantees to initialize/destruct just once. - -void VerilatedImp::setup() { new (&VerilatedImp::s_s) VerilatedImpData(); } -void VerilatedImp::teardown() { VerilatedImp::s_s.~VerilatedImpU(); } - //=========================================================================== // VerilatedImp:: Methods -std::string VerilatedImp::timeFormatSuffix() VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.v.m_sergMutex); - return s_s.v.m_serg.m_timeFormatSuffix; -} -void VerilatedImp::timeFormatSuffix(const std::string& value) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.v.m_sergMutex); - s_s.v.m_serg.m_timeFormatSuffix = value; -} -void VerilatedImp::timeFormatUnits(int value) VL_MT_SAFE { s_s.v.m_ser.m_timeFormatUnits = value; } -void VerilatedImp::timeFormatPrecision(int value) VL_MT_SAFE { - s_s.v.m_ser.m_timeFormatPrecision = value; -} -void VerilatedImp::timeFormatWidth(int value) VL_MT_SAFE { s_s.v.m_ser.m_timeFormatWidth = value; } - -void VerilatedImp::internalsDump() VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.v.m_argMutex); - VL_PRINTF_MT("internalsDump:\n"); - versionDump(); - VL_PRINTF_MT(" Argv:"); - for (const auto& i : s_s.v.m_argVec) VL_PRINTF_MT(" %s", i.c_str()); - VL_PRINTF_MT("\n"); - scopesDump(); - exportsDump(); - userDump(); -} void VerilatedImp::versionDump() VL_MT_SAFE { VL_PRINTF_MT(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion()); } -void VerilatedImp::commandArgs(int argc, const char** argv) VL_EXCLUDES(s_s.v.m_argMutex) { - const VerilatedLockGuard lock(s_s.v.m_argMutex); - s_s.v.m_argVec.clear(); // Always clear - commandArgsAddGuts(argc, argv); -} -void VerilatedImp::commandArgsAdd(int argc, const char** argv) VL_EXCLUDES(s_s.v.m_argMutex) { - const VerilatedLockGuard lock(s_s.v.m_argMutex); - commandArgsAddGuts(argc, argv); -} -void VerilatedImp::commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s_s.v.m_argMutex) { - if (!s_s.v.m_argVecLoaded) s_s.v.m_argVec.clear(); - for (int i = 0; i < argc; ++i) { - s_s.v.m_argVec.push_back(argv[i]); - commandArgVl(argv[i]); - } - s_s.v.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok -} -void VerilatedImp::commandArgVl(const std::string& arg) { - if (0 == strncmp(arg.c_str(), "+verilator+", strlen("+verilator+"))) { - std::string value; - if (arg == "+verilator+debug") { - Verilated::debug(4); - } else if (commandArgVlValue(arg, "+verilator+debugi+", value /*ref*/)) { - Verilated::debug(atoi(value.c_str())); - } else if (commandArgVlValue(arg, "+verilator+error+limit+", value /*ref*/)) { - Verilated::errorLimit(atoi(value.c_str())); - } else if (arg == "+verilator+help") { - versionDump(); - VL_PRINTF_MT("For help, please see 'verilator --help'\n"); - VL_FATAL_MT("COMMAND_LINE", 0, "", - "Exiting due to command line argument (not an error)"); - } else if (commandArgVlValue(arg, "+verilator+prof+threads+start+", value /*ref*/)) { - Verilated::profThreadsStart(atoll(value.c_str())); - } else if (commandArgVlValue(arg, "+verilator+prof+threads+window+", value /*ref*/)) { - Verilated::profThreadsWindow(atol(value.c_str())); - } else if (commandArgVlValue(arg, "+verilator+prof+threads+file+", value /*ref*/)) { - Verilated::profThreadsFilenamep(value.c_str()); - } else if (commandArgVlValue(arg, "+verilator+rand+reset+", value /*ref*/)) { - Verilated::randReset(atoi(value.c_str())); - } else if (commandArgVlValue(arg, "+verilator+seed+", value /*ref*/)) { - Verilated::randSeed(atoi(value.c_str())); - } else if (arg == "+verilator+noassert") { - Verilated::assertOn(false); - } else if (arg == "+verilator+V") { - versionDump(); // Someday more info too - VL_FATAL_MT("COMMAND_LINE", 0, "", - "Exiting due to command line argument (not an error)"); - } else if (arg == "+verilator+version") { - versionDump(); - VL_FATAL_MT("COMMAND_LINE", 0, "", - "Exiting due to command line argument (not an error)"); - } else { - VL_PRINTF_MT("%%Warning: Unknown +verilator runtime argument: '%s'\n", arg.c_str()); - } - } -} -bool VerilatedImp::commandArgVlValue(const std::string& arg, const std::string& prefix, - std::string& valuer) { - size_t len = prefix.length(); - if (0 == strncmp(prefix.c_str(), arg.c_str(), len)) { - valuer = arg.substr(len); - return true; - } else { - return false; - } -} - -//====================================================================== -// VerilatedSyms:: Methods - -VerilatedSyms::VerilatedSyms() { -#ifdef VL_THREADED - __Vm_evalMsgQp = new VerilatedEvalMsgQueue; -#endif -} -VerilatedSyms::~VerilatedSyms() { -#ifdef VL_THREADED - delete __Vm_evalMsgQp; -#endif -} - //=========================================================================== // VerilatedModule:: Methods @@ -2652,7 +2738,7 @@ void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const VerilatedScope::~VerilatedScope() { // Memory cleanup - not called during normal operation - VerilatedImp::scopeErase(this); + Verilated::threadContextp()->impp()->scopeErase(this); if (m_namep) VL_DO_CLEAR(delete[] m_namep, m_namep = nullptr); if (m_callbacksp) VL_DO_CLEAR(delete[] m_callbacksp, m_callbacksp = nullptr); if (m_varsp) VL_DO_CLEAR(delete m_varsp, m_varsp = nullptr); @@ -2667,13 +2753,17 @@ void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const m_symsp = symsp; m_type = type; m_timeunit = timeunit; - char* namep = new char[strlen(prefixp) + strlen(suffixp) + 2]; - strcpy(namep, prefixp); - if (*prefixp && *suffixp) strcat(namep, "."); - strcat(namep, suffixp); - m_namep = namep; + { + char* namep = new char[strlen(prefixp) + strlen(suffixp) + 2]; + char* dp = namep; + for (const char* sp = prefixp; *sp;) *dp++ = *sp++; + if (*prefixp && *suffixp) *dp++ = '.'; + for (const char* sp = suffixp; *sp;) *dp++ = *sp++; + *dp++ = '\0'; + m_namep = namep; + } m_identifierp = identifier; - VerilatedImp::scopeInsert(this); + Verilated::threadContextp()->impp()->scopeInsert(this); } void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE { diff --git a/include/verilated.h b/include/verilated.h index b78c3ad21..580cc2753 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -16,12 +16,15 @@ /// all C++ files it generates. It contains standard macros and /// classes required by the Verilated code. /// +/// Those macro/function/variable starting or ending in _ are internal, +/// however many of the other function/macros here are also internal. +/// /// Code available from: https://verilator.org /// //************************************************************************* -#ifndef _VERILATED_H_ -#define _VERILATED_H_ 1 ///< Header Guard +#ifndef VERILATOR_VERILATED_H_ +#define VERILATOR_VERILATED_H_ ///< Header Guard // clang-format off #include "verilatedos.h" @@ -35,6 +38,9 @@ #include #include #include +#include +#include +#include // avoided to reduce compile time // avoided and instead in verilated_heavy.h to reduce compile time // avoided and instead in verilated_heavy.h to reduce compile time @@ -84,6 +90,9 @@ typedef EData WData; ///< Verilated pack data, >64 bits, as an array typedef const WData* WDataInP; ///< Array input to a function typedef WData* WDataOutP; ///< Array output from a function +class VerilatedContextImp; +class VerilatedContextImpData; +class VerilatedCovContext; class VerilatedEvalMsgQueue; class VerilatedScopeNameMap; class VerilatedVar; @@ -119,7 +128,7 @@ enum VerilatedVarFlags { }; //========================================================================= -/// Mutex and threading support +// Mutex and threading support /// Return current thread ID (or 0), not super fast, cache if needed extern vluint32_t VL_THREAD_ID() VL_MT_SAFE; @@ -132,7 +141,9 @@ extern vluint32_t VL_THREAD_ID() VL_MT_SAFE; class VL_CAPABILITY("mutex") VerilatedMutex final { private: std::mutex m_mutex; // Mutex + public: + /// Construct mutex (without locking it) VerilatedMutex() = default; ~VerilatedMutex() = default; const VerilatedMutex& operator!() const { return *this; } // For -fthread_safety @@ -162,51 +173,57 @@ private: VerilatedMutex& m_mutexr; public: + /// Construct and hold given mutex lock until destruction or unlock() explicit VerilatedLockGuard(VerilatedMutex& mutexr) VL_ACQUIRE(mutexr) : m_mutexr(mutexr) { // Need () or GCC 4.8 false warning m_mutexr.lock(); } + /// Destruct and unlock the mutex ~VerilatedLockGuard() VL_RELEASE() { m_mutexr.unlock(); } + /// Unlock the mutex void lock() VL_ACQUIRE() { m_mutexr.lock(); } + /// Lock the mutex void unlock() VL_RELEASE() { m_mutexr.unlock(); } }; #else // !VL_THREADED -/// Empty non-threaded mutex to avoid #ifdefs in consuming code +// Empty non-threaded mutex to avoid #ifdefs in consuming code class VerilatedMutex final { public: - void lock() {} - void unlock() {} + void lock() {} // LCOV_EXCL_LINE + void unlock() {} // LCOV_EXCL_LINE }; -/// Empty non-threaded lock guard to avoid #ifdefs in consuming code +// Empty non-threaded lock guard to avoid #ifdefs in consuming code class VerilatedLockGuard final { VL_UNCOPYABLE(VerilatedLockGuard); public: explicit VerilatedLockGuard(VerilatedMutex&) {} ~VerilatedLockGuard() = default; - void lock() {} - void unlock() {} + void lock() {} // LCOV_EXCL_LINE + void unlock() {} // LCOV_EXCL_LINE }; #endif // VL_THREADED -/// Remember the calling thread at construction time, and make sure later calls use same thread +// Internals: Remember the calling thread at construction time, and make +// sure later calls use same thread + class VerilatedAssertOneThread final { // MEMBERS #if defined(VL_THREADED) && defined(VL_DEBUG) vluint32_t m_threadid; /// Thread that is legal public: // CONSTRUCTORS - /// The constructor establishes the thread id for all later calls. - /// If necessary, a different class could be made that inits it otherwise. + // The constructor establishes the thread id for all later calls. + // If necessary, a different class could be made that inits it otherwise. VerilatedAssertOneThread() : m_threadid{VL_THREAD_ID()} {} ~VerilatedAssertOneThread() { check(); } // METHODS - /// Check that the current thread ID is the same as the construction thread ID + // Check that the current thread ID is the same as the construction thread ID void check() VL_MT_UNSAFE_ONE { if (VL_UNCOVERABLE(m_threadid != VL_THREAD_ID())) { if (m_threadid == 0) { @@ -216,12 +233,10 @@ public: } } } - void changeThread() { m_threadid = 0; } // Allow intentional change-of-thread static void fatal_different() VL_MT_SAFE; #else // !VL_THREADED || !VL_DEBUG public: void check() {} - void changeThread() {} #endif }; @@ -284,21 +299,273 @@ public: #endif // clang-format on +//=========================================================================== +// Internal: Base class to allow virtual destruction + +class VerilatedVirtualBase VL_NOT_FINAL { +public: + VerilatedVirtualBase() = default; + virtual ~VerilatedVirtualBase() = default; +}; + +//=========================================================================== +/// Verilator simulation context +/// +/// The VerilatedContext contains the information common across all models +/// that are interconnected, for example this contains the simulation time +/// and if $finish was executed. +/// +/// VerilatedContexts maybe created by the user wrapper code and passed +/// when a model is created. If this is not done, then Verilator will use +/// the Verilated::defaultContextp()'s global context. + +class VerilatedContext VL_NOT_FINAL { + friend class VerilatedContextImp; + +protected: + // MEMBERS + // Slow path variables + mutable VerilatedMutex m_mutex; // Mutex for most s_s/s_ns members, when VL_THREADED + + struct Serialized { // All these members serialized/deserialized + // No std::strings or pointers or will serialize badly! + // Fast path + bool m_assertOn = true; // Assertions are enabled + bool m_calcUnusedSigs = false; // Waves file on, need all signals calculated + bool m_fatalOnError = true; // Fatal on $stop/non-fatal error + bool m_fatalOnVpiError = true; // Fatal on vpi error/unsupported + bool m_gotError = false; // A $finish statement executed + bool m_gotFinish = false; // A $finish or $stop statement executed + vluint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy + // Slow path + vlsint8_t m_timeunit; // Time unit as 0..15 + vlsint8_t m_timeprecision; // Time precision as 0..15 + int m_errorCount = 0; // Number of errors + int m_errorLimit = 1; // Stop on error number + int m_randReset = 0; // Random reset: 0=all 0s, 1=all 1s, 2=random + int m_randSeed = 0; // Random seed: 0=random + enum { UNITS_NONE = 99 }; // Default based on precision + int m_timeFormatUnits = UNITS_NONE; // $timeformat units + int m_timeFormatPrecision = 0; // $timeformat number of decimal places + int m_timeFormatWidth = 20; // $timeformat character width + // CONSTRUCTORS + Serialized(); + ~Serialized() = default; + } m_s; + + mutable VerilatedMutex m_timeDumpMutex; // Protect misc slow strings + std::string m_timeFormatSuffix VL_GUARDED_BY(m_timeDumpMutex); // $timeformat printf format + std::string m_dumpfile VL_GUARDED_BY(m_timeDumpMutex); // $dumpfile setting + + struct NonSerialized { // Non-serialized information + // These are reloaded from on command-line settings, so do not need to persist + // Fast path + vluint64_t m_profThreadsStart = 1; // +prof+threads starting time + vluint32_t m_profThreadsWindow = 2; // +prof+threads window size + // Slow path + std::string m_profThreadsFilename; // +prof+threads filename + } m_ns; + + mutable VerilatedMutex m_argMutex; // Protect m_argVec, m_argVecLoaded + // no need to be save-restored (serialized) the + // assumption is that the restore is allowed to pass different arguments + struct NonSerializedCommandArgs { + // Medium speed + bool m_argVecLoaded = false; // Ever loaded argument list + std::vector m_argVec; // Aargument list + } m_args VL_GUARDED_BY(m_argMutex); + + // Implementation details + std::unique_ptr m_impdatap; + // Coverage access + std::unique_ptr m_coveragep; // Pointer for coveragep() + + // File I/O + // Not serialized + mutable VerilatedMutex m_fdMutex; // Protect m_fdps, m_fdFree + std::vector m_fdps VL_GUARDED_BY(m_fdMutex); // File descriptors + // List of free descriptors (SLOW - FOPEN/CLOSE only) + std::vector m_fdFree VL_GUARDED_BY(m_fdMutex); + // List of free descriptors in the MCT region [4, 32) + std::vector m_fdFreeMct VL_GUARDED_BY(m_fdMutex); + +private: + // CONSTRUCTORS + VL_UNCOPYABLE(VerilatedContext); + +public: + /// Construct context. Also sets Verilated::threadContextp to the created context. + VerilatedContext(); + ~VerilatedContext(); + + // METHODS - User called + + /// Enable assertions + void assertOn(bool flag) VL_MT_SAFE; + /// Return if assertions enabled + bool assertOn() const VL_MT_SAFE { return m_s.m_assertOn; } + /// Enable calculation of unused signals (for traces) + void calcUnusedSigs(bool flag) VL_MT_SAFE; + /// Return if calculating of unused signals (for traces) + bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; } + /// Record command-line arguments, for retrieval by $test$plusargs/$value$plusargs, + /// and for parsing +verilator+ run-time arguments. + /// This should be called before the first model is created. + void commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex); + void commandArgs(int argc, char** argv) VL_MT_SAFE { + commandArgs(argc, const_cast(argv)); + } + /// Add a command-line argument to existing arguments + void commandArgsAdd(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex); + /// Match plusargs with a given prefix. Returns static char* valid only for a single call + const char* commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE_EXCLUDES(m_argMutex); + /// Return VerilatedCovContext, allocate if needed + /// Note if get unresolved reference then likely forgot to link verilated_cov.cpp + VerilatedCovContext* coveragep() VL_MT_SAFE; + /// Set debug level + /// Debug is currently global, but for forward compatibility have a per-context method + static void debug(int val) VL_MT_SAFE; + /// Return debug level + static int debug() VL_MT_SAFE; + /// Set current number of errors/assertions + void errorCount(int val) VL_MT_SAFE; + /// Increment current number of errors/assertions + void errorCountInc() VL_MT_SAFE; + /// Return current number of errors/assertions + int errorCount() const VL_MT_SAFE { return m_s.m_errorCount; } + /// Set number of errors/assertions before stop + void errorLimit(int val) VL_MT_SAFE; + /// Return number of errors/assertions before stop + int errorLimit() const VL_MT_SAFE { return m_s.m_errorLimit; } + /// Set to throw fatal error on $stop/non-fatal ettot + void fatalOnError(bool flag) VL_MT_SAFE; + /// Return if to throw fatal error on $stop/non-fatal + bool fatalOnError() const VL_MT_SAFE { return m_s.m_fatalOnError; } + /// Set to throw fatal error on VPI errors + void fatalOnVpiError(bool flag) VL_MT_SAFE; + /// Return if to throw fatal error on VPI errors + bool fatalOnVpiError() const VL_MT_SAFE { return m_s.m_fatalOnVpiError; } + /// Set if got a $stop or non-fatal error + void gotError(bool flag) VL_MT_SAFE; + /// Return if got a $stop or non-fatal error + bool gotError() const VL_MT_SAFE { return m_s.m_gotError; } + /// Set if got a $finish or $stop/error + void gotFinish(bool flag) VL_MT_SAFE; + /// Return if got a $finish or $stop/error + bool gotFinish() const VL_MT_SAFE { return m_s.m_gotFinish; } + /// Select initial value of otherwise uninitialized signals. + /// 0 = Set to zeros + /// 1 = Set all bits to one + /// 2 = Randomize all bits + void randReset(int val) VL_MT_SAFE; + /// Return randReset value + int randReset() VL_MT_SAFE { return m_s.m_randReset; } + /// Return default random seed + void randSeed(int val) VL_MT_SAFE; + /// Set default random seed, 0 = seed it automatically + int randSeed() const VL_MT_SAFE { return m_s.m_randSeed; } + + // Time handling + /// How Verilator runtime gets the current simulation time: + /// + /// * If using SystemC, time comes from the SystemC kernel-defined + /// sc_time_stamp64(). User's wrapper must not call + /// SimulationContext::time(value) nor timeInc(value). + /// + /// * Else, if SimulationContext::time(value) or + /// SimulationContext::timeInc(value) is ever called with non-zero, + /// then time will come via the context. This allows multiple contexts + /// to exist and have different simulation times. This must not be used + /// with SystemC. Note Verilated::time(value) and + /// Verilated::timeInc(value) call into SimulationContext::time and + /// timeInc, operating on the thread's context. + /// + /// * Else, if VL_TIME_STAMP64 is defined, time comes from the legacy + /// 'vluint64_t vl_time_stamp64()' which must a function be defined by + /// the user's wrapper. + /// + /// * Else, time comes from the legacy 'double sc_time_stamp()' which + /// must be a function defined by the user's wrapper. + vluint64_t time() const VL_MT_SAFE; + /// Set current simulation time. See time() for side effect details + void time(vluint64_t value) VL_MT_SAFE { m_s.m_time = value; } + /// Advance current simulation time. See time() for side effect details + void timeInc(vluint64_t add) VL_MT_UNSAFE { m_s.m_time += add; } + /// Return time units as power-of-ten + int timeunit() const VL_MT_SAFE { return -m_s.m_timeunit; } + /// Set time units as power-of-ten + void timeunit(int value) VL_MT_SAFE; + /// Return time units as IEEE-standard text + const char* timeunitString() const VL_MT_SAFE; + /// Get time precision as power-of-ten + int timeprecision() const VL_MT_SAFE { return -m_s.m_timeprecision; } + /// Return time precision as power-of-ten + void timeprecision(int value) VL_MT_SAFE; + /// Get time precision as IEEE-standard text + const char* timeprecisionString() const VL_MT_SAFE; + + /// Allow traces to at some point be enabled (disables some optimizations) + void traceEverOn(bool flag) VL_MT_SAFE { + if (flag) calcUnusedSigs(true); + } + + /// For debugging, print much of the Verilator internal state. + /// The output of this function may change in future + /// releases - contact the authors before production use. + void internalsDump() const VL_MT_SAFE; + + /// For debugging, print text list of all scope names with + /// dpiImport/Export context. This function may change in future + /// releases - contact the authors before production use. + void scopesDump() const VL_MT_SAFE; + +public: // But for internal use only + // Internal: access to implementation class + VerilatedContextImp* impp() { return reinterpret_cast(this); } + const VerilatedContextImp* impp() const { + return reinterpret_cast(this); + } + + // Internal: $dumpfile + void dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex); + std::string dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex); + + // Internal: --prof-threads related settings + void profThreadsStart(vluint64_t flag) VL_MT_SAFE; + vluint64_t profThreadsStart() const VL_MT_SAFE { return m_ns.m_profThreadsStart; } + void profThreadsWindow(vluint64_t flag) VL_MT_SAFE; + vluint32_t profThreadsWindow() const VL_MT_SAFE { return m_ns.m_profThreadsWindow; } + void profThreadsFilename(const std::string& flag) VL_MT_SAFE; + std::string profThreadsFilename() const VL_MT_SAFE; + + // Internal: Find scope + const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE; + const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE; + + // Internal: Serialization setup + static constexpr size_t serialized1Size() VL_PURE { return sizeof(m_s); } + void* serialized1Ptr() VL_MT_UNSAFE { return &m_s; } +}; + //=========================================================================== /// Verilator symbol table base class +/// Used for internal VPI implementation, and introspection into scopes class VerilatedSyms VL_NOT_FINAL { public: // But for internal use only + // MEMBERS + // Keep first so is at zero offset for fastest code + VerilatedContext* const _vm_contextp__; // Context for current model #ifdef VL_THREADED VerilatedEvalMsgQueue* __Vm_evalMsgQp; #endif - VerilatedSyms(); + explicit VerilatedSyms(VerilatedContext* contextp); // Pass null for default context ~VerilatedSyms(); }; //=========================================================================== -/// Verilator global class information class -/// This class is initialized by main thread only. Reading post-init is thread safe. +/// Verilator scope information class +/// Used for internal VPI implementation, and introspection into scopes class VerilatedScope final { public: @@ -359,55 +626,29 @@ public: class Verilated final { // MEMBERS - // Slow path variables - static VerilatedMutex s_mutex; ///< Mutex for s_s/s_ns members, when VL_THREADED - static struct Serialized { // All these members serialized/deserialized - // Fast path - bool s_calcUnusedSigs; ///< Waves file on, need all signals calculated - bool s_gotFinish; ///< A $finish statement executed - bool s_assertOn; ///< Assertions are enabled - bool s_fatalOnVpiError; ///< Stop on vpi error/unsupported - // Slow path - vlsint8_t s_timeunit; ///< Time unit as 0..15 - vlsint8_t s_timeprecision; ///< Time precision as 0..15 - int s_errorCount; ///< Number of errors - int s_errorLimit; ///< Stop on error number - int s_randReset; ///< Random reset: 0=all 0s, 1=all 1s, 2=random - int s_randSeed; ///< Random seed: 0=random - int s_randSeedEpoch; ///< Number incrementing on each reseed, 0=illegal - Serialized(); - ~Serialized() = default; - } s_s; + // Internal Note: There should be no Serialized state in Verilated::, + // instead serialized state should all be in VerilatedContext:: as by + // definition it needs to vary per-simulation - static struct NonSerialized { // Non-serialized information - // These are reloaded from on command-line settings, so do not need to persist - // Fast path - int s_debug = 0; ///< See accessors... only when VL_DEBUG set - vluint64_t s_profThreadsStart = 1; ///< +prof+threads starting time - vluint32_t s_profThreadsWindow = 2; ///< +prof+threads window size - // Slow path - const char* s_profThreadsFilenamep; ///< +prof+threads filename - void setup(); - void teardown(); - } s_ns; + // Internal note: Globals may multi-construct, see verilated.cpp top. - // no need to be save-restored (serialized) the - // assumption is that the restore is allowed to pass different arguments - static struct CommandArgValues { - VerilatedMutex m_argMutex; ///< Mutex for s_args members, when VL_THREADED - int argc = 0; - const char** argv = nullptr; - CommandArgValues() = default; - ~CommandArgValues() = default; - } s_args; + // Debug is reloaded from on command-line settings, so do not need to persist + static int s_debug; ///< See accessors... only when VL_DEBUG set + + static VerilatedContext* s_lastContextp; ///< Last context constructed/attached // Not covered by mutex, as per-thread static VL_THREAD_LOCAL struct ThreadLocal { + // No non-POD objects here due to this: + // Internal note: Globals may multi-construct, see verilated.cpp top. + + // Fast path + VerilatedContext* t_contextp = nullptr; // Thread's context #ifdef VL_THREADED - vluint32_t t_mtaskId = 0; ///< Current mtask# executing on this thread - vluint32_t t_endOfEvalReqd - = 0; ///< Messages may be pending, thread needs endOf-eval calls + vluint32_t t_mtaskId = 0; // mtask# executing on this thread + // Messages maybe pending on thread, needs end-of-eval calls + vluint32_t t_endOfEvalReqd = 0; #endif const VerilatedScope* t_dpiScopep = nullptr; ///< DPI context scope const char* t_dpiFilename = nullptr; ///< DPI context filename @@ -417,7 +658,6 @@ class Verilated final { ~ThreadLocal() = default; } t_s; -private: friend struct VerilatedInitializer; // CONSTRUCTORS @@ -432,110 +672,150 @@ public: /// Return debug level /// When multithreaded this may not immediately react to another thread /// changing the level (no mutex) - static inline int debug() VL_MT_SAFE { return s_ns.s_debug; } + static inline int debug() VL_MT_SAFE { return s_debug; } #else /// Return constant 0 debug level, so C++'s optimizer rips up static constexpr int debug() VL_PURE { return 0; } #endif - /// Select initial value of otherwise uninitialized signals. - //// - /// 0 = Set to zeros - /// 1 = Set all bits to one - /// 2 = Randomize all bits - static void randReset(int val) VL_MT_SAFE; - static int randReset() VL_MT_SAFE { return s_s.s_randReset; } ///< Return randReset value - static void randSeed(int val) VL_MT_SAFE; - static int randSeed() VL_MT_SAFE { return s_s.s_randSeed; } ///< Return randSeed value - static vluint32_t randSeedEpoch() VL_MT_SAFE { return s_s.s_randSeedEpoch; } - /// Random seed extended to 64 bits, and defaulted if user seed==0 - static vluint64_t randSeedDefault64() VL_MT_SAFE; + /// Set the last VerilatedContext accessed + /// Generally threadContextp(value) should be called instead + static void lastContextp(VerilatedContext* contextp) VL_MT_SAFE { s_lastContextp = contextp; } + /// Return the last VerilatedContext accessed + /// Generally threadContextp() should be called instead + static VerilatedContext* lastContextp() VL_MT_SAFE { + if (!s_lastContextp) lastContextp(defaultContextp()); + return s_lastContextp; + } + /// Set the VerilatedContext used by the current thread - /// Enable calculation of unused signals - static void calcUnusedSigs(bool flag) VL_MT_SAFE; - static bool calcUnusedSigs() VL_MT_SAFE { ///< Return calcUnusedSigs value - return s_s.s_calcUnusedSigs; + /// If using multiple contexts, and threads are created by the user's + /// wrapper (not Verilator itself) then this must be called to set the + /// context that applies to each thread + static void threadContextp(VerilatedContext* contextp) VL_MT_SAFE { + t_s.t_contextp = contextp; + lastContextp(contextp); } - /// Current number of errors/assertions - static void errorCount(int val) VL_MT_SAFE; - static void errorCountInc() VL_MT_SAFE; - static int errorCount() VL_MT_SAFE { return s_s.s_errorCount; } - /// Set number of errors/assertions before stop - static void errorLimit(int val) VL_MT_SAFE; - static int errorLimit() VL_MT_SAFE { return s_s.s_errorLimit; } - /// Did the simulation $finish? - static void gotFinish(bool flag) VL_MT_SAFE; - static bool gotFinish() VL_MT_SAFE { return s_s.s_gotFinish; } ///< Return if got a $finish - /// Allow traces to at some point be enabled (disables some optimizations) + /// Return the VerilatedContext for the current thread + static VerilatedContext* threadContextp() { + if (VL_UNLIKELY(!t_s.t_contextp)) t_s.t_contextp = lastContextp(); + return t_s.t_contextp; + } + /// Return the global VerilatedContext, used if none created by user + static VerilatedContext* defaultContextp() VL_MT_SAFE { + static VerilatedContext s_s; + return &s_s; + } + +#ifndef VL_NO_LEGACY + /// Call VerilatedContext::assertOn using current thread's VerilatedContext + static void assertOn(bool flag) VL_MT_SAFE { Verilated::threadContextp()->assertOn(flag); } + static bool assertOn() VL_MT_SAFE { return Verilated::threadContextp()->assertOn(); } + /// Call VerilatedContext::calcUnusedSigs using current thread's VerilatedContext + static void calcUnusedSigs(bool flag) VL_MT_SAFE { + Verilated::threadContextp()->calcUnusedSigs(flag); + } + static bool calcUnusedSigs() VL_MT_SAFE { + return Verilated::threadContextp()->calcUnusedSigs(); + } + /// Call VerilatedContext::commandArgs using current thread's VerilatedContext + static void commandArgs(int argc, const char** argv) VL_MT_SAFE { + Verilated::threadContextp()->commandArgs(argc, argv); + } + static void commandArgs(int argc, char** argv) VL_MT_SAFE { + commandArgs(argc, const_cast(argv)); + } + static void commandArgsAdd(int argc, const char** argv) { + Verilated::threadContextp()->commandArgsAdd(argc, argv); + } + static const char* commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE { + return Verilated::threadContextp()->commandArgsPlusMatch(prefixp); + } + /// Call VerilatedContext::errorLimit using current thread's VerilatedContext + static void errorLimit(int val) VL_MT_SAFE { Verilated::threadContextp()->errorLimit(val); } + static int errorLimit() VL_MT_SAFE { return Verilated::threadContextp()->errorLimit(); } + /// Call VerilatedContext::fatalOnError using current thread's VerilatedContext + static void fatalOnError(bool flag) VL_MT_SAFE { + Verilated::threadContextp()->fatalOnError(flag); + } + static bool fatalOnError() VL_MT_SAFE { return Verilated::threadContextp()->fatalOnError(); } + /// Call VerilatedContext::fatalOnVpiError using current thread's VerilatedContext + static void fatalOnVpiError(bool flag) VL_MT_SAFE { + Verilated::threadContextp()->fatalOnVpiError(flag); + } + static bool fatalOnVpiError() VL_MT_SAFE { + return Verilated::threadContextp()->fatalOnVpiError(); + } + /// Call VerilatedContext::gotError using current thread's VerilatedContext + static void gotError(bool flag) VL_MT_SAFE { Verilated::threadContextp()->gotError(flag); } + static bool gotError() VL_MT_SAFE { return Verilated::threadContextp()->gotError(); } + /// Call VerilatedContext::gotFinish using current thread's VerilatedContext + static void gotFinish(bool flag) VL_MT_SAFE { Verilated::threadContextp()->gotFinish(flag); } + static bool gotFinish() VL_MT_SAFE { return Verilated::threadContextp()->gotFinish(); } + /// Call VerilatedContext::randReset using current thread's VerilatedContext + static void randReset(int val) VL_MT_SAFE { Verilated::threadContextp()->randReset(val); } + static int randReset() VL_MT_SAFE { return Verilated::threadContextp()->randReset(); } + /// Call VerilatedContext::randSeed using current thread's VerilatedContext + static void randSeed(int val) VL_MT_SAFE { Verilated::threadContextp()->randSeed(val); } + static int randSeed() VL_MT_SAFE { return Verilated::threadContextp()->randSeed(); } + /// Call VerilatedContext::time using current thread's VerilatedContext + static void time(vluint64_t val) VL_MT_SAFE { Verilated::threadContextp()->time(val); } + static vluint64_t time() VL_MT_SAFE { return Verilated::threadContextp()->time(); } + static void timeInc(vluint64_t add) VL_MT_UNSAFE { Verilated::threadContextp()->timeInc(add); } + static int timeunit() VL_MT_SAFE { return Verilated::threadContextp()->timeunit(); } + static int timeprecision() VL_MT_SAFE { return Verilated::threadContextp()->timeprecision(); } + /// Call VerilatedContext::tracesEverOn using current thread's VerilatedContext static void traceEverOn(bool flag) VL_MT_SAFE { - if (flag) calcUnusedSigs(flag); + Verilated::threadContextp()->traceEverOn(flag); } - /// Enable/disable assertions - static void assertOn(bool flag) VL_MT_SAFE; - static bool assertOn() VL_MT_SAFE { return s_s.s_assertOn; } - /// Enable/disable vpi fatal - static void fatalOnVpiError(bool flag) VL_MT_SAFE; - static bool fatalOnVpiError() VL_MT_SAFE { return s_s.s_fatalOnVpiError; } - /// Time handling - static int timeunit() VL_MT_SAFE { return -s_s.s_timeunit; } - static const char* timeunitString() VL_MT_SAFE; - static void timeunit(int value) VL_MT_SAFE; - static int timeprecision() VL_MT_SAFE { return -s_s.s_timeprecision; } - static const char* timeprecisionString() VL_MT_SAFE; - static void timeprecision(int value) VL_MT_SAFE; - /// --prof-threads related settings - static void profThreadsStart(vluint64_t flag) VL_MT_SAFE; - static vluint64_t profThreadsStart() VL_MT_SAFE { return s_ns.s_profThreadsStart; } - static void profThreadsWindow(vluint64_t flag) VL_MT_SAFE; - static vluint32_t profThreadsWindow() VL_MT_SAFE { return s_ns.s_profThreadsWindow; } - static void profThreadsFilenamep(const char* flagp) VL_MT_SAFE; - static const char* profThreadsFilenamep() VL_MT_SAFE { return s_ns.s_profThreadsFilenamep; } +#endif typedef void (*VoidPCb)(void*); // Callback type for below - /// Callbacks to run on global flush + /// Add callback to run on global flush static void addFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE; + /// Remove callback to run on global flush static void removeFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE; + /// Run flush callbacks registered with addFlushCb static void runFlushCallbacks() VL_MT_SAFE; #ifndef VL_NO_LEGACY static void flushCall() VL_MT_SAFE { runFlushCallbacks(); } // Deprecated #endif - /// Callbacks to run prior to termination + /// Add callback to run prior to exit termination static void addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE; + /// Remove callback to run prior to exit termination static void removeExitCb(VoidPCb cb, void* datap) VL_MT_SAFE; + /// Run exit callbacks registered with addExitCb static void runExitCallbacks() VL_MT_SAFE; - /// Record command line arguments, for retrieval by $test$plusargs/$value$plusargs, - /// and for parsing +verilator+ run-time arguments. - /// This should be called before the first model is created. - static void commandArgs(int argc, const char** argv) VL_MT_SAFE; - static void commandArgs(int argc, char** argv) VL_MT_SAFE { - commandArgs(argc, const_cast(argv)); - } - static void commandArgsAdd(int argc, const char** argv); - static CommandArgValues* getCommandArgs() VL_MT_SAFE { return &s_args; } - /// Match plusargs with a given prefix. Returns static char* valid only for a single call - static const char* commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE; - - /// Produce name & version for (at least) VPI + /// Return product name for (at least) VPI static const char* productName() VL_PURE; + /// Return product version for (at least) VPI static const char* productVersion() VL_PURE; - /// Convenience OS utilities + /// Call OS to make a directory static void mkdir(const char* dirname) VL_MT_UNSAFE; /// When multithreaded, quiesce the model to prepare for trace/saves/coverage /// This may only be called when no locks are held. static void quiesce() VL_MT_SAFE; +#ifndef VL_NO_LEGACY /// For debugging, print much of the Verilator internal state. /// The output of this function may change in future /// releases - contact the authors before production use. - static void internalsDump() VL_MT_SAFE; - + static void internalsDump() VL_MT_SAFE { Verilated::threadContextp()->internalsDump(); } /// For debugging, print text list of all scope names with /// dpiImport/Export context. This function may change in future /// releases - contact the authors before production use. - static void scopesDump() VL_MT_SAFE; + static void scopesDump() VL_MT_SAFE { Verilated::threadContextp()->scopesDump(); } + // Internal: Find scope + static const VerilatedScope* scopeFind(const char* namep) VL_MT_SAFE { + return Verilated::threadContextp()->scopeFind(namep); + } + static const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE { + return Verilated::threadContextp()->scopeNameMap(); + } +#endif public: // METHODS - INTERNAL USE ONLY (but public due to what uses it) @@ -547,10 +827,6 @@ public: static void nullPointerError(const char* filename, int linenum) VL_ATTR_NORETURN VL_MT_SAFE; static void overWidthError(const char* signame) VL_ATTR_NORETURN VL_MT_SAFE; - // Internal: Find scope - static const VerilatedScope* scopeFind(const char* namep) VL_MT_SAFE; - static const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE; - // Internal: Get and set DPI context static const VerilatedScope* dpiScope() VL_MT_SAFE { return t_s.t_dpiScopep; } static void dpiScope(const VerilatedScope* scopep) VL_MT_SAFE { t_s.t_dpiScopep = scopep; } @@ -566,23 +842,19 @@ public: static int dpiLineno() VL_MT_SAFE { return t_s.t_dpiLineno; } static int exportFuncNum(const char* namep) VL_MT_SAFE; - // Internal: Serialization setup - static constexpr size_t serialized1Size() VL_PURE { return sizeof(s_s); } - static constexpr void* serialized1Ptr() VL_MT_UNSAFE { return &s_s; } // For Serialize only - static size_t serialized2Size() VL_PURE; - static void* serialized2Ptr() VL_MT_UNSAFE; #ifdef VL_THREADED - /// Internal: Set the mtaskId, called when an mtask starts + // Internal: Set the mtaskId, called when an mtask starts + // Per thread, so no need to be in VerilatedContext static void mtaskId(vluint32_t id) VL_MT_SAFE { t_s.t_mtaskId = id; } static vluint32_t mtaskId() VL_MT_SAFE { return t_s.t_mtaskId; } static void endOfEvalReqdInc() VL_MT_SAFE { ++t_s.t_endOfEvalReqd; } static void endOfEvalReqdDec() VL_MT_SAFE { --t_s.t_endOfEvalReqd; } - /// Internal: Called at end of each thread mtask, before finishing eval + // Internal: Called at end of each thread mtask, before finishing eval static void endOfThreadMTask(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE { if (VL_UNLIKELY(t_s.t_endOfEvalReqd)) endOfThreadMTaskGuts(evalMsgQp); } - /// Internal: Called at end of eval loop + // Internal: Called at end of eval loop static void endOfEval(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE; #endif @@ -592,6 +864,9 @@ private: #endif }; +inline void VerilatedContext::debug(int val) VL_MT_SAFE { Verilated::debug(val); } +inline int VerilatedContext::debug() VL_MT_SAFE { return Verilated::debug(); } + //========================================================================= // Extern functions -- User may override -- See verilated.cpp @@ -601,7 +876,7 @@ private: /// Verilator internal code must call VL_FINISH_MT instead, which eventually calls this. extern void vl_finish(const char* filename, int linenum, const char* hier); -/// Routine to call for $stop +/// Routine to call for $stop and non-fatal error /// User code may wish to replace this function, to do so, define VL_USER_STOP. /// This code does not have to be thread safe. /// Verilator internal code must call VL_FINISH_MT instead, which eventually calls this. @@ -670,7 +945,8 @@ inline QData VL_RDTSC_Q() { } #endif -extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp) VL_MT_SAFE; +extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp, + const VerilatedContext* contextp) VL_MT_SAFE; /// Math extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, @@ -736,7 +1012,7 @@ extern const char* vl_mc_scan_plusargs(const char* prefixp); // PLIish #define VL_SET_QW(lwp) \ ((static_cast((lwp)[0])) \ | (static_cast((lwp)[1]) << (static_cast(VL_EDATASIZE)))) -#define _VL_SET_QII(ld, rd) ((static_cast(ld) << 32ULL) | static_cast(rd)) +#define VL_SET_QII(ld, rd) ((static_cast(ld) << 32ULL) | static_cast(rd)) /// Return FILE* from IData extern FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE; @@ -816,7 +1092,7 @@ static inline QData VL_EXTENDSIGN_Q(int lbits, QData lhs) VL_PURE { } // Debugging prints -extern void _VL_DEBUG_PRINT_W(int lbits, WDataInP iwp); +extern void _vl_debug_print_w(int lbits, WDataInP iwp); //========================================================================= // Pli macros @@ -846,16 +1122,40 @@ extern int VL_TIME_STR_CONVERT(const char* strp) VL_PURE; // Already defined: extern sc_time sc_time_stamp(); inline vluint64_t vl_time_stamp64() { return sc_time_stamp().value(); } #else // Non-SystemC -# ifdef VL_TIME_STAMP64 -extern vluint64_t vl_time_stamp64(); -# else -extern double sc_time_stamp(); // Verilator 4.032 and newer -inline vluint64_t vl_time_stamp64() { return static_cast(sc_time_stamp()); } +# if !defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY) +# ifdef VL_TIME_STAMP64 +// vl_time_stamp64() may be optionally defined by the user to return time. +// On MSVC++ weak symbols are not supported so must be declared, or define +// VL_TIME_CONTEXT. +extern vluint64_t vl_time_stamp64() VL_ATTR_WEAK; +# else +// sc_time_stamp() may be optionally defined by the user to return time. +// On MSVC++ weak symbols are not supported so must be declared, or define +// VL_TIME_CONTEXT. +extern double sc_time_stamp() VL_ATTR_WEAK; // Verilator 4.032 and newer +inline vluint64_t vl_time_stamp64() { + // clang9.0.1 requires & although we really do want the weak symbol value + return VL_LIKELY(&sc_time_stamp) ? static_cast(sc_time_stamp()) : 0; +} +# endif # endif #endif -#define VL_TIME_Q() (static_cast(vl_time_stamp64())) -#define VL_TIME_D() (static_cast(vl_time_stamp64())) +inline vluint64_t VerilatedContext::time() const VL_MT_SAFE { + // When using non-default context, fastest path is return time + if (VL_LIKELY(m_s.m_time)) return m_s.m_time; +#if defined(SYSTEMC_VERSION) || (!defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY)) + // Zero time could mean really at zero, or using callback + // clang9.0.1 requires & although we really do want the weak symbol value + if (VL_LIKELY(&vl_time_stamp64)) { // else is weak symbol that is not defined + return vl_time_stamp64(); + } +#endif + return 0; +} + +#define VL_TIME_Q() (Verilated::threadContextp()->time()) +#define VL_TIME_D() (static_cast(VL_TIME_Q())) /// Time scaled from 1-per-precision into a module's time units ("Unit"-ed, not "United") // Optimized assuming scale is always constant. @@ -898,7 +1198,7 @@ double vl_time_multiplier(int scale) VL_PURE; // EMIT_RULE: VL_ASSIGNCLEAN: oclean=clean; obits==lbits; #define VL_ASSIGNCLEAN_W(obits, owp, lwp) VL_CLEAN_WW((obits), (obits), (owp), (lwp)) -static inline WDataOutP _VL_CLEAN_INPLACE_W(int obits, WDataOutP owp) VL_MT_SAFE { +static inline WDataOutP _vl_clean_inplace_w(int obits, WDataOutP owp) VL_MT_SAFE { int words = VL_WORDS_I(obits); owp[words - 1] &= VL_MASK_E(obits); return owp; @@ -1372,10 +1672,10 @@ static inline WDataOutP VL_NOT_W(int words, WDataOutP owp, WDataInP lwp) VL_MT_S // EMIT_RULE: VL_GTE: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; // EMIT_RULE: VL_LTE: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; #define VL_NEQ_W(words, lwp, rwp) (!VL_EQ_W(words, lwp, rwp)) -#define VL_LT_W(words, lwp, rwp) (_VL_CMP_W(words, lwp, rwp) < 0) -#define VL_LTE_W(words, lwp, rwp) (_VL_CMP_W(words, lwp, rwp) <= 0) -#define VL_GT_W(words, lwp, rwp) (_VL_CMP_W(words, lwp, rwp) > 0) -#define VL_GTE_W(words, lwp, rwp) (_VL_CMP_W(words, lwp, rwp) >= 0) +#define VL_LT_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) < 0) +#define VL_LTE_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) <= 0) +#define VL_GT_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) > 0) +#define VL_GTE_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) >= 0) // Output clean, AND MUST BE CLEAN static inline IData VL_EQ_W(int words, WDataInP lwp, WDataInP rwp) VL_MT_SAFE { @@ -1385,7 +1685,7 @@ static inline IData VL_EQ_W(int words, WDataInP lwp, WDataInP rwp) VL_MT_SAFE { } // Internal usage -static inline int _VL_CMP_W(int words, WDataInP lwp, WDataInP rwp) VL_MT_SAFE { +static inline int _vl_cmp_w(int words, WDataInP lwp, WDataInP rwp) VL_MT_SAFE { for (int i = words - 1; i >= 0; --i) { if (lwp[i] > rwp[i]) return 1; if (lwp[i] < rwp[i]) return -1; @@ -1393,10 +1693,10 @@ static inline int _VL_CMP_W(int words, WDataInP lwp, WDataInP rwp) VL_MT_SAFE { return 0; // == } -#define VL_LTS_IWW(obits, lbits, rbbits, lwp, rwp) (_VL_CMPS_W(lbits, lwp, rwp) < 0) -#define VL_LTES_IWW(obits, lbits, rbits, lwp, rwp) (_VL_CMPS_W(lbits, lwp, rwp) <= 0) -#define VL_GTS_IWW(obits, lbits, rbits, lwp, rwp) (_VL_CMPS_W(lbits, lwp, rwp) > 0) -#define VL_GTES_IWW(obits, lbits, rbits, lwp, rwp) (_VL_CMPS_W(lbits, lwp, rwp) >= 0) +#define VL_LTS_IWW(obits, lbits, rbbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) < 0) +#define VL_LTES_IWW(obits, lbits, rbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) <= 0) +#define VL_GTS_IWW(obits, lbits, rbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) > 0) +#define VL_GTES_IWW(obits, lbits, rbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) >= 0) static inline IData VL_GTS_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { // For lbits==32, this becomes just a single instruction, otherwise ~5. @@ -1444,7 +1744,7 @@ static inline IData VL_LTES_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PU return lhs_signed <= rhs_signed; } -static inline int _VL_CMPS_W(int lbits, WDataInP lwp, WDataInP rwp) VL_MT_SAFE { +static inline int _vl_cmps_w(int lbits, WDataInP lwp, WDataInP rwp) VL_MT_SAFE { int words = VL_WORDS_I(lbits); int i = words - 1; // We need to flip sense if negative comparison @@ -1621,12 +1921,12 @@ static inline WDataOutP VL_DIVS_WWW(int lbits, WDataOutP owp, WDataInP lwp, WData rwstore[VL_MULS_MAX_WORDS]; WDataInP ltup = lwp; WDataInP rtup = rwp; - if (lsign) ltup = _VL_CLEAN_INPLACE_W(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), lwstore, lwp)); - if (rsign) rtup = _VL_CLEAN_INPLACE_W(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), rwstore, rwp)); + if (lsign) ltup = _vl_clean_inplace_w(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), lwstore, lwp)); + if (rsign) rtup = _vl_clean_inplace_w(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), rwstore, rwp)); if ((lsign && !rsign) || (!lsign && rsign)) { WData qNoSign[VL_MULS_MAX_WORDS]; VL_DIV_WWW(lbits, qNoSign, ltup, rtup); - _VL_CLEAN_INPLACE_W(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), owp, qNoSign)); + _vl_clean_inplace_w(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), owp, qNoSign)); return owp; } else { return VL_DIV_WWW(lbits, owp, ltup, rtup); @@ -1643,12 +1943,12 @@ static inline WDataOutP VL_MODDIVS_WWW(int lbits, WDataOutP owp, WDataInP lwp, WData rwstore[VL_MULS_MAX_WORDS]; WDataInP ltup = lwp; WDataInP rtup = rwp; - if (lsign) ltup = _VL_CLEAN_INPLACE_W(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), lwstore, lwp)); - if (rsign) rtup = _VL_CLEAN_INPLACE_W(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), rwstore, rwp)); + if (lsign) ltup = _vl_clean_inplace_w(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), lwstore, lwp)); + if (rsign) rtup = _vl_clean_inplace_w(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), rwstore, rwp)); if (lsign) { // Only dividend sign matters for modulus WData qNoSign[VL_MULS_MAX_WORDS]; VL_MODDIV_WWW(lbits, qNoSign, ltup, rtup); - _VL_CLEAN_INPLACE_W(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), owp, qNoSign)); + _vl_clean_inplace_w(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), owp, qNoSign)); return owp; } else { return VL_MODDIV_WWW(lbits, owp, ltup, rtup); @@ -1747,75 +2047,98 @@ QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, WDataInP rwp, bool lsig // INTERNAL: Stuff LHS bit 0++ into OUTPUT at specified offset // ld may be "dirty", output is clean -static inline void _VL_INSERT_II(int, CData& lhsr, IData ld, int hbit, int lbit) VL_PURE { +static inline void _vl_insert_II(int, CData& lhsr, IData ld, int hbit, int lbit, + int rbits) VL_PURE { + IData cleanmask = VL_MASK_I(rbits); IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; - lhsr = (lhsr & ~insmask) | ((ld << lbit) & insmask); + lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); } -static inline void _VL_INSERT_II(int, SData& lhsr, IData ld, int hbit, int lbit) VL_PURE { +static inline void _vl_insert_II(int, SData& lhsr, IData ld, int hbit, int lbit, + int rbits) VL_PURE { + IData cleanmask = VL_MASK_I(rbits); IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; - lhsr = (lhsr & ~insmask) | ((ld << lbit) & insmask); + lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); } -static inline void _VL_INSERT_II(int, IData& lhsr, IData ld, int hbit, int lbit) VL_PURE { +static inline void _vl_insert_II(int, IData& lhsr, IData ld, int hbit, int lbit, + int rbits) VL_PURE { + IData cleanmask = VL_MASK_I(rbits); IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; - lhsr = (lhsr & ~insmask) | ((ld << lbit) & insmask); + lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); } -static inline void _VL_INSERT_QQ(int, QData& lhsr, QData ld, int hbit, int lbit) VL_PURE { +static inline void _vl_insert_QQ(int, QData& lhsr, QData ld, int hbit, int lbit, + int rbits) VL_PURE { + QData cleanmask = VL_MASK_Q(rbits); QData insmask = (VL_MASK_Q(hbit - lbit + 1)) << lbit; - lhsr = (lhsr & ~insmask) | ((ld << lbit) & insmask); + lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); } -static inline void _VL_INSERT_WI(int, WDataOutP owp, IData ld, int hbit, int lbit) VL_MT_SAFE { +static inline void _vl_insert_WI(int, WDataOutP owp, IData ld, int hbit, int lbit, + int rbits = 0) VL_MT_SAFE { int hoffset = VL_BITBIT_E(hbit); int loffset = VL_BITBIT_E(lbit); + int roffset = VL_BITBIT_E(rbits); + int hword = VL_BITWORD_E(hbit); + int lword = VL_BITWORD_E(lbit); + int rword = VL_BITWORD_E(rbits); + EData cleanmask = hword == rword ? VL_MASK_E(roffset) : VL_MASK_E(0); + if (hoffset == VL_SIZEBITS_E && loffset == 0) { // Fast and common case, word based insertion - owp[VL_BITWORD_E(lbit)] = ld; + owp[VL_BITWORD_E(lbit)] = ld & cleanmask; } else { - int hword = VL_BITWORD_E(hbit); - int lword = VL_BITWORD_E(lbit); EData lde = static_cast(ld); if (hword == lword) { // know < EData bits because above checks it + // Assignment is contained within one word of destination EData insmask = (VL_MASK_E(hoffset - loffset + 1)) << loffset; - owp[lword] = (owp[lword] & ~insmask) | ((lde << loffset) & insmask); + owp[lword] = (owp[lword] & ~insmask) | ((lde << loffset) & (insmask & cleanmask)); } else { + // Assignment crosses a word boundary in destination EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << 0; EData linsmask = (VL_MASK_E((VL_EDATASIZE - 1) - loffset + 1)) << loffset; int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword owp[lword] = (owp[lword] & ~linsmask) | ((lde << loffset) & linsmask); - owp[hword] = (owp[hword] & ~hinsmask) | ((lde >> nbitsonright) & hinsmask); + owp[hword] + = (owp[hword] & ~hinsmask) | ((lde >> nbitsonright) & (hinsmask & cleanmask)); } } } // INTERNAL: Stuff large LHS bit 0++ into OUTPUT at specified offset // lwp may be "dirty" -static inline void _VL_INSERT_WW(int, WDataOutP owp, WDataInP lwp, int hbit, int lbit) VL_MT_SAFE { - int hoffset = hbit & VL_SIZEBITS_E; - int loffset = lbit & VL_SIZEBITS_E; +static inline void _vl_insert_WW(int, WDataOutP owp, WDataInP lwp, int hbit, int lbit, + int rbits = 0) VL_MT_SAFE { + int hoffset = VL_BITBIT_E(hbit); + int loffset = VL_BITBIT_E(lbit); + int roffset = VL_BITBIT_E(rbits); int lword = VL_BITWORD_E(lbit); + int hword = VL_BITWORD_E(hbit); + int rword = VL_BITWORD_E(rbits); int words = VL_WORDS_I(hbit - lbit + 1); + // Cleaning mask, only applied to top word of the assignment. Is a no-op + // if we don't assign to the top word of the destination. + EData cleanmask = hword == rword ? VL_MASK_E(roffset) : VL_MASK_E(0); + if (hoffset == VL_SIZEBITS_E && loffset == 0) { // Fast and common case, word based insertion - for (int i = 0; i < words; ++i) owp[lword + i] = lwp[i]; + for (int i = 0; i < (words - 1); ++i) owp[lword + i] = lwp[i]; + owp[hword] = lwp[words - 1] & cleanmask; } else if (loffset == 0) { // Non-32bit, but nicely aligned, so stuff all but the last word for (int i = 0; i < (words - 1); ++i) owp[lword + i] = lwp[i]; // Know it's not a full word as above fast case handled it EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)); - owp[lword + words - 1] - = (owp[words + lword - 1] & ~hinsmask) | (lwp[words - 1] & hinsmask); + owp[hword] = (owp[hword] & ~hinsmask) | (lwp[words - 1] & (hinsmask & cleanmask)); } else { EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << 0; EData linsmask = (VL_MASK_E((VL_EDATASIZE - 1) - loffset + 1)) << loffset; int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword (know loffset!=0) // Middle words - int hword = VL_BITWORD_E(hbit); for (int i = 0; i < words; ++i) { { // Lower word int oword = lword + i; EData d = lwp[i] << loffset; EData od = (owp[oword] & ~linsmask) | (d & linsmask); if (oword == hword) { - owp[oword] = (owp[oword] & ~hinsmask) | (od & hinsmask); + owp[oword] = (owp[oword] & ~hinsmask) | (od & (hinsmask & cleanmask)); } else { owp[oword] = od; } @@ -1826,7 +2149,7 @@ static inline void _VL_INSERT_WW(int, WDataOutP owp, WDataInP lwp, int hbit, int EData d = lwp[i] >> nbitsonright; EData od = (d & ~linsmask) | (owp[oword] & linsmask); if (oword == hword) { - owp[oword] = (owp[oword] & ~hinsmask) | (od & hinsmask); + owp[oword] = (owp[oword] & ~hinsmask) | (od & (hinsmask & cleanmask)); } else { owp[oword] = od; } @@ -1836,11 +2159,11 @@ static inline void _VL_INSERT_WW(int, WDataOutP owp, WDataInP lwp, int hbit, int } } -static inline void _VL_INSERT_WQ(int obits, WDataOutP owp, QData ld, int hbit, - int lbit) VL_MT_SAFE { +static inline void _vl_insert_WQ(int obits, WDataOutP owp, QData ld, int hbit, int lbit, + int rbits = 0) VL_MT_SAFE { WData lwp[VL_WQ_WORDS_E]; VL_SET_WQ(lwp, ld); - _VL_INSERT_WW(obits, owp, lwp, hbit, lbit); + _vl_insert_WW(obits, owp, lwp, hbit, lbit, rbits); } // EMIT_RULE: VL_REPLICATE: oclean=clean>width32, dirty<=width32; lclean=clean; rclean==clean; @@ -1868,7 +2191,7 @@ static inline WDataOutP VL_REPLICATE_WII(int obits, int lbits, int, WDataOutP ow IData rep) VL_MT_SAFE { owp[0] = ld; for (unsigned i = 1; i < rep; ++i) { - _VL_INSERT_WI(obits, owp, ld, i * lbits + lbits - 1, i * lbits); + _vl_insert_WI(obits, owp, ld, i * lbits + lbits - 1, i * lbits); } return owp; } @@ -1876,7 +2199,7 @@ static inline WDataOutP VL_REPLICATE_WQI(int obits, int lbits, int, WDataOutP ow IData rep) VL_MT_SAFE { VL_SET_WQ(owp, ld); for (unsigned i = 1; i < rep; ++i) { - _VL_INSERT_WQ(obits, owp, ld, i * lbits + lbits - 1, i * lbits); + _vl_insert_WQ(obits, owp, ld, i * lbits + lbits - 1, i * lbits); } return owp; } @@ -1884,7 +2207,7 @@ static inline WDataOutP VL_REPLICATE_WWI(int obits, int lbits, int, WDataOutP ow IData rep) VL_MT_SAFE { for (int i = 0; i < VL_WORDS_I(lbits); ++i) owp[i] = lwp[i]; for (unsigned i = 1; i < rep; ++i) { - _VL_INSERT_WW(obits, owp, lwp, i * lbits + lbits - 1, i * lbits); + _vl_insert_WW(obits, owp, lwp, i * lbits + lbits - 1, i * lbits); } return owp; } @@ -2024,63 +2347,63 @@ static inline WDataOutP VL_CONCAT_WII(int obits, int lbits, int rbits, WDataOutP IData rd) VL_MT_SAFE { owp[0] = rd; for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _VL_INSERT_WI(obits, owp, ld, rbits + lbits - 1, rbits); + _vl_insert_WI(obits, owp, ld, rbits + lbits - 1, rbits); return owp; } static inline WDataOutP VL_CONCAT_WWI(int obits, int lbits, int rbits, WDataOutP owp, WDataInP lwp, IData rd) VL_MT_SAFE { owp[0] = rd; for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _VL_INSERT_WW(obits, owp, lwp, rbits + lbits - 1, rbits); + _vl_insert_WW(obits, owp, lwp, rbits + lbits - 1, rbits); return owp; } static inline WDataOutP VL_CONCAT_WIW(int obits, int lbits, int rbits, WDataOutP owp, IData ld, WDataInP rwp) VL_MT_SAFE { for (int i = 0; i < VL_WORDS_I(rbits); ++i) owp[i] = rwp[i]; for (int i = VL_WORDS_I(rbits); i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _VL_INSERT_WI(obits, owp, ld, rbits + lbits - 1, rbits); + _vl_insert_WI(obits, owp, ld, rbits + lbits - 1, rbits); return owp; } static inline WDataOutP VL_CONCAT_WIQ(int obits, int lbits, int rbits, WDataOutP owp, IData ld, QData rd) VL_MT_SAFE { VL_SET_WQ(owp, rd); for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _VL_INSERT_WI(obits, owp, ld, rbits + lbits - 1, rbits); + _vl_insert_WI(obits, owp, ld, rbits + lbits - 1, rbits); return owp; } static inline WDataOutP VL_CONCAT_WQI(int obits, int lbits, int rbits, WDataOutP owp, QData ld, IData rd) VL_MT_SAFE { owp[0] = rd; for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _VL_INSERT_WQ(obits, owp, ld, rbits + lbits - 1, rbits); + _vl_insert_WQ(obits, owp, ld, rbits + lbits - 1, rbits); return owp; } static inline WDataOutP VL_CONCAT_WQQ(int obits, int lbits, int rbits, WDataOutP owp, QData ld, QData rd) VL_MT_SAFE { VL_SET_WQ(owp, rd); for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _VL_INSERT_WQ(obits, owp, ld, rbits + lbits - 1, rbits); + _vl_insert_WQ(obits, owp, ld, rbits + lbits - 1, rbits); return owp; } static inline WDataOutP VL_CONCAT_WWQ(int obits, int lbits, int rbits, WDataOutP owp, WDataInP lwp, QData rd) VL_MT_SAFE { VL_SET_WQ(owp, rd); for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _VL_INSERT_WW(obits, owp, lwp, rbits + lbits - 1, rbits); + _vl_insert_WW(obits, owp, lwp, rbits + lbits - 1, rbits); return owp; } static inline WDataOutP VL_CONCAT_WQW(int obits, int lbits, int rbits, WDataOutP owp, QData ld, WDataInP rwp) VL_MT_SAFE { for (int i = 0; i < VL_WORDS_I(rbits); ++i) owp[i] = rwp[i]; for (int i = VL_WORDS_I(rbits); i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _VL_INSERT_WQ(obits, owp, ld, rbits + lbits - 1, rbits); + _vl_insert_WQ(obits, owp, ld, rbits + lbits - 1, rbits); return owp; } static inline WDataOutP VL_CONCAT_WWW(int obits, int lbits, int rbits, WDataOutP owp, WDataInP lwp, WDataInP rwp) VL_MT_SAFE { for (int i = 0; i < VL_WORDS_I(rbits); ++i) owp[i] = rwp[i]; for (int i = VL_WORDS_I(rbits); i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _VL_INSERT_WW(obits, owp, lwp, rbits + lbits - 1, rbits); + _vl_insert_WW(obits, owp, lwp, rbits + lbits - 1, rbits); return owp; } @@ -2089,7 +2412,7 @@ static inline WDataOutP VL_CONCAT_WWW(int obits, int lbits, int rbits, WDataOutP // Static shift, used by internal functions // The output is the same as the input - it overlaps! -static inline void _VL_SHIFTL_INPLACE_W(int obits, WDataOutP iowp, +static inline void _vl_shiftl_inplace_w(int obits, WDataOutP iowp, IData rd /*1 or 4*/) VL_MT_SAFE { int words = VL_WORDS_I(obits); EData linsmask = VL_MASK_E(rd); @@ -2115,7 +2438,7 @@ static inline WDataOutP VL_SHIFTL_WWI(int obits, int, int, WDataOutP owp, WDataI for (int i = word_shift; i < VL_WORDS_I(obits); ++i) owp[i] = lwp[i - word_shift]; } else { for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _VL_INSERT_WW(obits, owp, lwp, obits - 1, rd); + _vl_insert_WW(obits, owp, lwp, obits - 1, rd); } return owp; } @@ -2404,7 +2727,7 @@ static inline WDataOutP VL_SEL_WWII(int obits, int lbits, int, int, WDataOutP ow // Just a word extract for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = lwp[i + word_shift]; } else { - // Not a _VL_INSERT because the bits come from any bit number and goto bit 0 + // Not a _vl_insert because the bits come from any bit number and goto bit 0 int loffset = lsb & VL_SIZEBITS_E; int nbitsfromlow = VL_EDATASIZE - loffset; // bits that end up in lword (know loffset!=0) // Middle words @@ -2458,7 +2781,7 @@ static inline WDataOutP VL_RTOIROUND_W_D(int obits, WDataOutP owp, double lhs) V if (lsb < 0) { VL_SET_WQ(owp, mantissa >> -lsb); } else if (lsb < obits) { - _VL_INSERT_WQ(obits, owp, mantissa, lsb + 52, lsb); + _vl_insert_WQ(obits, owp, mantissa, lsb + 52, lsb); } if (lhs < 0) VL_NEGATE_INPLACE_W(VL_WORDS_I(obits), owp); return owp; @@ -2468,34 +2791,43 @@ static inline WDataOutP VL_RTOIROUND_W_D(int obits, WDataOutP owp, double lhs) V // Range assignments // EMIT_RULE: VL_ASSIGNRANGE: rclean=dirty; -static inline void VL_ASSIGNSEL_IIII(int obits, int lsb, CData& lhsr, IData rhs) VL_PURE { - _VL_INSERT_II(obits, lhsr, rhs, lsb + obits - 1, lsb); +static inline void VL_ASSIGNSEL_IIII(int rbits, int obits, int lsb, CData& lhsr, + IData rhs) VL_PURE { + _vl_insert_II(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); } -static inline void VL_ASSIGNSEL_IIII(int obits, int lsb, SData& lhsr, IData rhs) VL_PURE { - _VL_INSERT_II(obits, lhsr, rhs, lsb + obits - 1, lsb); +static inline void VL_ASSIGNSEL_IIII(int rbits, int obits, int lsb, SData& lhsr, + IData rhs) VL_PURE { + _vl_insert_II(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); } -static inline void VL_ASSIGNSEL_IIII(int obits, int lsb, IData& lhsr, IData rhs) VL_PURE { - _VL_INSERT_II(obits, lhsr, rhs, lsb + obits - 1, lsb); +static inline void VL_ASSIGNSEL_IIII(int rbits, int obits, int lsb, IData& lhsr, + IData rhs) VL_PURE { + _vl_insert_II(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); } -static inline void VL_ASSIGNSEL_QIII(int obits, int lsb, QData& lhsr, IData rhs) VL_PURE { - _VL_INSERT_QQ(obits, lhsr, rhs, lsb + obits - 1, lsb); +static inline void VL_ASSIGNSEL_QIII(int rbits, int obits, int lsb, QData& lhsr, + IData rhs) VL_PURE { + _vl_insert_QQ(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); } -static inline void VL_ASSIGNSEL_QQII(int obits, int lsb, QData& lhsr, QData rhs) VL_PURE { - _VL_INSERT_QQ(obits, lhsr, rhs, lsb + obits - 1, lsb); +static inline void VL_ASSIGNSEL_QQII(int rbits, int obits, int lsb, QData& lhsr, + QData rhs) VL_PURE { + _vl_insert_QQ(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); } -static inline void VL_ASSIGNSEL_QIIQ(int obits, int lsb, QData& lhsr, QData rhs) VL_PURE { - _VL_INSERT_QQ(obits, lhsr, rhs, lsb + obits - 1, lsb); +static inline void VL_ASSIGNSEL_QIIQ(int rbits, int obits, int lsb, QData& lhsr, + QData rhs) VL_PURE { + _vl_insert_QQ(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); } // static inline void VL_ASSIGNSEL_IIIW(int obits, int lsb, IData& lhsr, WDataInP rwp) VL_MT_SAFE { // Illegal, as lhs width >= rhs width -static inline void VL_ASSIGNSEL_WIII(int obits, int lsb, WDataOutP owp, IData rhs) VL_MT_SAFE { - _VL_INSERT_WI(obits, owp, rhs, lsb + obits - 1, lsb); +static inline void VL_ASSIGNSEL_WIII(int rbits, int obits, int lsb, WDataOutP owp, + IData rhs) VL_MT_SAFE { + _vl_insert_WI(obits, owp, rhs, lsb + obits - 1, lsb, rbits); } -static inline void VL_ASSIGNSEL_WIIQ(int obits, int lsb, WDataOutP owp, QData rhs) VL_MT_SAFE { - _VL_INSERT_WQ(obits, owp, rhs, lsb + obits - 1, lsb); +static inline void VL_ASSIGNSEL_WIIQ(int rbits, int obits, int lsb, WDataOutP owp, + QData rhs) VL_MT_SAFE { + _vl_insert_WQ(obits, owp, rhs, lsb + obits - 1, lsb, rbits); } -static inline void VL_ASSIGNSEL_WIIW(int obits, int lsb, WDataOutP owp, WDataInP rwp) VL_MT_SAFE { - _VL_INSERT_WW(obits, owp, rwp, lsb + obits - 1, lsb); +static inline void VL_ASSIGNSEL_WIIW(int rbits, int obits, int lsb, WDataOutP owp, + WDataInP rwp) VL_MT_SAFE { + _vl_insert_WW(obits, owp, rwp, lsb + obits - 1, lsb, rbits); } //====================================================================== @@ -2517,81 +2849,81 @@ static inline WDataOutP VL_COND_WIWW(int obits, int, int, int, WDataOutP owp, in // hence all upper words must be zeroed. // If changing the number of functions here, also change EMITCINLINES_NUM_CONSTW -#define _END(obits, wordsSet) \ +#define VL_C_END_(obits, wordsSet) \ for (int i = (wordsSet); i < VL_WORDS_I(obits); ++i) o[i] = 0; \ return o // clang-format off static inline WDataOutP VL_CONST_W_1X(int obits, WDataOutP o, EData d0) VL_MT_SAFE { o[0] = d0; - _END(obits, 1); + VL_C_END_(obits, 1); } static inline WDataOutP VL_CONST_W_2X(int obits, WDataOutP o, EData d1, EData d0) VL_MT_SAFE { o[0] = d0; o[1] = d1; - _END(obits, 2); + VL_C_END_(obits, 2); } static inline WDataOutP VL_CONST_W_3X(int obits, WDataOutP o, EData d2, EData d1, EData d0) VL_MT_SAFE { o[0] = d0; o[1] = d1; o[2] = d2; - _END(obits,3); + VL_C_END_(obits,3); } static inline WDataOutP VL_CONST_W_4X(int obits, WDataOutP o, EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; - _END(obits,4); + VL_C_END_(obits,4); } static inline WDataOutP VL_CONST_W_5X(int obits, WDataOutP o, EData d4, EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; o[4] = d4; - _END(obits,5); + VL_C_END_(obits,5); } static inline WDataOutP VL_CONST_W_6X(int obits, WDataOutP o, EData d5, EData d4, EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; o[4] = d4; o[5] = d5; - _END(obits,6); + VL_C_END_(obits,6); } static inline WDataOutP VL_CONST_W_7X(int obits, WDataOutP o, EData d6, EData d5, EData d4, EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; o[4] = d4; o[5] = d5; o[6] = d6; - _END(obits,7); + VL_C_END_(obits,7); } static inline WDataOutP VL_CONST_W_8X(int obits, WDataOutP o, EData d7, EData d6, EData d5, EData d4, EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; o[4] = d4; o[5] = d5; o[6] = d6; o[7] = d7; - _END(obits,8); + VL_C_END_(obits,8); } // static inline WDataOutP VL_CONSTHI_W_1X(int obits, int lsb, WDataOutP obase, EData d0) VL_MT_SAFE { WDataOutP o = obase + VL_WORDS_I(lsb); o[0] = d0; - _END(obits, VL_WORDS_I(lsb) + 1); + VL_C_END_(obits, VL_WORDS_I(lsb) + 1); } static inline WDataOutP VL_CONSTHI_W_2X(int obits, int lsb, WDataOutP obase, EData d1, EData d0) VL_MT_SAFE { WDataOutP o = obase + VL_WORDS_I(lsb); o[0] = d0; o[1] = d1; - _END(obits, VL_WORDS_I(lsb) + 2); + VL_C_END_(obits, VL_WORDS_I(lsb) + 2); } static inline WDataOutP VL_CONSTHI_W_3X(int obits, int lsb, WDataOutP obase, EData d2, EData d1, EData d0) VL_MT_SAFE { WDataOutP o = obase + VL_WORDS_I(lsb); o[0] = d0; o[1] = d1; o[2] = d2; - _END(obits, VL_WORDS_I(lsb) + 3); + VL_C_END_(obits, VL_WORDS_I(lsb) + 3); } static inline WDataOutP VL_CONSTHI_W_4X(int obits, int lsb, WDataOutP obase, EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { WDataOutP o = obase + VL_WORDS_I(lsb); o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; - _END(obits, VL_WORDS_I(lsb) + 4); + VL_C_END_(obits, VL_WORDS_I(lsb) + 4); } static inline WDataOutP VL_CONSTHI_W_5X(int obits, int lsb, WDataOutP obase, EData d4, @@ -2599,7 +2931,7 @@ static inline WDataOutP VL_CONSTHI_W_5X(int obits, int lsb, WDataOutP obase, WDataOutP o = obase + VL_WORDS_I(lsb); o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; o[4] = d4; - _END(obits, VL_WORDS_I(lsb) + 5); + VL_C_END_(obits, VL_WORDS_I(lsb) + 5); } static inline WDataOutP VL_CONSTHI_W_6X(int obits, int lsb, WDataOutP obase, EData d5, EData d4, @@ -2607,7 +2939,7 @@ static inline WDataOutP VL_CONSTHI_W_6X(int obits, int lsb, WDataOutP obase, WDataOutP o = obase + VL_WORDS_I(lsb); o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; o[4] = d4; o[5] = d5; - _END(obits, VL_WORDS_I(lsb) + 6); + VL_C_END_(obits, VL_WORDS_I(lsb) + 6); } static inline WDataOutP VL_CONSTHI_W_7X(int obits, int lsb, WDataOutP obase, EData d6, EData d5, EData d4, @@ -2615,7 +2947,7 @@ static inline WDataOutP VL_CONSTHI_W_7X(int obits, int lsb, WDataOutP obase, WDataOutP o = obase + VL_WORDS_I(lsb); o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; o[4] = d4; o[5] = d5; o[6] = d6; - _END(obits, VL_WORDS_I(lsb) + 7); + VL_C_END_(obits, VL_WORDS_I(lsb) + 7); } static inline WDataOutP VL_CONSTHI_W_8X(int obits, int lsb, WDataOutP obase, EData d7, EData d6, EData d5, EData d4, @@ -2623,10 +2955,10 @@ static inline WDataOutP VL_CONSTHI_W_8X(int obits, int lsb, WDataOutP obase, WDataOutP o = obase + VL_WORDS_I(lsb); o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; o[4] = d4; o[5] = d5; o[6] = d6; o[7] = d7; - _END(obits, VL_WORDS_I(lsb) + 8); + VL_C_END_(obits, VL_WORDS_I(lsb) + 8); } -#undef _END +#undef VL_C_END_ // Partial constant, lower words of vector wider than 8*32, starting at bit number lsb static inline void VL_CONSTLO_W_8X(int lsb, WDataOutP obase, diff --git a/include/verilated_cov.cpp b/include/verilated_cov.cpp index 6d8e5a458..85904d9d6 100644 --- a/include/verilated_cov.cpp +++ b/include/verilated_cov.cpp @@ -26,10 +26,10 @@ #include //============================================================================= -// VerilatedCovImpBase -/// Implementation base class for constants +// VerilatedCovConst +// Implementation constants -struct VerilatedCovImpBase VL_NOT_FINAL { +struct VerilatedCovConst VL_NOT_FINAL { // TYPES enum { MAX_KEYS = 33 }; /// Maximum user arguments + filename+lineno enum { KEY_UNDEF = 0 }; /// Magic key # for unspecified values @@ -39,16 +39,16 @@ struct VerilatedCovImpBase VL_NOT_FINAL { // VerilatedCovImpItem /// Implementation class for a VerilatedCov item -class VerilatedCovImpItem VL_NOT_FINAL : VerilatedCovImpBase { +class VerilatedCovImpItem VL_NOT_FINAL { public: // But only local to this file // MEMBERS - int m_keys[MAX_KEYS]; ///< Key - int m_vals[MAX_KEYS]; ///< Value for specified key + int m_keys[VerilatedCovConst::MAX_KEYS]; ///< Key + int m_vals[VerilatedCovConst::MAX_KEYS]; ///< Value for specified key // CONSTRUCTORS // Derived classes should call zero() in their constructor VerilatedCovImpItem() { - for (int i = 0; i < MAX_KEYS; ++i) { - m_keys[i] = KEY_UNDEF; + for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) { + m_keys[i] = VerilatedCovConst::KEY_UNDEF; m_vals[i] = 0; } } @@ -83,11 +83,13 @@ public: //============================================================================= // VerilatedCovImp -/// Implementation class for VerilatedCov. See that class for public method information. -/// All value and keys are indexed into a unique number. Thus we can greatly reduce -/// the storage requirements for otherwise identical keys. +/// +/// Implementation class for VerilatedCovContext. See that class for +/// public method information. All value and keys are indexed into a +/// unique number. Thus we can greatly reduce the storage requirements for +/// otherwise identical keys. -class VerilatedCovImp final : VerilatedCovImpBase { +class VerilatedCovImp final : public VerilatedCovContext { private: // TYPES typedef std::map ValueIndexMap; @@ -99,18 +101,21 @@ private: ValueIndexMap m_valueIndexes VL_GUARDED_BY(m_mutex); ///< Unique arbitrary value for values IndexValueMap m_indexValues VL_GUARDED_BY(m_mutex); ///< Unique arbitrary value for keys ItemList m_items VL_GUARDED_BY(m_mutex); ///< List of all items - int m_nextIndex VL_GUARDED_BY(m_mutex) = (KEY_UNDEF + 1); ///< Next insert value + int m_nextIndex VL_GUARDED_BY(m_mutex) + = (VerilatedCovConst::KEY_UNDEF + 1); ///< Next insert value VerilatedCovImpItem* m_insertp VL_GUARDED_BY(m_mutex) = nullptr; ///< Item about to insert const char* m_insertFilenamep VL_GUARDED_BY(m_mutex) = nullptr; ///< Filename about to insert int m_insertLineno VL_GUARDED_BY(m_mutex) = 0; ///< Line number about to insert +public: // CONSTRUCTORS VerilatedCovImp() = default; VL_UNCOPYABLE(VerilatedCovImp); -public: - ~VerilatedCovImp() { clearGuts(); } +protected: + friend class VerilatedCovContext; + virtual ~VerilatedCovImp() override { clearGuts(); } static VerilatedCovImp& imp() VL_MT_SAFE { static VerilatedCovImp s_singleton; return s_singleton; @@ -133,7 +138,7 @@ private: for (const char* pos = text.c_str(); *pos; ++pos) { if (!isprint(*pos) || *pos == '%' || *pos == '"') { char hex[10]; - sprintf(hex, "%%%02X", pos[0]); + VL_SNPRINTF(hex, 10, "%%%02X", pos[0]); rtn += hex; } else { rtn += *pos; @@ -203,8 +208,8 @@ private: } bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match) VL_REQUIRES(m_mutex) { - for (int i = 0; i < MAX_KEYS; ++i) { - if (itemp->m_keys[i] != KEY_UNDEF) { + for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) { + if (itemp->m_keys[i] != VerilatedCovConst::KEY_UNDEF) { // We don't compare keys, only values std::string val = m_indexValues[itemp->m_vals[i]]; if (std::string::npos != val.find(match)) { // Found @@ -235,17 +240,17 @@ private: m_items.clear(); m_indexValues.clear(); m_valueIndexes.clear(); - m_nextIndex = KEY_UNDEF + 1; + m_nextIndex = VerilatedCovConst::KEY_UNDEF + 1; } public: // PUBLIC METHODS - void clear() VL_EXCLUDES(m_mutex) { + void clear() VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock(m_mutex); clearGuts(); } - void clearNonMatch(const char* matchp) VL_EXCLUDES(m_mutex) { + void clearNonMatch(const char* matchp) VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock(m_mutex); if (matchp && matchp[0]) { @@ -260,24 +265,25 @@ public: m_items = newlist; } } - void zero() VL_EXCLUDES(m_mutex) { + void zero() VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock(m_mutex); for (const auto& itemp : m_items) itemp->zero(); } // We assume there's always call to i/f/p in that order - void inserti(VerilatedCovImpItem* itemp) VL_EXCLUDES(m_mutex) { + void inserti(VerilatedCovImpItem* itemp) VL_MT_SAFE_EXCLUDES(m_mutex) { const VerilatedLockGuard lock(m_mutex); assert(!m_insertp); m_insertp = itemp; } - void insertf(const char* filenamep, int lineno) VL_EXCLUDES(m_mutex) { + void insertf(const char* filenamep, int lineno) VL_MT_SAFE_EXCLUDES(m_mutex) { const VerilatedLockGuard lock(m_mutex); m_insertFilenamep = filenamep; m_insertLineno = lineno; } - void insertp(const char* ckeyps[MAX_KEYS], const char* valps[MAX_KEYS]) VL_EXCLUDES(m_mutex) { + void insertp(const char* ckeyps[VerilatedCovConst::MAX_KEYS], + const char* valps[VerilatedCovConst::MAX_KEYS]) VL_MT_SAFE_EXCLUDES(m_mutex) { const VerilatedLockGuard lock(m_mutex); assert(m_insertp); // First two key/vals are filename @@ -296,14 +302,14 @@ public: valps[2] = page_default.c_str(); // Keys -> strings - std::string keys[MAX_KEYS]; - for (int i = 0; i < MAX_KEYS; ++i) { + std::string keys[VerilatedCovConst::MAX_KEYS]; + for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) { if (ckeyps[i] && ckeyps[i][0]) keys[i] = ckeyps[i]; } // Ignore empty keys - for (int i = 0; i < MAX_KEYS; ++i) { + for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) { if (!keys[i].empty()) { - for (int j = i + 1; j < MAX_KEYS; ++j) { + for (int j = i + 1; j < VerilatedCovConst::MAX_KEYS; ++j) { if (keys[i] == keys[j]) { // Duplicate key. Keep the last one keys[i] = ""; break; @@ -313,7 +319,7 @@ public: } // Insert the values int addKeynum = 0; - for (int i = 0; i < MAX_KEYS; ++i) { + for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) { const std::string key = keys[i]; if (!keys[i].empty()) { const std::string val = valps[i]; @@ -334,7 +340,7 @@ public: m_insertp = nullptr; } - void write(const char* filename) VL_EXCLUDES(m_mutex) { + void write(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); const VerilatedLockGuard lock(m_mutex); #ifndef VM_COVERAGE @@ -358,8 +364,8 @@ public: std::string hier; bool per_instance = false; - for (int i = 0; i < MAX_KEYS; ++i) { - if (itemp->m_keys[i] != KEY_UNDEF) { + for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) { + if (itemp->m_keys[i] != VerilatedCovConst::KEY_UNDEF) { std::string key = VerilatedCovKey::shortKey(m_indexValues[itemp->m_keys[i]]); std::string val = m_indexValues[itemp->m_vals[i]]; if (key == VL_CIK_PER_INSTANCE) { @@ -406,64 +412,62 @@ public: }; //============================================================================= -// VerilatedCov +// VerilatedCovContext -void VerilatedCov::clear() VL_MT_SAFE { VerilatedCovImp::imp().clear(); } -void VerilatedCov::clearNonMatch(const char* matchp) VL_MT_SAFE { - VerilatedCovImp::imp().clearNonMatch(matchp); +void VerilatedCovContext::clear() VL_MT_SAFE { impp()->clear(); } +void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE { + impp()->clearNonMatch(matchp); } -void VerilatedCov::zero() VL_MT_SAFE { VerilatedCovImp::imp().zero(); } -void VerilatedCov::write(const char* filenamep) VL_MT_SAFE { - VerilatedCovImp::imp().write(filenamep); +void VerilatedCovContext::zero() VL_MT_SAFE { impp()->zero(); } +void VerilatedCovContext::write(const char* filenamep) VL_MT_SAFE { impp()->write(filenamep); } +void VerilatedCovContext::_inserti(vluint32_t* itemp) VL_MT_SAFE { + impp()->inserti(new VerilatedCoverItemSpec(itemp)); } -void VerilatedCov::_inserti(vluint32_t* itemp) VL_MT_SAFE { - VerilatedCovImp::imp().inserti(new VerilatedCoverItemSpec(itemp)); +void VerilatedCovContext::_inserti(vluint64_t* itemp) VL_MT_SAFE { + impp()->inserti(new VerilatedCoverItemSpec(itemp)); } -void VerilatedCov::_inserti(vluint64_t* itemp) VL_MT_SAFE { - VerilatedCovImp::imp().inserti(new VerilatedCoverItemSpec(itemp)); -} -void VerilatedCov::_insertf(const char* filename, int lineno) VL_MT_SAFE { - VerilatedCovImp::imp().insertf(filename, lineno); +void VerilatedCovContext::_insertf(const char* filename, int lineno) VL_MT_SAFE { + impp()->insertf(filename, lineno); } #define K(n) const char* key##n #define A(n) const char *key##n, const char *valp##n // Argument list #define C(n) key##n, valp##n // Calling argument list #define N(n) "", "" // Null argument list -void VerilatedCov::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10), - A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18), A(19), A(20), - A(21), A(22), A(23), A(24), A(25), A(26), A(27), A(28), - A(29)) VL_MT_SAFE { - const char* keyps[VerilatedCovImpBase::MAX_KEYS] +void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), + A(10), A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18), + A(19), A(20), A(21), A(22), A(23), A(24), A(25), A(26), A(27), + A(28), A(29)) VL_MT_SAFE { + const char* keyps[VerilatedCovConst::MAX_KEYS] = {nullptr, nullptr, nullptr, // filename,lineno,page key0, key1, key2, key3, key4, key5, key6, key7, key8, key9, key10, key11, key12, key13, key14, key15, key16, key17, key18, key19, key20, key21, key22, key23, key24, key25, key26, key27, key28, key29}; - const char* valps[VerilatedCovImpBase::MAX_KEYS] + const char* valps[VerilatedCovConst::MAX_KEYS] = {nullptr, nullptr, nullptr, // filename,lineno,page valp0, valp1, valp2, valp3, valp4, valp5, valp6, valp7, valp8, valp9, valp10, valp11, valp12, valp13, valp14, valp15, valp16, valp17, valp18, valp19, valp20, valp21, valp22, valp23, valp24, valp25, valp26, valp27, valp28, valp29}; - VerilatedCovImp::imp().insertp(keyps, valps); + impp()->insertp(keyps, valps); } // And versions with fewer arguments (oh for a language with named parameters!) -void VerilatedCov::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), - A(9)) VL_MT_SAFE { +void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), + A(9)) VL_MT_SAFE { _insertp(C(0), C(1), C(2), C(3), C(4), C(5), C(6), C(7), C(8), C(9), N(10), N(11), N(12), N(13), N(14), N(15), N(16), N(17), N(18), N(19), N(20), N(21), N(22), N(23), N(24), N(25), N(26), N(27), N(28), N(29)); } -void VerilatedCov::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10), - A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18), - A(19)) VL_MT_SAFE { +void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), + A(10), A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18), + A(19)) VL_MT_SAFE { _insertp(C(0), C(1), C(2), C(3), C(4), C(5), C(6), C(7), C(8), C(9), C(10), C(11), C(12), C(13), C(14), C(15), C(16), C(17), C(18), C(19), N(20), N(21), N(22), N(23), N(24), N(25), N(26), N(27), N(28), N(29)); } // Backward compatibility for Verilator -void VerilatedCov::_insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4), - const std::string& val4, A(5), A(6), A(7)) VL_MT_SAFE { +void VerilatedCovContext::_insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4), + const std::string& val4, A(5), A(6), A(7)) VL_MT_SAFE { std::string val2str = vlCovCvtToStr(val2); std::string val3str = vlCovCvtToStr(val3); _insertp(C(0), C(1), key2, val2str.c_str(), key3, val3str.c_str(), key4, val4.c_str(), C(5), @@ -474,3 +478,26 @@ void VerilatedCov::_insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4), #undef C #undef N #undef K + +//============================================================================= +// VerilatedCov + +#ifndef VL_NO_LEGACY +VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE { + return Verilated::threadContextp()->coveragep(); +} +#endif + +//============================================================================= +// VerilatedContext + +VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE { + static VerilatedMutex s_mutex; + if (VL_UNLIKELY(!m_coveragep)) { + const VerilatedLockGuard lock(s_mutex); + if (VL_LIKELY(!m_coveragep)) { // Not redundant, prevents race + m_coveragep.reset(new VerilatedCovImp); + } + } + return reinterpret_cast(m_coveragep.get()); +} diff --git a/include/verilated_cov.h b/include/verilated_cov.h index 4fd213a7d..450b4c071 100644 --- a/include/verilated_cov.h +++ b/include/verilated_cov.h @@ -16,15 +16,18 @@ /// //============================================================================= -#ifndef _VERILATED_COV_H_ -#define _VERILATED_COV_H_ 1 +#ifndef VERILATOR_VERILATED_COV_H_ +#define VERILATOR_VERILATED_COV_H_ #include "verilatedos.h" +#include "verilated.h" #include #include #include +class VerilatedCovImp; + //============================================================================= /// Conditionally compile coverage code @@ -68,9 +71,9 @@ /// VL_COVER_INSERT(&m_cases[i], "comment", "Coverage Case", "i", cvtToNumStr(i)); /// } -#define VL_COVER_INSERT(countp, ...) \ - VL_IF_COVER(VerilatedCov::_inserti(countp); VerilatedCov::_insertf(__FILE__, __LINE__); \ - VerilatedCov::_insertp("hier", name(), __VA_ARGS__)) +#define VL_COVER_INSERT(covcontextp, countp, ...) \ + VL_IF_COVER(covcontextp->_inserti(countp); covcontextp->_insertf(__FILE__, __LINE__); \ + covcontextp->_insertp("hier", name(), __VA_ARGS__)) //============================================================================= /// Convert VL_COVER_INSERT value arguments to strings @@ -83,29 +86,35 @@ template std::string vlCovCvtToStr(const T& t) VL_PURE { //============================================================================= // VerilatedCov -/// Verilator coverage global class -//// -/// Global class with methods affecting all coverage data. +/// Verilator coverage per-context structure /// All public methods in this class are thread safe. -class VerilatedCov final { - VL_UNCOPYABLE(VerilatedCov); +class VerilatedCovContext VL_NOT_FINAL : public VerilatedVirtualBase { + VL_UNCOPYABLE(VerilatedCovContext); public: - // GLOBAL METHODS + // METHODS /// Return default filename static const char* defaultFilename() VL_PURE { return "coverage.dat"; } /// Write all coverage data to a file - static void write(const char* filenamep = defaultFilename()) VL_MT_SAFE; + void write(const char* filenamep = defaultFilename()) VL_MT_SAFE; + /// Clear coverage points (and call delete on all items) + void clear() VL_MT_SAFE; + /// Clear items not matching the provided string + void clearNonMatch(const char* matchp) VL_MT_SAFE; + /// Zero coverage points + void zero() VL_MT_SAFE; + +public: // But Internal use only /// Insert a coverage item /// We accept from 1-30 key/value pairs, all as strings. /// Call _insert1, followed by _insert2 and _insert3 /// Do not call directly; use VL_COVER_INSERT or higher level macros instead // _insert1: Remember item pointer with count. (Not const, as may add zeroing function) - static void _inserti(vluint32_t* itemp) VL_MT_SAFE; - static void _inserti(vluint64_t* itemp) VL_MT_SAFE; + void _inserti(vluint32_t* itemp) VL_MT_SAFE; + void _inserti(vluint64_t* itemp) VL_MT_SAFE; // _insert2: Set default filename and line number - static void _insertf(const char* filename, int lineno) VL_MT_SAFE; + void _insertf(const char* filename, int lineno) VL_MT_SAFE; // _insert3: Set parameters // We could have just the maximum argument version, but this compiles // much slower (nearly 2x) than having smaller versions also. However @@ -113,25 +122,65 @@ public: #define K(n) const char* key##n #define A(n) const char *key##n, const char *valp##n // Argument list #define D(n) const char *key##n = nullptr, const char *valp##n = nullptr // Argument list - static void _insertp(D(0), D(1), D(2), D(3), D(4), D(5), D(6), D(7), D(8), D(9)); - static void _insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10), D(11), - D(12), D(13), D(14), D(15), D(16), D(17), D(18), D(19)); - static void _insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10), A(11), - A(12), A(13), A(14), A(15), A(16), A(17), A(18), A(19), A(20), D(21), - D(22), D(23), D(24), D(25), D(26), D(27), D(28), D(29)); + void _insertp(D(0), D(1), D(2), D(3), D(4), D(5), D(6), D(7), D(8), D(9)); + void _insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10), D(11), D(12), + D(13), D(14), D(15), D(16), D(17), D(18), D(19)); + void _insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10), A(11), A(12), + A(13), A(14), A(15), A(16), A(17), A(18), A(19), A(20), D(21), D(22), D(23), + D(24), D(25), D(26), D(27), D(28), D(29)); // Backward compatibility for Verilator - static void _insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4), const std::string& val4, - A(5), A(6), A(7)); + void _insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4), const std::string& val4, A(5), + A(6), A(7)); #undef K #undef A #undef D - /// Clear coverage points (and call delete on all items) - static void clear() VL_MT_SAFE; - /// Clear items not matching the provided string - static void clearNonMatch(const char* matchp) VL_MT_SAFE; - /// Zero coverage points - static void zero() VL_MT_SAFE; + +protected: + friend class VerilatedCovImp; + // CONSTRUCTORS + // Internal: Only made as part of VerilatedCovImp + VerilatedCovContext() {} + virtual ~VerilatedCovContext() {} + + // METHODS + // Internal: access to implementation class + VerilatedCovImp* impp() { return reinterpret_cast(this); } }; +//============================================================================= +// VerilatedCov +/// Verilator coverage global class +/// +/// Global class that accesses via current thread's context. These are +/// provided for backward-compatibility, use VerilatedContext->coveragep() +/// instead. + +#ifndef VL_NO_LEGACY +class VerilatedCov final { + VL_UNCOPYABLE(VerilatedCov); + +public: + // METHODS + /// Return default filename for the current thread + static const char* defaultFilename() VL_PURE { return VerilatedCovContext::defaultFilename(); } + /// Write all coverage data to a file for the current thread + static void write(const char* filenamep = defaultFilename()) VL_MT_SAFE { + threadCovp()->write(filenamep); + } + /// Clear coverage points (and call delete on all items) for the current thread + static void clear() VL_MT_SAFE { threadCovp()->clear(); } + /// Clear items not matching the provided string for the current thread + static void clearNonMatch(const char* matchp) VL_MT_SAFE { + threadCovp()->clearNonMatch(matchp); + } + /// Zero coverage points for the current thread + static void zero() VL_MT_SAFE { threadCovp()->zero(); } + +private: + /// Current thread's coverage structure + static VerilatedCovContext* threadCovp() VL_MT_SAFE; +}; +#endif + #endif // Guard diff --git a/include/verilated_cov_key.h b/include/verilated_cov_key.h index 21e64b308..e0dad8c61 100644 --- a/include/verilated_cov_key.h +++ b/include/verilated_cov_key.h @@ -16,8 +16,8 @@ /// //============================================================================= -#ifndef _VERILATED_COV_KEY_H_ -#define _VERILATED_COV_KEY_H_ 1 +#ifndef VERILATOR_VERILATED_COV_KEY_H_ +#define VERILATOR_VERILATED_COV_KEY_H_ #include "verilatedos.h" diff --git a/include/verilated_dpi.cpp b/include/verilated_dpi.cpp index 816b92c2e..5a9e8aa74 100644 --- a/include/verilated_dpi.cpp +++ b/include/verilated_dpi.cpp @@ -19,7 +19,7 @@ /// //========================================================================= -#define _VERILATED_DPI_CPP_ +#define VERILATOR_VERILATED_DPI_CPP_ #include "verilatedos.h" #include "verilated_dpi.h" @@ -37,11 +37,11 @@ //====================================================================== // Internal macros -#define _VL_SVDPI_WARN(...) VL_PRINTF_MT(__VA_ARGS__) +#define VL_SVDPI_WARN_(...) VL_PRINTF_MT(__VA_ARGS__) // Function requires a "context" in the import declaration -#define _VL_SVDPI_CONTEXT_WARN() \ - _VL_SVDPI_WARN("%%Warning: DPI C Function called by Verilog DPI import with missing " \ +#define VL_SVDPI_CONTEXT_WARN_() \ + VL_SVDPI_WARN_("%%Warning: DPI C Function called by Verilog DPI import with missing " \ "'context' keyword.\n") //====================================================================== @@ -120,7 +120,7 @@ void svGetPartselLogic(svLogicVecVal* dp, const svLogicVecVal* sp, int lsb, int dp[VL_WORDS_I(width) - 1].bval &= VL_MASK_I(width); } void svPutPartselBit(svBitVecVal* dp, const svBitVecVal s, int lbit, int width) { - // See also _VL_INSERT_WI + // See also _vl_insert_WI int hbit = lbit + width - 1; int hoffset = VL_BITBIT_I(hbit); int loffset = VL_BITBIT_I(lbit); @@ -218,7 +218,7 @@ static void* _vl_sv_adjusted_datap(const VerilatedDpiOpenVar* varp, int nargs, i int indx2, int indx3) { void* datap = varp->datap(); if (VL_UNLIKELY(nargs != varp->udims())) { - _VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function called on" + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function called on" " %d dimensional array using %d dimensional function.\n", varp->udims(), nargs); return nullptr; @@ -226,7 +226,7 @@ static void* _vl_sv_adjusted_datap(const VerilatedDpiOpenVar* varp, int nargs, i if (nargs >= 1) { datap = varp->datapAdjustIndex(datap, 1, indx1); if (VL_UNLIKELY(!datap)) { - _VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function index 1 " + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function index 1 " "out of bounds; %d outside [%d:%d].\n", indx1, varp->left(1), varp->right(1)); return nullptr; @@ -235,7 +235,7 @@ static void* _vl_sv_adjusted_datap(const VerilatedDpiOpenVar* varp, int nargs, i if (nargs >= 2) { datap = varp->datapAdjustIndex(datap, 2, indx2); if (VL_UNLIKELY(!datap)) { - _VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function index 2 " + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function index 2 " "out of bounds; %d outside [%d:%d].\n", indx2, varp->left(2), varp->right(2)); return nullptr; @@ -244,7 +244,7 @@ static void* _vl_sv_adjusted_datap(const VerilatedDpiOpenVar* varp, int nargs, i if (nargs >= 3) { datap = varp->datapAdjustIndex(datap, 3, indx3); if (VL_UNLIKELY(!datap)) { - _VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function index 3 " + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function index 3 " "out of bounds; %d outside [%d:%d].\n", indx1, varp->left(3), varp->right(3)); return nullptr; @@ -285,7 +285,7 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, return; } default: // LCOV_EXCL_START // Errored earlier - _VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", varp->vltype()); return; // LCOV_EXCL_STOP } @@ -327,7 +327,7 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl return; } default: // LCOV_EXCL_START // Errored earlier - _VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", varp->vltype()); return; // LCOV_EXCL_STOP } @@ -343,14 +343,14 @@ static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecV case VLVT_UINT8: *(reinterpret_cast(datap)) = s[0]; return; case VLVT_UINT16: *(reinterpret_cast(datap)) = s[0]; return; case VLVT_UINT32: *(reinterpret_cast(datap)) = s[0]; return; - case VLVT_UINT64: *(reinterpret_cast(datap)) = _VL_SET_QII(s[1], s[0]); break; + case VLVT_UINT64: *(reinterpret_cast(datap)) = VL_SET_QII(s[1], s[0]); break; case VLVT_WDATA: { WDataOutP wdatap = (reinterpret_cast(datap)); for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) wdatap[i] = s[i]; return; } default: // LCOV_EXCL_START // Errored earlier - _VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", varp->vltype()); return; // LCOV_EXCL_STOP } @@ -365,16 +365,14 @@ static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogic case VLVT_UINT8: *(reinterpret_cast(datap)) = s[0].aval; return; case VLVT_UINT16: *(reinterpret_cast(datap)) = s[0].aval; return; case VLVT_UINT32: *(reinterpret_cast(datap)) = s[0].aval; return; - case VLVT_UINT64: - *(reinterpret_cast(datap)) = _VL_SET_QII(s[1].aval, s[0].aval); - break; + case VLVT_UINT64: *(reinterpret_cast(datap)) = VL_SET_QII(s[1].aval, s[0].aval); break; case VLVT_WDATA: { WDataOutP wdatap = (reinterpret_cast(datap)); for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) wdatap[i] = s[i].aval; return; } default: // LCOV_EXCL_START // Errored earlier - _VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", varp->vltype()); return; // LCOV_EXCL_STOP } @@ -390,7 +388,7 @@ static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1 switch (varp->vltype()) { // LCOV_EXCL_BR_LINE case VLVT_UINT8: return (*(reinterpret_cast(datap))) & 1; default: // LCOV_EXCL_START // Errored earlier - _VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", varp->vltype()); return 0; // LCOV_EXCL_STOP } @@ -406,7 +404,7 @@ static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int narg switch (varp->vltype()) { // LCOV_EXCL_BR_LINE case VLVT_UINT8: *(reinterpret_cast(datap)) = value; return; default: // LCOV_EXCL_START // Errored earlier - _VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", varp->vltype()); return; // LCOV_EXCL_STOP } @@ -720,7 +718,7 @@ void svPutLogicArrElem3(const svOpenArrayHandle d, svLogic value, int indx1, int svScope svGetScope() { if (VL_UNLIKELY(!Verilated::dpiInContext())) { - _VL_SVDPI_CONTEXT_WARN(); + VL_SVDPI_CONTEXT_WARN_(); return nullptr; } // NOLINTNEXTLINE(google-readability-casting) @@ -742,7 +740,7 @@ const char* svGetNameFromScope(const svScope scope) { svScope svGetScopeFromName(const char* scopeName) { // NOLINTNEXTLINE(google-readability-casting) - return (svScope)(VerilatedImp::scopeFind(scopeName)); + return (svScope)(Verilated::threadContextp()->scopeFind(scopeName)); } int svPutUserData(const svScope scope, void* userKey, void* userData) { @@ -756,7 +754,7 @@ void* svGetUserData(const svScope scope, void* userKey) { int svGetCallerInfo(const char** fileNamepp, int* lineNumberp) { if (VL_UNLIKELY(!Verilated::dpiInContext())) { - _VL_SVDPI_CONTEXT_WARN(); + VL_SVDPI_CONTEXT_WARN_(); return false; } if (VL_LIKELY(fileNamepp)) *fileNamepp = Verilated::dpiFilenamep(); // thread local diff --git a/include/verilated_dpi.h b/include/verilated_dpi.h index 3b9744ee4..cc2e27ef3 100644 --- a/include/verilated_dpi.h +++ b/include/verilated_dpi.h @@ -20,8 +20,8 @@ /// //************************************************************************* -#ifndef _VERILATED_DPI_H_ -#define _VERILATED_DPI_H_ 1 ///< Header Guard +#ifndef VERILATOR_VERILATED_DPI_H_ +#define VERILATOR_VERILATED_DPI_H_ ///< Header Guard #include "verilatedos.h" #include "verilated.h" // Also presumably included by caller @@ -32,18 +32,18 @@ //=================================================================== // SETTING OPERATORS -/// Convert svBitVecVal to Verilator internal data +// Convert svBitVecVal to Verilator internal data static inline void VL_SET_W_SVBV(int obits, WDataOutP owp, const svBitVecVal* lwp) VL_MT_SAFE { int words = VL_WORDS_I(obits); for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i]; owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits); } static inline QData VL_SET_Q_SVBV(const svBitVecVal* lwp) VL_MT_SAFE { - return _VL_SET_QII(lwp[1], lwp[0]); + return VL_SET_QII(lwp[1], lwp[0]); } static inline IData VL_SET_I_SVBV(const svBitVecVal* lwp) VL_MT_SAFE { return lwp[0]; } -/// Convert Verilator internal data to svBitVecVal +// Convert Verilator internal data to svBitVecVal static inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, WDataInP lwp) VL_MT_SAFE { int words = VL_WORDS_I(obits); for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i]; @@ -54,20 +54,20 @@ static inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, QData ld) VL_MT_SAFE { VL_SET_WQ(owp, ld); } -/// Convert svLogicVecVal to Verilator internal data -/// Note these functions ignore X/Z in svLogicVecVal +// Convert svLogicVecVal to Verilator internal data +// Note these functions ignore X/Z in svLogicVecVal static inline void VL_SET_W_SVLV(int obits, WDataOutP owp, const svLogicVecVal* lwp) VL_MT_SAFE { int words = VL_WORDS_I(obits); for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i].aval; owp[words - 1] = lwp[words - 1].aval & VL_MASK_I(obits); } static inline QData VL_SET_Q_SVLV(const svLogicVecVal* lwp) VL_MT_SAFE { - return _VL_SET_QII(lwp[1].aval, lwp[0].aval); + return VL_SET_QII(lwp[1].aval, lwp[0].aval); } static inline IData VL_SET_I_SVLV(const svLogicVecVal* lwp) VL_MT_SAFE { return lwp[0].aval; } -/// Convert Verilator internal data to svLogicVecVal -/// Note these functions never create X/Z in svLogicVecVal +// Convert Verilator internal data to svLogicVecVal +// Note these functions never create X/Z in svLogicVecVal static inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, WDataInP lwp) VL_MT_SAFE { int words = VL_WORDS_I(obits); for (int i = 0; i < words; ++i) owp[i].bval = 0; diff --git a/include/verilated_fst_c.cpp b/include/verilated_fst_c.cpp index c205ceda8..cfbec316d 100644 --- a/include/verilated_fst_c.cpp +++ b/include/verilated_fst_c.cpp @@ -15,7 +15,6 @@ /// \brief C++ Tracing in FST Format /// //============================================================================= -// SPDIFF_OFF // clang-format off @@ -66,8 +65,8 @@ VerilatedFst::~VerilatedFst() { if (m_strbuf) VL_DO_CLEAR(delete[] m_strbuf, m_strbuf = nullptr); } -void VerilatedFst::open(const char* filename) VL_MT_UNSAFE { - m_assertOne.check(); +void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock(m_mutex); m_fst = fstWriterCreate(filename, 1); fstWriterSetPackType(m_fst, FST_WR_PT_LZ4); fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref @@ -97,15 +96,16 @@ void VerilatedFst::open(const char* filename) VL_MT_UNSAFE { if (!m_strbuf) m_strbuf = new char[maxBits() + 32]; } -void VerilatedFst::close() { - m_assertOne.check(); - VerilatedTrace::close(); +void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock(m_mutex); + VerilatedTrace::closeBase(); fstWriterClose(m_fst); m_fst = nullptr; } -void VerilatedFst::flush() { - VerilatedTrace::flush(); +void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock(m_mutex); + VerilatedTrace::flushBase(); fstWriterFlushContext(m_fst); } diff --git a/include/verilated_fst_c.h b/include/verilated_fst_c.h index f21a6087c..64efe75c1 100644 --- a/include/verilated_fst_c.h +++ b/include/verilated_fst_c.h @@ -17,8 +17,8 @@ //============================================================================= // SPDIFF_OFF -#ifndef _VERILATED_FST_C_H_ -#define _VERILATED_FST_C_H_ 1 +#ifndef VERILATOR_VERILATED_FST_C_H_ +#define VERILATOR_VERILATED_FST_C_H_ #include "verilated.h" #include "verilated_trace.h" @@ -32,8 +32,8 @@ //============================================================================= // VerilatedFst -/// Base class to create a Verilator FST dump -/// This is an internally used class - see VerilatedFstC for what to call from applications +// Base class to create a Verilator FST dump +// This is an internally used class - see VerilatedFstC for what to call from applications class VerilatedFst final : public VerilatedTrace { private: @@ -82,27 +82,28 @@ protected: public: //========================================================================= // External interface to client code + // (All must be threadsafe) explicit VerilatedFst(void* fst = nullptr); ~VerilatedFst(); - /// Open the file; call isOpen() to see if errors - void open(const char* filename) VL_MT_UNSAFE; - /// Close the file - void close() VL_MT_UNSAFE; - /// Flush any remaining data to this file - void flush() VL_MT_UNSAFE; - /// Is file open? - bool isOpen() const { return m_fst != nullptr; } + // Open the file; call isOpen() to see if errors + void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex); + // Close the file + void close() VL_MT_SAFE_EXCLUDES(m_mutex); + // Flush any remaining data to this file + void flush() VL_MT_SAFE_EXCLUDES(m_mutex); + // Return if file is open + bool isOpen() const VL_MT_SAFE { return m_fst != nullptr; } //========================================================================= // Internal interface to Verilator generated code - /// Inside dumping routines, declare a data type + // Inside dumping routines, declare a data type void declDTypeEnum(int dtypenum, const char* name, vluint32_t elements, unsigned int minValbits, const char** itemNamesp, const char** itemValuesp); - /// Inside dumping routines, declare a signal + // Inside dumping routines, declare a signal void declBit(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir, fstVarType vartype, bool array, int arraynum); void declBus(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir, @@ -126,7 +127,6 @@ template <> void VerilatedTrace::set_time_resolution(const std::st // VerilatedFstC /// Create a FST dump file in C standalone (no SystemC) simulations. /// Also derived for use in SystemC simulations. -/// Thread safety: Unless otherwise indicated, every function is VL_MT_UNSAFE_ONE class VerilatedFstC final { VerilatedFst m_sptrace; ///< Trace file being created @@ -135,22 +135,22 @@ class VerilatedFstC final { VL_UNCOPYABLE(VerilatedFstC); public: + /// Construct the dump. Optional argument is ignored. explicit VerilatedFstC(void* filep = nullptr) : m_sptrace{filep} {} + /// Destruct, flush, and close the dump ~VerilatedFstC() { close(); } - /// Routines can only be called from one thread; allow next call from different thread - void changeThread() { spTrace()->changeThread(); } - // ACCESSORS - /// Is file open? - bool isOpen() const { return m_sptrace.isOpen(); } - // METHODS + // METHODS - User called + + /// Return if file is open + bool isOpen() const VL_MT_SAFE { return m_sptrace.isOpen(); } /// Open a new FST file - void open(const char* filename) VL_MT_UNSAFE_ONE { m_sptrace.open(filename); } + void open(const char* filename) VL_MT_SAFE { m_sptrace.open(filename); } /// Close dump - void close() VL_MT_UNSAFE_ONE { m_sptrace.close(); } + void close() VL_MT_SAFE { m_sptrace.close(); } /// Flush dump - void flush() VL_MT_UNSAFE_ONE { m_sptrace.flush(); } + void flush() VL_MT_SAFE { m_sptrace.flush(); } /// Write one cycle of dump data void dump(vluint64_t timeui) { m_sptrace.dump(timeui); } /// Write one cycle of dump data - backward compatible and to reduce @@ -158,16 +158,26 @@ public: void dump(double timestamp) { dump(static_cast(timestamp)); } void dump(vluint32_t timestamp) { dump(static_cast(timestamp)); } void dump(int timestamp) { dump(static_cast(timestamp)); } - /// Set time units (s/ms, defaults to ns) - /// For Verilated models, these propage from the Verilated default --timeunit - void set_time_unit(const char* unitp) { m_sptrace.set_time_unit(unitp); } - void set_time_unit(const std::string& unit) { m_sptrace.set_time_unit(unit); } - /// Set time resolution (s/ms, defaults to ns) - /// For Verilated models, these propage from the Verilated default --timeunit - void set_time_resolution(const char* unitp) { m_sptrace.set_time_resolution(unitp); } - void set_time_resolution(const std::string& unit) { m_sptrace.set_time_resolution(unit); } - /// Internal class access + // METHODS - Internal/backward compatible + // \protectedsection + + // Set time units (s/ms, defaults to ns) + // Users should not need to call this, as for Verilated models, these + // propage from the Verilated default timeunit + void set_time_unit(const char* unitp) VL_MT_SAFE { m_sptrace.set_time_unit(unitp); } + void set_time_unit(const std::string& unit) VL_MT_SAFE { m_sptrace.set_time_unit(unit); } + // Set time resolution (s/ms, defaults to ns) + // Users should not need to call this, as for Verilated models, these + // propage from the Verilated default timeprecision + void set_time_resolution(const char* unitp) VL_MT_SAFE { + m_sptrace.set_time_resolution(unitp); + } + void set_time_resolution(const std::string& unit) VL_MT_SAFE { + m_sptrace.set_time_resolution(unit); + } + + // Internal class access inline VerilatedFst* spTrace() { return &m_sptrace; }; }; diff --git a/include/verilated_heavy.h b/include/verilated_heavy.h index 91d2272ae..cfc013b2c 100644 --- a/include/verilated_heavy.h +++ b/include/verilated_heavy.h @@ -21,8 +21,8 @@ /// //************************************************************************* -#ifndef _VERILATED_HEAVY_H_ -#define _VERILATED_HEAVY_H_ 1 ///< Header Guard +#ifndef VERILATOR_VERILATED_HEAVY_H_ +#define VERILATOR_VERILATED_HEAVY_H_ ///< Header Guard #include "verilated.h" @@ -900,8 +900,8 @@ extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp extern void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp, ...) VL_MT_SAFE; extern std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE; -extern void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, - int width) VL_MT_SAFE; +extern void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, int width, + VerilatedContext* contextp) VL_MT_SAFE; extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE; inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, CData& rdr) VL_MT_SAFE { WData rwp[2]; // WData must always be at least 2 @@ -956,10 +956,4 @@ extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE; extern IData VL_FGETS_NI(std::string& dest, IData fpi); -//====================================================================== -// Dumping - -extern const char* vl_dumpctl_filenamep(bool setit = false, - const std::string& filename = "") VL_MT_SAFE; - #endif // Guard diff --git a/include/verilated_imp.h b/include/verilated_imp.h index e9f343a16..4c462d0f1 100644 --- a/include/verilated_imp.h +++ b/include/verilated_imp.h @@ -16,11 +16,12 @@ /// //========================================================================= -#ifndef _VERILATED_IMP_H_ -#define _VERILATED_IMP_H_ 1 ///< Header Guard +#ifndef VERILATOR_VERILATED_IMP_H_ +#define VERILATOR_VERILATED_IMP_H_ ///< Header Guard // clang-format off -#if !defined(_VERILATED_CPP_) && !defined(_VERILATED_DPI_CPP_) && !defined(_VERILATED_VPI_CPP_) +#if !defined(VERILATOR_VERILATED_CPP_) && !defined(VERILATOR_VERILATED_DPI_CPP_) \ + && !defined(VERILATOR_VERILATED_VPI_CPP_) && !defined(VERILATOR_VERILATED_SAVE_CPP_) # error "verilated_imp.h only to be included by verilated*.cpp internals" #endif @@ -99,13 +100,13 @@ private: public: // METHODS //// Add message to queue (called by producer) - void post(const VerilatedMsg& msg) VL_EXCLUDES(m_mutex) { + void post(const VerilatedMsg& msg) VL_MT_SAFE_EXCLUDES(m_mutex) { const VerilatedLockGuard lock(m_mutex); m_queue.insert(msg); // Pass by value to copy the message into queue ++m_depth; } /// Service queue until completion (called by consumer) - void process() VL_EXCLUDES(m_mutex) { + void process() VL_MT_SAFE_EXCLUDES(m_mutex) { // Tracking m_depth is redundant to e.g. getting the mutex and looking at queue size, // but on the reader side it's 4x faster to test an atomic then getting a mutex while (m_depth) { @@ -190,78 +191,238 @@ public: } }; +//====================================================================== +// VerilatedContextImpData + +/// Class for hidden implementation members inside VerilatedContext +// Avoids needing std::unordered_map inside verilated.h +class VerilatedContextImpData final { + friend class VerilatedContext; + friend class VerilatedContextImp; + +protected: + /// Map of + // Used by scopeInsert, scopeFind, scopeErase, scopeNameMap + mutable VerilatedMutex m_nameMutex; ///< Protect m_nameMap + VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex); +}; + +//====================================================================== +// VerilatedContextImp +// Class to "add" implementation-only methods to VerilatedContext + +class VerilatedContextImp final : VerilatedContext { + friend class VerilatedContext; + + // MEMBERS - non-static not allowed, use only VerilatedContext + // Select initial value of otherwise uninitialized signals. + // Internal note: Globals may multi-construct, see verilated.cpp top. + + // Medium speed, so uses singleton accessing + struct Statics { + VerilatedMutex s_randMutex; // Mutex protecting s_randSeedEpoch + // Number incrementing on each reseed, 0=illegal + int s_randSeedEpoch = 1; // Reads ok, wish had a VL_WRITE_GUARDED_BY(s_randMutex) + }; + static Statics& s() { + static Statics s_s; + return s_s; + } + +private: + // CONSTRUCTORS - no data can live here, use only VerilatedContext + VerilatedContextImp() = delete; + ~VerilatedContextImp() = delete; + +public: // But only for verilated*.cpp + // METHODS - extending into VerilatedContext, call via impp()-> + + // Random seed handling + vluint64_t randSeedDefault64() const VL_MT_SAFE; + static vluint32_t randSeedEpoch() VL_MT_SAFE { return s().s_randSeedEpoch; } + + // METHODS - timeformat + int timeFormatUnits() VL_MT_SAFE { + if (m_s.m_timeFormatUnits == VerilatedContext::Serialized::UNITS_NONE) + return timeprecision(); + return m_s.m_timeFormatUnits; + } + void timeFormatUnits(int value) VL_MT_SAFE { m_s.m_timeFormatUnits = value; } + int timeFormatPrecision() const VL_MT_SAFE { return m_s.m_timeFormatPrecision; } + void timeFormatPrecision(int value) VL_MT_SAFE { m_s.m_timeFormatPrecision = value; } + int timeFormatWidth() const VL_MT_SAFE { return m_s.m_timeFormatWidth; } + void timeFormatWidth(int value) VL_MT_SAFE { m_s.m_timeFormatWidth = value; } + std::string timeFormatSuffix() VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { + const VerilatedLockGuard lock(m_timeDumpMutex); + return m_timeFormatSuffix; + } + void timeFormatSuffix(const std::string& value) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { + const VerilatedLockGuard lock(m_timeDumpMutex); + m_timeFormatSuffix = value; + } + + // METHODS - arguments + std::string argPlusMatch(const char* prefixp) VL_MT_SAFE_EXCLUDES(m_argMutex); + std::pair argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex); + +public: // But only for verilated*.cpp + // METHODS - scope name + void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE; + void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE; + +public: // But only for verilated*.cpp + // METHODS - file IO + IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) { + const VerilatedLockGuard lock(m_fdMutex); + if (m_fdFreeMct.empty()) return 0; + IData idx = m_fdFreeMct.back(); + m_fdFreeMct.pop_back(); + m_fdps[idx] = fopen(filenamep, "w"); + if (VL_UNLIKELY(!m_fdps[idx])) return 0; + return (1 << idx); + } + IData fdNew(const char* filenamep, const char* modep) VL_MT_SAFE_EXCLUDES(m_fdMutex) { + FILE* fp = fopen(filenamep, modep); + if (VL_UNLIKELY(!fp)) return 0; + // Bit 31 indicates it's a descriptor not a MCD + const VerilatedLockGuard lock(m_fdMutex); + if (m_fdFree.empty()) { + // Need to create more space in m_fdps and m_fdFree + const std::size_t start = std::max(31UL + 1UL + 3UL, m_fdps.size()); + const std::size_t excess = 10; + m_fdps.resize(start + excess); + std::fill(m_fdps.begin() + start, m_fdps.end(), (FILE*)0); + m_fdFree.resize(excess); + for (std::size_t i = 0, id = start; i < m_fdFree.size(); ++i, ++id) { + m_fdFree[i] = id; + } + } + IData idx = m_fdFree.back(); + m_fdFree.pop_back(); + m_fdps[idx] = fp; + return (idx | (1UL << 31)); // bit 31 indicates not MCD + } + void fdFlush(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) { + const VerilatedLockGuard lock(m_fdMutex); + const VerilatedFpList fdlist = fdToFpList(fdi); + for (const auto& i : fdlist) fflush(i); + } + IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE_EXCLUDES(m_fdMutex) { + const VerilatedLockGuard lock(m_fdMutex); + const VerilatedFpList fdlist = fdToFpList(fdi); + if (VL_UNLIKELY(fdlist.size() != 1)) return 0; + return static_cast( + fseek(*fdlist.begin(), static_cast(offset), static_cast(origin))); + } + IData fdTell(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) { + const VerilatedLockGuard lock(m_fdMutex); + const VerilatedFpList fdlist = fdToFpList(fdi); + if (VL_UNLIKELY(fdlist.size() != 1)) return 0; + return static_cast(ftell(*fdlist.begin())); + } + void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE_EXCLUDES(m_fdMutex) { + const VerilatedLockGuard lock(m_fdMutex); + const VerilatedFpList fdlist = fdToFpList(fdi); + for (const auto& i : fdlist) { + if (VL_UNLIKELY(!i)) continue; + (void)fwrite(output.c_str(), 1, output.size(), i); + } + } + void fdClose(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) { + const VerilatedLockGuard lock(m_fdMutex); + if ((fdi & (1 << 31)) != 0) { + // Non-MCD case + IData idx = VL_MASK_I(31) & fdi; + if (VL_UNLIKELY(idx >= m_fdps.size())) return; + if (VL_UNLIKELY(!m_fdps[idx])) return; // Already free + fclose(m_fdps[idx]); + m_fdps[idx] = (FILE*)0; + m_fdFree.push_back(idx); + } else { + // MCD case + for (int i = 0; (fdi != 0) && (i < 31); i++, fdi >>= 1) { + if (fdi & VL_MASK_I(1)) { + fclose(m_fdps[i]); + m_fdps[i] = nullptr; + m_fdFreeMct.push_back(i); + } + } + } + } + inline FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) { + const VerilatedLockGuard lock(m_fdMutex); + const VerilatedFpList fdlist = fdToFpList(fdi); + if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr; + return *fdlist.begin(); + } + +private: + VerilatedFpList fdToFpList(IData fdi) VL_REQUIRES(m_fdMutex) { + VerilatedFpList fp; + if ((fdi & (1 << 31)) != 0) { + // Non-MCD case + const IData idx = fdi & VL_MASK_I(31); + switch (idx) { + case 0: fp.push_back(stdin); break; + case 1: fp.push_back(stdout); break; + case 2: fp.push_back(stderr); break; + default: + if (VL_LIKELY(idx < m_fdps.size())) fp.push_back(m_fdps[idx]); + break; + } + } else { + // MCD Case + for (size_t i = 0; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) { + if (fdi & VL_MASK_I(1)) fp.push_back(m_fdps[i]); + } + } + return fp; + } + +protected: + // METHODS - protected + void commandArgsAddGuts(int argc, const char** argv); + void commandArgVl(const std::string& arg); + bool commandArgVlValue(const std::string& arg, const std::string& prefix, std::string& valuer); + void commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex); +}; + //====================================================================== // VerilatedImp class VerilatedImpData final { // Whole class is internal use only - Global information shared between verilated*.cpp files. + // All only medium-speed, so use singleton function protected: friend class Verilated; friend class VerilatedImp; // TYPES - typedef std::vector ArgVec; typedef std::map, void*> UserMap; typedef std::map ExportNameMap; // MEMBERS - - struct Serialized { // All these members serialized/deserialized - int m_timeFormatUnits = UNITS_NONE; // $timeformat units - int m_timeFormatPrecision = 0; // $timeformat number of decimal places - int m_timeFormatWidth = 20; // $timeformat character width - enum { UNITS_NONE = 99 }; // Default based on precision - Serialized() = default; - ~Serialized() = default; - } m_ser; - - VerilatedMutex m_sergMutex; ///< Protect m_ser - struct SerializedG { // All these members serialized/deserialized and guarded - std::string m_timeFormatSuffix; // $timeformat printf format - } m_serg VL_GUARDED_BY(m_sergMutex); - // Nothing below here is save-restored; users expected to re-register appropriately - VerilatedMutex m_argMutex; ///< Protect m_argVec, m_argVecLoaded - /// Argument list (NOT save-restored, may want different results) - ArgVec m_argVec VL_GUARDED_BY(m_argMutex); - bool m_argVecLoaded VL_GUARDED_BY(m_argMutex); ///< Ever loaded argument list - VerilatedMutex m_userMapMutex; ///< Protect m_userMap + // For userInsert, userFind. As indexed by pointer is common across contexts. UserMap m_userMap VL_GUARDED_BY(m_userMapMutex); ///< Map of <(scope,userkey), userData> - VerilatedMutex m_nameMutex; ///< Protect m_nameMap - /// Map of - VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex); - VerilatedMutex m_hierMapMutex; ///< Protect m_hierMap /// Map that represents scope hierarchy + // Used by hierarchyAdd, hierarchyRemove, hierarchyMap VerilatedHierarchyMap m_hierMap VL_GUARDED_BY(m_hierMapMutex); // Slow - somewhat static: VerilatedMutex m_exportMutex; ///< Protect m_nameMap /// Map of + // Used by exportInsert, exportFind, exportName. + // Export numbers same across all contexts as just a string-to-number conversion ExportNameMap m_exportMap VL_GUARDED_BY(m_exportMutex); - int m_exportNext VL_GUARDED_BY(m_exportMutex); ///< Next export funcnum - - // File I/O - VerilatedMutex m_fdMutex; ///< Protect m_fdps, m_fdFree - std::vector m_fdps VL_GUARDED_BY(m_fdMutex); ///< File descriptors - /// List of free descriptors (SLOW - FOPEN/CLOSE only) - std::vector m_fdFree VL_GUARDED_BY(m_fdMutex); - // List of free descriptors in the MCT region [4, 32) - std::vector m_fdFreeMct VL_GUARDED_BY(m_fdMutex); + int m_exportNext VL_GUARDED_BY(m_exportMutex) = 0; ///< Next export funcnum // CONSTRUCTORS - VerilatedImpData() - : m_argVecLoaded{false} - , m_exportNext{0} { - - m_fdps.resize(31); - std::fill(m_fdps.begin(), m_fdps.end(), (FILE*)0); - m_fdFreeMct.resize(30); - for (std::size_t i = 0, id = 1; i < m_fdFreeMct.size(); ++i, ++id) { m_fdFreeMct[i] = id; } - } + VerilatedImpData() = default; }; class VerilatedImp final { @@ -270,95 +431,62 @@ protected: friend class Verilated; // MEMBERS - union VerilatedImpU { ///< Enclose in an union to call ctor/dtor manually - VerilatedImpData v; - VerilatedImpU() {} // Can't be = default; - ~VerilatedImpU() {} // Can't be = default; - }; - static VerilatedImpU s_s; ///< Static Singleton; One and only static this + static VerilatedImpData& s() { // Singleton + static VerilatedImpData s_s; + return s_s; + } public: // But only for verilated*.cpp // CONSTRUCTORS VerilatedImp() = default; ~VerilatedImp() = default; - static void setup(); - static void teardown(); private: VL_UNCOPYABLE(VerilatedImp); public: // METHODS - debug - static void internalsDump() VL_MT_SAFE; static void versionDump() VL_MT_SAFE; - // METHODS - arguments -public: - static void commandArgs(int argc, const char** argv) VL_EXCLUDES(s_s.v.m_argMutex); - static void commandArgsAdd(int argc, const char** argv) VL_EXCLUDES(s_s.v.m_argMutex); - static std::string argPlusMatch(const char* prefixp) VL_EXCLUDES(s_s.v.m_argMutex) { - const VerilatedLockGuard lock(s_s.v.m_argMutex); - // Note prefixp does not include the leading "+" - size_t len = strlen(prefixp); - if (VL_UNLIKELY(!s_s.v.m_argVecLoaded)) { - s_s.v.m_argVecLoaded = true; // Complain only once - VL_FATAL_MT("unknown", 0, "", - "%Error: Verilog called $test$plusargs or $value$plusargs without" - " testbench C first calling Verilated::commandArgs(argc,argv)."); - } - for (const auto& i : s_s.v.m_argVec) { - if (i[0] == '+') { - if (0 == strncmp(prefixp, i.c_str() + 1, len)) return i; - } - } - return ""; - } - -private: - static void commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s_s.v.m_argMutex); - static void commandArgVl(const std::string& arg); - static bool commandArgVlValue(const std::string& arg, const std::string& prefix, - std::string& valuer); - public: // METHODS - user scope tracking - // We implement this as a single large map instead of one map per scope + // We implement this as a single large map instead of one map per scope. // There's often many more scopes than userdata's and thus having a ~48byte // per map overhead * N scopes would take much more space and cache thrashing. + // As scopep's are pointers, this implicitly handles multiple Context's static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.v.m_userMapMutex); - const auto it = s_s.v.m_userMap.find(std::make_pair(scopep, userKey)); - if (it != s_s.v.m_userMap.end()) { + const VerilatedLockGuard lock(s().m_userMapMutex); + const auto it = s().m_userMap.find(std::make_pair(scopep, userKey)); + if (it != s().m_userMap.end()) { it->second = userData; } else { - s_s.v.m_userMap.emplace(std::make_pair(scopep, userKey), userData); + s().m_userMap.emplace(std::make_pair(scopep, userKey), userData); } } static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.v.m_userMapMutex); - const auto& it = vlstd::as_const(s_s.v.m_userMap).find(std::make_pair(scopep, userKey)); - if (VL_UNLIKELY(it == s_s.v.m_userMap.end())) return nullptr; + const VerilatedLockGuard lock(s().m_userMapMutex); + const auto& it = vlstd::as_const(s().m_userMap).find(std::make_pair(scopep, userKey)); + if (VL_UNLIKELY(it == s().m_userMap.end())) return nullptr; return it->second; } -private: +public: // But only for verilated.cpp /// Symbol table destruction cleans up the entries for each scope. static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE { // Slow ok - called once/scope on destruction, so we simply iterate. - const VerilatedLockGuard lock(s_s.v.m_userMapMutex); - for (auto it = s_s.v.m_userMap.begin(); it != s_s.v.m_userMap.end();) { + const VerilatedLockGuard lock(s().m_userMapMutex); + for (auto it = s().m_userMap.begin(); it != s().m_userMap.end();) { if (it->first.first == scopep) { - s_s.v.m_userMap.erase(it++); + s().m_userMap.erase(it++); } else { ++it; } } } static void userDump() VL_MT_SAFE { - const VerilatedLockGuard lock( - s_s.v.m_userMapMutex); // Avoid it changing in middle of dump + const VerilatedLockGuard lock(s().m_userMapMutex); // Avoid it changing in middle of dump bool first = true; - for (const auto& i : s_s.v.m_userMap) { + for (const auto& i : s().m_userMap) { if (first) { VL_PRINTF_MT(" userDump:\n"); first = false; @@ -368,54 +496,18 @@ private: } } -public: // But only for verilated*.cpp - // METHODS - scope name - static void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE { - // Slow ok - called once/scope at construction - const VerilatedLockGuard lock(s_s.v.m_nameMutex); - const auto it = s_s.v.m_nameMap.find(scopep->name()); - if (it == s_s.v.m_nameMap.end()) s_s.v.m_nameMap.emplace(scopep->name(), scopep); - } - static inline const VerilatedScope* scopeFind(const char* namep) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.v.m_nameMutex); - // If too slow, can assume this is only VL_MT_SAFE_POSINIT - const auto& it = s_s.v.m_nameMap.find(namep); - if (VL_UNLIKELY(it == s_s.v.m_nameMap.end())) return nullptr; - return it->second; - } - static void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE { - // Slow ok - called once/scope at destruction - const VerilatedLockGuard lock(s_s.v.m_nameMutex); - userEraseScope(scopep); - const auto it = s_s.v.m_nameMap.find(scopep->name()); - if (it != s_s.v.m_nameMap.end()) s_s.v.m_nameMap.erase(it); - } - static void scopesDump() VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.v.m_nameMutex); - VL_PRINTF_MT(" scopesDump:\n"); - for (const auto& i : s_s.v.m_nameMap) { - const VerilatedScope* scopep = i.second; - scopep->scopeDump(); - } - VL_PRINTF_MT("\n"); - } - static const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE_POSTINIT { - // Thread save only assuming this is called only after model construction completed - return &s_s.v.m_nameMap; - } - public: // But only for verilated*.cpp // METHODS - hierarchy static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE { // Slow ok - called at construction for VPI accessible elements - const VerilatedLockGuard lock(s_s.v.m_hierMapMutex); - s_s.v.m_hierMap[fromp].push_back(top); + const VerilatedLockGuard lock(s().m_hierMapMutex); + s().m_hierMap[fromp].push_back(top); } static void hierarchyRemove(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE { // Slow ok - called at destruction for VPI accessible elements - const VerilatedLockGuard lock(s_s.v.m_hierMapMutex); - VerilatedHierarchyMap& map = s_s.v.m_hierMap; + const VerilatedLockGuard lock(s().m_hierMapMutex); + VerilatedHierarchyMap& map = s().m_hierMap; if (map.find(fromp) == map.end()) return; auto& scopes = map[fromp]; const auto it = find(scopes.begin(), scopes.end(), top); @@ -423,7 +515,7 @@ public: // But only for verilated*.cpp } static const VerilatedHierarchyMap* hierarchyMap() VL_MT_SAFE_POSTINIT { // Thread save only assuming this is called only after model construction completed - return &s_s.v.m_hierMap; + return &s().m_hierMap; } public: // But only for verilated*.cpp @@ -437,19 +529,19 @@ public: // But only for verilated*.cpp // miss at the cost of a multiply, and all lookups move to slowpath. static int exportInsert(const char* namep) VL_MT_SAFE { // Slow ok - called once/function at creation - const VerilatedLockGuard lock(s_s.v.m_exportMutex); - const auto it = s_s.v.m_exportMap.find(namep); - if (it == s_s.v.m_exportMap.end()) { - s_s.v.m_exportMap.emplace(namep, s_s.v.m_exportNext++); - return s_s.v.m_exportNext++; + const VerilatedLockGuard lock(s().m_exportMutex); + const auto it = s().m_exportMap.find(namep); + if (it == s().m_exportMap.end()) { + s().m_exportMap.emplace(namep, s().m_exportNext++); + return s().m_exportNext++; } else { return it->second; } } static int exportFind(const char* namep) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.v.m_exportMutex); - const auto& it = s_s.v.m_exportMap.find(namep); - if (VL_LIKELY(it != s_s.v.m_exportMap.end())) return it->second; + const VerilatedLockGuard lock(s().m_exportMutex); + const auto& it = s().m_exportMap.find(namep); + if (VL_LIKELY(it != s().m_exportMap.end())) return it->second; std::string msg = (std::string("%Error: Testbench C called ") + namep + " but no such DPI export function name exists in ANY model"); VL_FATAL_MT("unknown", 0, "", msg.c_str()); @@ -457,16 +549,16 @@ public: // But only for verilated*.cpp } static const char* exportName(int funcnum) VL_MT_SAFE { // Slowpath; find name for given export; errors only so no map to reverse-map it - const VerilatedLockGuard lock(s_s.v.m_exportMutex); - for (const auto& i : s_s.v.m_exportMap) { + const VerilatedLockGuard lock(s().m_exportMutex); + for (const auto& i : s().m_exportMap) { if (i.second == funcnum) return i.first; } return "*UNKNOWN*"; } static void exportsDump() VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.v.m_exportMutex); + const VerilatedLockGuard lock(s().m_exportMutex); bool first = true; - for (const auto& i : s_s.v.m_exportMap) { + for (const auto& i : s().m_exportMap) { if (first) { VL_PRINTF_MT(" exportDump:\n"); first = false; @@ -476,131 +568,6 @@ public: // But only for verilated*.cpp } // We don't free up m_exportMap until the end, because we can't be sure // what other models are using the assigned funcnum's. - -public: // But only for verilated*.cpp - // METHODS - timeformat - static std::string timeFormatSuffix() VL_MT_SAFE; - static void timeFormatSuffix(const std::string& value) VL_MT_SAFE; - static int timeFormatUnits() VL_MT_SAFE { - if (s_s.v.m_ser.m_timeFormatUnits == VerilatedImpData::Serialized::UNITS_NONE) { - return Verilated::timeprecision(); - } - return s_s.v.m_ser.m_timeFormatUnits; - } - static int timeFormatPrecision() VL_MT_SAFE { return s_s.v.m_ser.m_timeFormatPrecision; } - static int timeFormatWidth() VL_MT_SAFE { return s_s.v.m_ser.m_timeFormatWidth; } - static void timeFormatUnits(int value) VL_MT_SAFE; - static void timeFormatPrecision(int value) VL_MT_SAFE; - static void timeFormatWidth(int value) VL_MT_SAFE; - -public: // But only for verilated*.cpp - // METHODS - file IO - static IData fdNewMcd(const char* filenamep) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.v.m_fdMutex); - if (s_s.v.m_fdFreeMct.empty()) return 0; - IData idx = s_s.v.m_fdFreeMct.back(); - s_s.v.m_fdFreeMct.pop_back(); - s_s.v.m_fdps[idx] = fopen(filenamep, "w"); - if (VL_UNLIKELY(!s_s.v.m_fdps[idx])) return 0; - return (1 << idx); - } - static IData fdNew(const char* filenamep, const char* modep) VL_MT_SAFE { - FILE* fp = fopen(filenamep, modep); - if (VL_UNLIKELY(!fp)) return 0; - // Bit 31 indicates it's a descriptor not a MCD - const VerilatedLockGuard lock(s_s.v.m_fdMutex); - if (s_s.v.m_fdFree.empty()) { - // Need to create more space in m_fdps and m_fdFree - const std::size_t start = std::max(31UL + 1UL + 3UL, s_s.v.m_fdps.size()); - const std::size_t excess = 10; - s_s.v.m_fdps.resize(start + excess); - std::fill(s_s.v.m_fdps.begin() + start, s_s.v.m_fdps.end(), (FILE*)0); - s_s.v.m_fdFree.resize(excess); - for (std::size_t i = 0, id = start; i < s_s.v.m_fdFree.size(); ++i, ++id) { - s_s.v.m_fdFree[i] = id; - } - } - IData idx = s_s.v.m_fdFree.back(); - s_s.v.m_fdFree.pop_back(); - s_s.v.m_fdps[idx] = fp; - return (idx | (1UL << 31)); // bit 31 indicates not MCD - } - static void fdFlush(IData fdi) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.v.m_fdMutex); - const VerilatedFpList fdlist = fdToFpList(fdi); - for (const auto& i : fdlist) fflush(i); - } - static IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.v.m_fdMutex); - const VerilatedFpList fdlist = fdToFpList(fdi); - if (VL_UNLIKELY(fdlist.size() != 1)) return 0; - return static_cast( - fseek(*fdlist.begin(), static_cast(offset), static_cast(origin))); - } - static IData fdTell(IData fdi) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.v.m_fdMutex); - const VerilatedFpList fdlist = fdToFpList(fdi); - if (VL_UNLIKELY(fdlist.size() != 1)) return 0; - return static_cast(ftell(*fdlist.begin())); - } - static void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.v.m_fdMutex); - const VerilatedFpList fdlist = fdToFpList(fdi); - for (const auto& i : fdlist) { - if (VL_UNLIKELY(!i)) continue; - (void)fwrite(output.c_str(), 1, output.size(), i); - } - } - static void fdClose(IData fdi) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.v.m_fdMutex); - if ((fdi & (1 << 31)) != 0) { - // Non-MCD case - IData idx = VL_MASK_I(31) & fdi; - if (VL_UNLIKELY(idx >= s_s.v.m_fdps.size())) return; - if (VL_UNLIKELY(!s_s.v.m_fdps[idx])) return; // Already free - fclose(s_s.v.m_fdps[idx]); - s_s.v.m_fdps[idx] = (FILE*)0; - s_s.v.m_fdFree.push_back(idx); - } else { - // MCD case - for (int i = 0; (fdi != 0) && (i < 31); i++, fdi >>= 1) { - if (fdi & VL_MASK_I(1)) { - fclose(s_s.v.m_fdps[i]); - s_s.v.m_fdps[i] = nullptr; - s_s.v.m_fdFreeMct.push_back(i); - } - } - } - } - static inline FILE* fdToFp(IData fdi) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.v.m_fdMutex); - const VerilatedFpList fdlist = fdToFpList(fdi); - if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr; - return *fdlist.begin(); - } - -private: - static inline VerilatedFpList fdToFpList(IData fdi) VL_REQUIRES(s_s.v.m_fdMutex) { - VerilatedFpList fp; - if ((fdi & (1 << 31)) != 0) { - // Non-MCD case - const IData idx = fdi & VL_MASK_I(31); - switch (idx) { - case 0: fp.push_back(stdin); break; - case 1: fp.push_back(stdout); break; - case 2: fp.push_back(stderr); break; - default: - if (VL_LIKELY(idx < s_s.v.m_fdps.size())) fp.push_back(s_s.v.m_fdps[idx]); - break; - } - } else { - // MCD Case - for (size_t i = 0; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) { - if (fdi & VL_MASK_I(1)) fp.push_back(s_s.v.m_fdps[i]); - } - } - return fp; - } }; //====================================================================== diff --git a/include/verilated_intrinsics.h b/include/verilated_intrinsics.h index 4deea30e1..bba2a324c 100644 --- a/include/verilated_intrinsics.h +++ b/include/verilated_intrinsics.h @@ -19,8 +19,8 @@ /// //************************************************************************* -#ifndef _VERILATED_INTRINSICS_H_ -#define _VERILATED_INTRINSICS_H_ 1 ///< Header Guard +#ifndef VERILATOR_VERILATED_INTRINSICS_H_ +#define VERILATOR_VERILATED_INTRINSICS_H_ ///< Header Guard // clang-format off diff --git a/include/verilated_save.cpp b/include/verilated_save.cpp index 7743859af..ed71dc4b9 100644 --- a/include/verilated_save.cpp +++ b/include/verilated_save.cpp @@ -16,9 +16,12 @@ /// //============================================================================= +#define VERILATOR_VERILATED_SAVE_CPP_ + #include "verilatedos.h" #include "verilated.h" #include "verilated_save.h" +#include "verilated_imp.h" #include #include @@ -43,7 +46,7 @@ // CONSTANTS /// Value of first bytes of each file (must be multiple of 8 bytes) -static const char* const VLTSAVE_HEADER_STR = "verilatorsave01\n"; +static const char* const VLTSAVE_HEADER_STR = "verilatorsave02\n"; /// Value of last bytes of each file (must be multiple of 8 bytes) static const char* const VLTSAVE_TRAILER_STR = "vltsaved"; @@ -77,11 +80,6 @@ void VerilatedSerialize::header() VL_MT_UNSAFE_ONE { VerilatedSerialize& os = *this; // So can cut and paste standard << code below assert((strlen(VLTSAVE_HEADER_STR) & 7) == 0); // Keep aligned os.write(VLTSAVE_HEADER_STR, strlen(VLTSAVE_HEADER_STR)); - - // Verilated doesn't do it itself, as if we're not using save/restore - // it doesn't need to compile this stuff in - os.write(Verilated::serialized1Ptr(), Verilated::serialized1Size()); - os.write(Verilated::serialized2Ptr(), Verilated::serialized2Size()); } void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE { @@ -95,8 +93,6 @@ void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE { VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str()); // Die before we close() as close would infinite loop } - os.read(Verilated::serialized1Ptr(), Verilated::serialized1Size()); - os.read(Verilated::serialized2Ptr(), Verilated::serialized2Size()); } void VerilatedSerialize::trailer() VL_MT_UNSAFE_ONE { @@ -249,3 +245,19 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE { //============================================================================= // Serialization of types + +VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp) { + os.write(rhsp->serialized1Ptr(), rhsp->serialized1Size()); + os << rhsp->impp()->timeFormatSuffix(); + os << rhsp->dumpfile(); + return os; +} +VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhsp) { + os.read(rhsp->serialized1Ptr(), rhsp->serialized1Size()); + std::string s; + os >> s; + rhsp->impp()->timeFormatSuffix(s); + os >> s; + rhsp->dumpfile(s); + return os; +} diff --git a/include/verilated_save.h b/include/verilated_save.h index e39ca820e..b710308da 100644 --- a/include/verilated_save.h +++ b/include/verilated_save.h @@ -16,8 +16,8 @@ /// //============================================================================= -#ifndef _VERILATED_SAVE_C_H_ -#define _VERILATED_SAVE_C_H_ 1 +#ifndef VERILATOR_VERILATED_SAVE_C_H_ +#define VERILATOR_VERILATED_SAVE_C_H_ #include "verilatedos.h" #include "verilated_heavy.h" @@ -61,7 +61,7 @@ public: std::string filename() const { return m_filename; } virtual void close() VL_MT_UNSAFE_ONE { flush(); } virtual void flush() VL_MT_UNSAFE_ONE {} - inline VerilatedSerialize& write(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE { + VerilatedSerialize& write(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE { const vluint8_t* __restrict dp = (const vluint8_t* __restrict)datap; while (size) { bufferCheck(); @@ -122,7 +122,7 @@ public: std::string filename() const { return m_filename; } virtual void close() VL_MT_UNSAFE_ONE { flush(); } virtual void flush() VL_MT_UNSAFE_ONE {} - inline VerilatedDeserialize& read(void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE { + VerilatedDeserialize& read(void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE { vluint8_t* __restrict dp = static_cast(datap); while (size) { bufferCheck(); @@ -236,7 +236,7 @@ inline VerilatedSerialize& operator<<(VerilatedSerialize& os, float& rhs) { inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, float& rhs) { return os.read(&rhs, sizeof(rhs)); } -inline VerilatedSerialize& operator<<(VerilatedSerialize& os, std::string& rhs) { +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const std::string& rhs) { vluint32_t len = rhs.length(); os << len; return os.write(rhs.data(), len); @@ -247,6 +247,9 @@ inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& r rhs.resize(len); return os.read((void*)rhs.data(), len); } +VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp); +VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhsp); + template VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray& rhs) { os << rhs.atDefault(); diff --git a/include/verilated_sc.h b/include/verilated_sc.h index 31884ba53..ed88081f3 100644 --- a/include/verilated_sc.h +++ b/include/verilated_sc.h @@ -19,8 +19,8 @@ /// //************************************************************************* -#ifndef _VERILATED_SC_H_ -#define _VERILATED_SC_H_ 1 ///< Header Guard +#ifndef VERILATOR_VERILATED_SC_H_ +#define VERILATOR_VERILATED_SC_H_ ///< Header Guard #include "verilatedos.h" diff --git a/include/verilated_sym_props.h b/include/verilated_sym_props.h index 41d0b3193..feb3ea94e 100644 --- a/include/verilated_sym_props.h +++ b/include/verilated_sym_props.h @@ -24,8 +24,8 @@ /// //************************************************************************* -#ifndef _VERILATED_SYM_PROPS_H_ -#define _VERILATED_SYM_PROPS_H_ 1 ///< Header Guard +#ifndef VERILATOR_VERILATED_SYM_PROPS_H_ +#define VERILATOR_VERILATED_SYM_PROPS_H_ ///< Header Guard #include "verilatedos.h" @@ -70,7 +70,7 @@ public: class VerilatedVarProps VL_NOT_FINAL { // TYPES - enum { MAGIC = 0xddc4f829 }; + static constexpr vluint32_t MAGIC = 0xddc4f829UL; // MEMBERS const vluint32_t m_magic; // Magic number const VerilatedVarType m_vltype; // Data type diff --git a/include/verilated_syms.h b/include/verilated_syms.h index 191ad5014..72f294c45 100644 --- a/include/verilated_syms.h +++ b/include/verilated_syms.h @@ -23,8 +23,8 @@ /// //************************************************************************* -#ifndef _VERILATED_SYMS_H_ -#define _VERILATED_SYMS_H_ 1 ///< Header Guard +#ifndef VERILATOR_VERILATED_SYMS_H_ +#define VERILATOR_VERILATED_SYMS_H_ ///< Header Guard #include "verilatedos.h" #include "verilated_heavy.h" diff --git a/include/verilated_threads.cpp b/include/verilated_threads.cpp index 54f029d11..6eb241fac 100644 --- a/include/verilated_threads.cpp +++ b/include/verilated_threads.cpp @@ -19,6 +19,11 @@ #include +//============================================================================= +// Globals + +// Internal note: Globals may multi-construct, see verilated.cpp top. + std::atomic VlMTaskVertex::s_yields; VL_THREAD_LOCAL VlThreadPool::ProfileTrace* VlThreadPool::t_profilep = nullptr; @@ -35,12 +40,12 @@ VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount) //============================================================================= // VlWorkerThread -VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, bool profiling) - : m_waiting{false} - , m_poolp{poolp} +VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling) + : m_poolp{poolp} , m_profiling{profiling} // Must init this last -- after setting up fields that it might read: , m_exiting{false} - , m_cthread{startWorker, this} {} + , m_cthread{startWorker, this} + , m_contextp{contextp} {} VlWorkerThread::~VlWorkerThread() { m_exiting.store(true, std::memory_order_release); @@ -70,12 +75,15 @@ void VlWorkerThread::workerLoop() { if (VL_UNLIKELY(m_profiling)) m_poolp->tearDownProfilingClientThread(); } -void VlWorkerThread::startWorker(VlWorkerThread* workerp) { workerp->workerLoop(); } +void VlWorkerThread::startWorker(VlWorkerThread* workerp) { + Verilated::threadContextp(workerp->m_contextp); + workerp->workerLoop(); +} //============================================================================= // VlThreadPool -VlThreadPool::VlThreadPool(int nThreads, bool profiling) +VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profiling) : m_profiling{profiling} { // --threads N passes nThreads=N-1, as the "main" threads counts as 1 unsigned cpus = std::thread::hardware_concurrency(); @@ -89,7 +97,7 @@ VlThreadPool::VlThreadPool(int nThreads, bool profiling) } // Create'em for (int i = 0; i < nThreads; ++i) { - m_workers.push_back(new VlWorkerThread(this, profiling)); + m_workers.push_back(new VlWorkerThread(this, contextp, profiling)); } // Set up a profile buffer for the current thread too -- on the // assumption that it's the same thread that calls eval and may be @@ -109,7 +117,7 @@ void VlThreadPool::tearDownProfilingClientThread() { t_profilep = nullptr; } -void VlThreadPool::setupProfilingClientThread() { +void VlThreadPool::setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex) { assert(!t_profilep); t_profilep = new ProfileTrace; // Reserve some space in the thread-local profiling buffer; @@ -121,7 +129,7 @@ void VlThreadPool::setupProfilingClientThread() { } } -void VlThreadPool::profileAppendAll(const VlProfileRec& rec) { +void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex) { const VerilatedLockGuard lk(m_mutex); for (const auto& profilep : m_allProfiles) { // Every thread's profile trace gets a copy of rec. @@ -129,7 +137,8 @@ void VlThreadPool::profileAppendAll(const VlProfileRec& rec) { } } -void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed) { +void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed) + VL_MT_SAFE_EXCLUDES(m_mutex) { const VerilatedLockGuard lk(m_mutex); VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep);); @@ -145,8 +154,9 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed) { fprintf(fp, "VLPROFTHREAD 1.0 # Verilator thread profile dump version 1.0\n"); fprintf(fp, "VLPROF arg --threads %" VL_PRI64 "u\n", vluint64_t(m_workers.size() + 1)); fprintf(fp, "VLPROF arg +verilator+prof+threads+start+%" VL_PRI64 "u\n", - Verilated::profThreadsStart()); - fprintf(fp, "VLPROF arg +verilator+prof+threads+window+%u\n", Verilated::profThreadsWindow()); + Verilated::threadContextp()->profThreadsStart()); + fprintf(fp, "VLPROF arg +verilator+prof+threads+window+%u\n", + Verilated::threadContextp()->profThreadsWindow()); fprintf(fp, "VLPROF stat yields %" VL_PRI64 "u\n", VlMTaskVertex::yields()); vluint32_t thread_id = 0; diff --git a/include/verilated_threads.h b/include/verilated_threads.h index b502d9971..ded328b5b 100644 --- a/include/verilated_threads.h +++ b/include/verilated_threads.h @@ -16,8 +16,8 @@ /// //============================================================================= -#ifndef _VERILATED_THREADS_H_ -#define _VERILATED_THREADS_H_ +#ifndef VERILATOR_VERILATED_THREADS_H_ +#define VERILATOR_VERILATED_THREADS_H_ #include "verilatedos.h" #include "verilated.h" // for VerilatedMutex and clang annotations @@ -189,7 +189,7 @@ private: VerilatedMutex m_mutex; std::condition_variable_any m_cv; // Only notify the condition_variable if the worker is waiting - bool m_waiting VL_GUARDED_BY(m_mutex); + bool m_waiting VL_GUARDED_BY(m_mutex) = false; // Why a vector? We expect the pending list to be very short, typically // 0 or 1 or 2, so popping from the front shouldn't be @@ -203,16 +203,17 @@ private: bool m_profiling; // Is profiling enabled? std::atomic m_exiting; // Worker thread should exit std::thread m_cthread; // Underlying C++ thread record + VerilatedContext* m_contextp; // Context for spawned thread VL_UNCOPYABLE(VlWorkerThread); public: // CONSTRUCTORS - explicit VlWorkerThread(VlThreadPool* poolp, bool profiling); + explicit VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling); ~VlWorkerThread(); // METHODS - inline void dequeWork(ExecRec* workp) { + inline void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) { // Spin for a while, waiting for new data for (int i = 0; i < VL_LOCK_SPINS; ++i) { if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) { // @@ -233,7 +234,8 @@ public: m_ready_size.fetch_sub(1, std::memory_order_relaxed); } inline void wakeUp() { addTask(nullptr, false, nullptr); } - inline void addTask(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym) { + inline void addTask(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym) + VL_MT_SAFE_EXCLUDES(m_mutex) { bool notify; { const VerilatedLockGuard lk(m_mutex); @@ -262,6 +264,7 @@ class VlThreadPool final { // corrupting the profiling data. It's super cheap to append // a VlProfileRec struct on the end of a pre-allocated vector; // this is the only cost we pay in real-time during a profiling cycle. + // Internal note: Globals may multi-construct, see verilated.cpp top. static VL_THREAD_LOCAL ProfileTrace* t_profilep; ProfileSet m_allProfiles VL_GUARDED_BY(m_mutex); VerilatedMutex m_mutex; @@ -271,7 +274,7 @@ public: // Construct a thread pool with 'nThreads' dedicated threads. The thread // pool will create these threads and make them available to execute tasks // via this->workerp(index)->addTask(...) - VlThreadPool(int nThreads, bool profiling); + VlThreadPool(VerilatedContext* contextp, int nThreads, bool profiling); ~VlThreadPool(); // METHODS @@ -285,11 +288,11 @@ public: t_profilep->emplace_back(); return &(t_profilep->back()); } - void profileAppendAll(const VlProfileRec& rec); - void profileDump(const char* filenamep, vluint64_t ticksElapsed); + void profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex); + void profileDump(const char* filenamep, vluint64_t ticksElapsed) VL_MT_SAFE_EXCLUDES(m_mutex); // In profiling mode, each executing thread must call // this once to setup profiling state: - void setupProfilingClientThread(); + void setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex); void tearDownProfilingClientThread(); private: diff --git a/include/verilated_trace.h b/include/verilated_trace.h index feb261606..d5f9a5cb6 100644 --- a/include/verilated_trace.h +++ b/include/verilated_trace.h @@ -12,13 +12,12 @@ //============================================================================= /// /// \file -/// \brief Tracing functionality common to all formats +/// \brief Internal tracing functionality common to all formats /// //============================================================================= -// SPDIFF_OFF -#ifndef _VERILATED_TRACE_H_ -#define _VERILATED_TRACE_H_ 1 +#ifndef VERILATOR_VERILATED_TRACE_H_ +#define VERILATOR_VERILATED_TRACE_H_ // clang-format off @@ -48,21 +47,21 @@ private: public: // Put an element at the back of the queue - void put(T value) { + void put(T value) VL_MT_SAFE_EXCLUDES(m_mutex) { VerilatedLockGuard lock(m_mutex); m_queue.push_back(value); m_cv.notify_one(); } // Put an element at the front of the queue - void put_front(T value) { + void put_front(T value) VL_MT_SAFE_EXCLUDES(m_mutex) { VerilatedLockGuard lock(m_mutex); m_queue.push_front(value); m_cv.notify_one(); } // Get an element from the front of the queue. Blocks if none available - T get() { + 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(); }); assert(!m_queue.empty()); @@ -72,7 +71,7 @@ public: } // Non blocking get - bool tryGet(T& result) { + bool tryGet(T& result) VL_MT_SAFE_EXCLUDES(m_mutex) { const VerilatedLockGuard lockGuard(m_mutex); if (m_queue.empty()) return false; result = m_queue.front(); @@ -152,7 +151,8 @@ private: double m_timeRes; ///< Time resolution (ns/ms etc) double m_timeUnit; ///< Time units (ns/ms etc) - void addCallbackRecord(std::vector& cbVec, CallbackRecord& cbRec); + void addCallbackRecord(std::vector& cbVec, CallbackRecord& cbRec) + VL_MT_SAFE_EXCLUDES(m_mutex); // Equivalent to 'this' but is of the sub-type 'T_Derived*'. Use 'self()->' // to access duck-typed functions to avoid a virtual function call. @@ -204,7 +204,7 @@ protected: //========================================================================= // Internals available to format specific implementations - VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread + VerilatedMutex m_mutex; // Ensure dump() etc only called from single thread vluint32_t nextCode() const { return m_nextCode; } vluint32_t numSignals() const { return m_numSignals; } @@ -221,13 +221,13 @@ protected: void declCode(vluint32_t code, vluint32_t bits, bool tri); - /// Is this an escape? + // Is this an escape? bool isScopeEscape(char c) { return c != '\f' && (isspace(c) || c == m_scopeEscape); } - /// Character that splits scopes. Note whitespace are ALWAYS escapes. + // Character that splits scopes. Note whitespace are ALWAYS escapes. char scopeEscape() { return m_scopeEscape; } - void close(); - void flush(); + void closeBase(); + void flushBase(); //========================================================================= // Virtual functions to be provided by the format specific implementation @@ -248,30 +248,24 @@ public: ~VerilatedTrace(); // Set time units (s/ms, defaults to ns) - void set_time_unit(const char* unitp); - void set_time_unit(const std::string& unit); + void set_time_unit(const char* unitp) VL_MT_SAFE; + void set_time_unit(const std::string& unit) VL_MT_SAFE; // Set time resolution (s/ms, defaults to ns) - void set_time_resolution(const char* unitp); - void set_time_resolution(const std::string& unit); + void set_time_resolution(const char* unitp) VL_MT_SAFE; + void set_time_resolution(const std::string& unit) VL_MT_SAFE; // Call - void dump(vluint64_t timeui); + void dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex); //========================================================================= // Non-hot path internal interface to Verilator generated code - void addInitCb(initCb_t cb, void* userp) VL_MT_UNSAFE_ONE; - void addFullCb(dumpCb_t cb, void* userp) VL_MT_UNSAFE_ONE; - void addChgCb(dumpCb_t cb, void* userp) VL_MT_UNSAFE_ONE; - void addCleanupCb(dumpCb_t cb, void* userp) VL_MT_UNSAFE_ONE; - - void changeThread() { m_assertOne.changeThread(); } - - void module(const std::string& name) VL_MT_UNSAFE_ONE { - m_assertOne.check(); - m_moduleName = name; - } + void addInitCb(initCb_t cb, void* userp) VL_MT_SAFE; + void addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE; + void addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE; + void addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE; + void module(const std::string& name) VL_MT_UNSAFE; void scopeEscape(char flag) { m_scopeEscape = flag; } //========================================================================= diff --git a/include/verilated_trace_imp.cpp b/include/verilated_trace_imp.cpp index fe4580794..5ed1f3d88 100644 --- a/include/verilated_trace_imp.cpp +++ b/include/verilated_trace_imp.cpp @@ -15,7 +15,6 @@ /// \brief Implementation of tracing functionality common to all trace formats /// //============================================================================= -// SPDIFF_OFF // clang-format off @@ -70,7 +69,7 @@ static std::string doubleToTimescale(double value) { else if (value >= 1e-18) { suffixp = "as"; value *= 1e18; } // clang-format on char valuestr[100]; - sprintf(valuestr, "%0.0f%s", value, suffixp); + VL_SNPRINTF(valuestr, 100, "%0.0f%s", value, suffixp); return valuestr; // Gets converted to string, so no ref to stack } @@ -237,7 +236,7 @@ template <> void VerilatedTrace::shutdownWorker() { //============================================================================= // Life cycle -template <> void VerilatedTrace::close() { +template <> void VerilatedTrace::closeBase() { #ifdef VL_TRACE_THREADED shutdownWorker(); while (m_numTraceBuffers) { @@ -247,7 +246,7 @@ template <> void VerilatedTrace::close() { #endif } -template <> void VerilatedTrace::flush() { +template <> void VerilatedTrace::flushBase() { #ifdef VL_TRACE_THREADED // Hand an empty buffer to the worker thread vluint32_t* const bufferp = getTraceBuffer(); @@ -263,12 +262,12 @@ template <> void VerilatedTrace::flush() { // Callbacks to run on global events template <> void VerilatedTrace::onFlush(void* selfp) { - // Note this calls 'flush' on the derived class + // This calls 'flush' on the derived classo (which must then get any mutex) reinterpret_cast(selfp)->flush(); } template <> void VerilatedTrace::onExit(void* selfp) { - // Note this calls 'close' on the derived class + // This calls 'close' on the derived class (which must then get any mutex) reinterpret_cast(selfp)->close(); } @@ -292,8 +291,8 @@ VerilatedTrace::VerilatedTrace() , m_numTraceBuffers { 0 } #endif { - set_time_unit(Verilated::timeunitString()); - set_time_resolution(Verilated::timeprecisionString()); + set_time_unit(Verilated::threadContextp()->timeunitString()); + set_time_resolution(Verilated::threadContextp()->timeprecisionString()); } template <> VerilatedTrace::~VerilatedTrace() { @@ -301,7 +300,7 @@ template <> VerilatedTrace::~VerilatedTrace() { Verilated::removeFlushCb(VerilatedTrace::onFlush, this); Verilated::removeExitCb(VerilatedTrace::onExit, this); #ifdef VL_TRACE_THREADED - close(); + closeBase(); #endif } @@ -309,8 +308,6 @@ template <> VerilatedTrace::~VerilatedTrace() { // Internals available to format specific implementations template <> void VerilatedTrace::traceInit() VL_MT_UNSAFE { - m_assertOne.check(); - // Note: It is possible to re-open a trace file (VCD in particular), // so we must reset the next code here, but it must have the same number // of codes on re-open @@ -355,7 +352,7 @@ template <> void VerilatedTrace::traceInit() VL_MT_UNSAFE { template <> void VerilatedTrace::declCode(vluint32_t code, vluint32_t bits, bool tri) { - if (!code) { + if (VL_UNCOVERABLE(!code)) { VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: internal trace problem, code 0 is illegal"); } // Note: The tri-state flag is not used by Verilator, but is here for @@ -377,24 +374,26 @@ template <> std::string VerilatedTrace::timeResStr() const { //========================================================================= // External interface to client code -template <> void VerilatedTrace::set_time_unit(const char* unitp) { +template <> void VerilatedTrace::set_time_unit(const char* unitp) VL_MT_SAFE { m_timeUnit = timescaleToDouble(unitp); } - -template <> void VerilatedTrace::set_time_unit(const std::string& unit) { +template <> void VerilatedTrace::set_time_unit(const std::string& unit) VL_MT_SAFE { set_time_unit(unit.c_str()); } - -template <> void VerilatedTrace::set_time_resolution(const char* unitp) { +template <> void VerilatedTrace::set_time_resolution(const char* unitp) VL_MT_SAFE { m_timeRes = timescaleToDouble(unitp); } - -template <> void VerilatedTrace::set_time_resolution(const std::string& unit) { +template <> +void VerilatedTrace::set_time_resolution(const std::string& unit) VL_MT_SAFE { set_time_resolution(unit.c_str()); } -template <> void VerilatedTrace::dump(vluint64_t timeui) { - m_assertOne.check(); +template <> +void VerilatedTrace::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex) { + // Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE. + // This does get the mutex, but if multiple threads are trying to dump + // chances are the data being dumped will have other problems + const VerilatedLockGuard lock(m_mutex); if (VL_UNCOVERABLE(m_timeLastDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START VL_PRINTF_MT("%%Warning: previous dump at t=%" VL_PRI64 "u, requesting t=%" VL_PRI64 "u, dump call ignored\n", @@ -427,7 +426,7 @@ template <> void VerilatedTrace::dump(vluint64_t timeui) { m_traceBufferWritep += 3; } else { // Update time point - flush(); + flushBase(); emitTimeChange(timeui); } #else @@ -473,8 +472,9 @@ template <> void VerilatedTrace::dump(vluint64_t timeui) { template <> void VerilatedTrace::addCallbackRecord(std::vector& cbVec, - CallbackRecord& cbRec) { - m_assertOne.check(); + CallbackRecord& cbRec) + VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock(m_mutex); if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START std::string msg = (std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__ + " called with already open file"); @@ -483,22 +483,26 @@ void VerilatedTrace::addCallbackRecord(std::vector cbVec.push_back(cbRec); } -template <> void VerilatedTrace::addInitCb(initCb_t cb, void* userp) { +template <> void VerilatedTrace::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE { CallbackRecord cbr(cb, userp); addCallbackRecord(m_initCbs, cbr); } -template <> void VerilatedTrace::addFullCb(dumpCb_t cb, void* userp) { +template <> void VerilatedTrace::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE { CallbackRecord cbr(cb, userp); addCallbackRecord(m_fullCbs, cbr); } -template <> void VerilatedTrace::addChgCb(dumpCb_t cb, void* userp) { +template <> void VerilatedTrace::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE { CallbackRecord cbr(cb, userp); addCallbackRecord(m_chgCbs, cbr); } -template <> void VerilatedTrace::addCleanupCb(dumpCb_t cb, void* userp) { +template <> void VerilatedTrace::addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE { CallbackRecord cbr(cb, userp); addCallbackRecord(m_cleanupCbs, cbr); } +template <> void VerilatedTrace::module(const std::string& name) VL_MT_UNSAFE { + // Called via callbacks way above in call stack, which already hold m_mutex + m_moduleName = name; +} //========================================================================= // Hot path internal interface to Verilator generated code @@ -651,6 +655,8 @@ void verilated_trace_imp_selftest() { #define SELF_CHECK_TS(scale) \ SELF_CHECK(doubleToTimescale(timescaleToDouble(scale)), std::string{scale}); + SELF_CHECK_TS("100s"); + SELF_CHECK_TS("10s"); SELF_CHECK_TS("1s"); SELF_CHECK_TS("100ms"); SELF_CHECK_TS("10ms"); diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index c59468167..7fc6bd4a3 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -15,7 +15,6 @@ /// \brief C++ Tracing in VCD Format /// //============================================================================= -// SPDIFF_OFF // clang-format off @@ -34,8 +33,6 @@ # include #endif -// SPDIFF_ON - #ifndef O_LARGEFILE // For example on WIN32 # define O_LARGEFILE 0 #endif @@ -100,14 +97,14 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep) { m_suffixesp = nullptr; } -void VerilatedVcd::open(const char* filename) { - m_assertOne.check(); +void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock(m_mutex); if (isOpen()) return; // Set member variables m_filename = filename; // "" is ok, as someone may overload open - openNext(m_rolloverMB != 0); + openNextImp(m_rolloverMB != 0); if (!isOpen()) return; dumpHeader(); @@ -116,13 +113,17 @@ void VerilatedVcd::open(const char* filename) { m_suffixesp = &m_suffixes[0]; // Note: C++11 m_suffixes.data(); // When using rollover, the first chunk contains the header only. - if (m_rolloverMB) openNext(true); + if (m_rolloverMB) openNextImp(true); } -void VerilatedVcd::openNext(bool incFilename) { +void VerilatedVcd::openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex) { // Open next filename in concat sequence, mangle filename if // incFilename is true. - m_assertOne.check(); + const VerilatedLockGuard lock(m_mutex); + openNextImp(incFilename); +} + +void VerilatedVcd::openNextImp(bool incFilename) { closePrev(); // Close existing if (incFilename) { // Find _0000.{ext} in filename @@ -166,7 +167,7 @@ void VerilatedVcd::openNext(bool incFilename) { } bool VerilatedVcd::preChangeDump() { - if (VL_UNLIKELY(m_rolloverMB && m_wroteBytes > m_rolloverMB)) openNext(true); + if (VL_UNLIKELY(m_rolloverMB && m_wroteBytes > m_rolloverMB)) openNextImp(true); return isOpen(); } @@ -222,7 +223,7 @@ void VerilatedVcd::closePrev() { // This function is on the flush() call path if (!isOpen()) return; - VerilatedTrace::flush(); + VerilatedTrace::flushBase(); bufferFlush(); m_isOpen = false; m_filep->close(); @@ -239,9 +240,9 @@ void VerilatedVcd::closeErr() { m_filep->close(); // May get error, just ignore it } -void VerilatedVcd::close() { +void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) { // This function is on the flush() call path - m_assertOne.check(); + const VerilatedLockGuard lock(m_mutex); if (!isOpen()) return; if (m_evcd) { printStr("$vcdclose "); @@ -251,11 +252,12 @@ void VerilatedVcd::close() { closePrev(); // closePrev() called VerilatedTrace::flush(), so we just // need to shut down the tracing thread here. - VerilatedTrace::close(); + VerilatedTrace::closeBase(); } -void VerilatedVcd::flush() { - VerilatedTrace::flush(); +void VerilatedVcd::flush() VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock(m_mutex); + VerilatedTrace::flushBase(); bufferFlush(); } @@ -269,7 +271,7 @@ void VerilatedVcd::printStr(const char* str) { void VerilatedVcd::printQuad(vluint64_t n) { char buf[100]; - sprintf(buf, "%" VL_PRI64 "u", n); + VL_SNPRINTF(buf, 100, "%" VL_PRI64 "u", n); printStr(buf); } @@ -293,7 +295,6 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE { // We add output data to m_writep. // When it gets nearly full we dump it using this routine which calls write() // This is much faster than using buffered I/O - m_assertOne.check(); if (VL_UNLIKELY(!isOpen())) return; char* wp = m_wrBufp; while (true) { @@ -347,9 +348,16 @@ void VerilatedVcd::printIndent(int level_change) { void VerilatedVcd::dumpHeader() { printStr("$version Generated by VerilatedVcd $end\n"); - time_t time_str = time(nullptr); printStr("$date "); - printStr(ctime(&time_str)); + { + time_t tick = time(nullptr); + tm ticktm; + VL_LOCALTIME_R(&tick, &ticktm); + constexpr int bufsize = 50; + char buf[bufsize]; + strftime(buf, bufsize, "%c", &ticktm); + printStr(buf); + } printStr(" $end\n"); printStr("$timescale "); @@ -487,11 +495,12 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep, decl += wirep; // usually "wire" } - char buf[1000]; - sprintf(buf, " %2d ", bits); + constexpr size_t bufsize = 1000; + char buf[bufsize]; + VL_SNPRINTF(buf, bufsize, " %2d ", bits); decl += buf; if (m_evcd) { - sprintf(buf, "<%u", code); + VL_SNPRINTF(buf, bufsize, "<%u", code); decl += buf; } else { // Add string code to decl @@ -505,7 +514,8 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep, // 1 bit values don't have a ' ' separator between value and string code const bool isBit = bits == 1; entryp[0] = ' '; // Separator - std::strcpy(entryp + !isBit, buf); // Code (overwrite separator if isBit) + // Use memcpy as we checked size above, and strcpy is flagged unsafe + std::memcpy(entryp + !isBit, buf, strlen(buf)); // Code (overwrite separator if isBit) entryp[length + !isBit] = '\n'; // Replace '\0' with line termination '\n' // Set length of suffix (used to increment write pointer) entryp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = !isBit + length + 1; @@ -513,12 +523,12 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep, decl += " "; decl += basename; if (array) { - sprintf(buf, "(%d)", arraynum); + VL_SNPRINTF(buf, bufsize, "(%d)", arraynum); decl += buf; hiername += buf; } if (bussed) { - sprintf(buf, " [%d:%d]", msb, lsb); + VL_SNPRINTF(buf, bufsize, " [%d:%d]", msb, lsb); decl += buf; } decl += " $end\n"; @@ -656,8 +666,8 @@ void VerilatedVcd::emitWData(vluint32_t code, const WData* newvalp, int bits) { VL_ATTR_ALWINLINE void VerilatedVcd::emitDouble(vluint32_t code, double newval) { char* wp = m_writep; - // Buffer can't overflow before sprintf; we sized during declaration - sprintf(wp, "r%.16g", newval); + // Buffer can't overflow before VL_SNPRINTF; we sized during declaration + VL_SNPRINTF(wp, m_wrChunkSize, "r%.16g", newval); wp += strlen(wp); finishLine(code, wp); } @@ -770,8 +780,8 @@ void VerilatedVcd::fullTriArray(vluint32_t code, const vluint32_t* newvalp, void VerilatedVcd::fullDouble(vluint32_t code, const double newval) { // cppcheck-suppress invalidPointerCast (*(reinterpret_cast(oldp(code)))) = newval; - // Buffer can't overflow before sprintf; we sized during declaration - sprintf(m_writep, "r%.16g", newval); + // Buffer can't overflow before VL_SNPRINTF; we sized during declaration + VL_SNPRINTF(m_writep, m_wrChunkSize, "r%.16g", newval); m_writep += strlen(m_writep); *m_writep++ = ' '; m_writep = writeCode(m_writep, code); @@ -869,6 +879,10 @@ void vcdTestMain(const char* filenamep) { { VerilatedVcdC* vcdp = new VerilatedVcdC; vcdp->evcd(true); + vcdp->set_time_unit("1ms"); + vcdp->set_time_unit(std::string("1ms")); + vcdp->set_time_resolution("1ns"); + vcdp->set_time_resolution(std::string("1ns")); vcdp->spTrace()->addInitCb(&vcdInit, 0); vcdp->spTrace()->addFullCb(&vcdFull, 0); vcdp->spTrace()->addChgCb(&vcdChange, 0); diff --git a/include/verilated_vcd_c.h b/include/verilated_vcd_c.h index ec42fae9b..37adbdd50 100644 --- a/include/verilated_vcd_c.h +++ b/include/verilated_vcd_c.h @@ -17,8 +17,8 @@ //============================================================================= // SPDIFF_OFF -#ifndef _VERILATED_VCD_C_H_ -#define _VERILATED_VCD_C_H_ 1 +#ifndef VERILATOR_VERILATED_VCD_C_H_ +#define VERILATOR_VERILATED_VCD_C_H_ #include "verilated.h" #include "verilated_trace.h" @@ -48,8 +48,8 @@ public: //============================================================================= // VerilatedVcd -/// Base class to create a Verilator VCD dump -/// This is an internally used class - see VerilatedVcdC for what to call from applications +// Base class to create a Verilator VCD dump +// This is an internally used class - see VerilatedVcdC for what to call from applications class VerilatedVcd VL_NOT_FINAL : public VerilatedTrace { private: @@ -86,9 +86,9 @@ private: // We only call this once per vector, so we need enough slop for a very wide "b###" line if (VL_UNLIKELY(m_writep > m_wrFlushp)) bufferFlush(); } + void openNextImp(bool incFilename); void closePrev(); void closeErr(); - void openNext(); void makeNameMap(); void deleteNameMap(); void printIndent(int level_change); @@ -136,20 +136,20 @@ public: ~VerilatedVcd(); // ACCESSORS - /// Set size in megabytes after which new file should be created + // Set size in megabytes after which new file should be created void rolloverMB(vluint64_t rolloverMB) { m_rolloverMB = rolloverMB; } // METHODS - /// Open the file; call isOpen() to see if errors - void open(const char* filename) VL_MT_UNSAFE_ONE; - /// Open next data-only file - void openNext(bool incFilename) VL_MT_UNSAFE_ONE; - /// Close the file - void close() VL_MT_UNSAFE_ONE; - /// Flush any remaining data to this file - void flush() VL_MT_UNSAFE_ONE; - /// Is file open? - bool isOpen() const { return m_isOpen; } + // Open the file; call isOpen() to see if errors + void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex); + // Open next data-only file + void openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex); + // Close the file + void close() VL_MT_SAFE_EXCLUDES(m_mutex); + // Flush any remaining data to this file + void flush() VL_MT_SAFE_EXCLUDES(m_mutex); + // Return if file is open + bool isOpen() const VL_MT_SAFE { return m_isOpen; } //========================================================================= // Internal interface to Verilator generated code @@ -211,8 +211,8 @@ public: chgDouble(oldp - this->oldp(0), newval); } - /// Inside dumping routines, dump one signal, faster when not inlined - /// due to code size reduction. + // Inside dumping routines, dump one signal, faster when not inlined + // due to code size reduction. void fullBit(vluint32_t code, const vluint32_t newval); void fullBus(vluint32_t code, const vluint32_t newval, int bits); void fullQuad(vluint32_t code, const vluint64_t newval, int bits); @@ -225,8 +225,8 @@ public: int bits); void fullDouble(vluint32_t code, const double newval); - /// Inside dumping routines, dump one signal if it has changed. - /// We do want to inline these to avoid calls when the value did not change. + // Inside dumping routines, dump one signal if it has changed. + // We do want to inline these to avoid calls when the value did not change. inline void chgBit(vluint32_t code, const vluint32_t newval) { vluint32_t diff = oldp(code)[0] ^ newval; if (VL_UNLIKELY(diff)) fullBit(code, newval); @@ -327,7 +327,6 @@ template <> void VerilatedTrace::set_time_resolution(const std::st // VerilatedVcdC /// Create a VCD dump file in C standalone (no SystemC) simulations. /// Also derived for use in SystemC simulations. -/// Thread safety: Unless otherwise indicated, every function is VL_MT_UNSAFE_ONE class VerilatedVcdC VL_NOT_FINAL { VerilatedVcd m_sptrace; ///< Trace file being created @@ -336,48 +335,56 @@ class VerilatedVcdC VL_NOT_FINAL { VL_UNCOPYABLE(VerilatedVcdC); public: + /// Construct the dump. Optional argument is a preconstructed file. explicit VerilatedVcdC(VerilatedVcdFile* filep = nullptr) : m_sptrace{filep} {} + /// Destruct, flush, and close the dump ~VerilatedVcdC() { close(); } - /// Routines can only be called from one thread; allow next call from different thread - void changeThread() { spTrace()->changeThread(); } public: - // ACCESSORS - /// Is file open? - bool isOpen() const { return m_sptrace.isOpen(); } - // METHODS + // METHODS - User called + + /// Return if file is open + bool isOpen() const VL_MT_SAFE { return m_sptrace.isOpen(); } /// Open a new VCD file /// This includes a complete header dump each time it is called, /// just as if this object was deleted and reconstructed. - void open(const char* filename) VL_MT_UNSAFE_ONE { m_sptrace.open(filename); } + void open(const char* filename) VL_MT_SAFE { m_sptrace.open(filename); } /// Continue a VCD dump by rotating to a new file name /// The header is only in the first file created, this allows /// "cat" to be used to combine the header plus any number of data files. - void openNext(bool incFilename = true) VL_MT_UNSAFE_ONE { m_sptrace.openNext(incFilename); } + void openNext(bool incFilename = true) VL_MT_SAFE { m_sptrace.openNext(incFilename); } /// Set size in megabytes after which new file should be created - void rolloverMB(size_t rolloverMB) { m_sptrace.rolloverMB(rolloverMB); } + void rolloverMB(size_t rolloverMB) VL_MT_SAFE { m_sptrace.rolloverMB(rolloverMB); } /// Close dump - void close() VL_MT_UNSAFE_ONE { m_sptrace.close(); } + void close() VL_MT_SAFE { m_sptrace.close(); } /// Flush dump - void flush() VL_MT_UNSAFE_ONE { m_sptrace.flush(); } + void flush() VL_MT_SAFE { m_sptrace.flush(); } /// Write one cycle of dump data - void dump(vluint64_t timeui) { m_sptrace.dump(timeui); } + void dump(vluint64_t timeui) VL_MT_SAFE { m_sptrace.dump(timeui); } /// Write one cycle of dump data - backward compatible and to reduce /// conversion warnings. It's better to use a vluint64_t time instead. void dump(double timestamp) { dump(static_cast(timestamp)); } void dump(vluint32_t timestamp) { dump(static_cast(timestamp)); } void dump(int timestamp) { dump(static_cast(timestamp)); } - /// Set time units (s/ms, defaults to ns) - /// For Verilated models, these propage from the Verilated default --timeunit - void set_time_unit(const char* unit) { m_sptrace.set_time_unit(unit); } - void set_time_unit(const std::string& unit) { m_sptrace.set_time_unit(unit); } - /// Set time resolution (s/ms, defaults to ns) - /// For Verilated models, these propage from the Verilated default --timeunit - void set_time_resolution(const char* unit) { m_sptrace.set_time_resolution(unit); } - void set_time_resolution(const std::string& unit) { m_sptrace.set_time_resolution(unit); } - /// Internal class access + // METHODS - Internal/backward compatible + // \protectedsection + + // Set time units (s/ms, defaults to ns) + // Users should not need to call this, as for Verilated models, these + // propage from the Verilated default timeunit + void set_time_unit(const char* unit) VL_MT_SAFE { m_sptrace.set_time_unit(unit); } + void set_time_unit(const std::string& unit) VL_MT_SAFE { m_sptrace.set_time_unit(unit); } + // Set time resolution (s/ms, defaults to ns) + // Users should not need to call this, as for Verilated models, these + // propage from the Verilated default timeprecision + void set_time_resolution(const char* unit) VL_MT_SAFE { m_sptrace.set_time_resolution(unit); } + void set_time_resolution(const std::string& unit) VL_MT_SAFE { + m_sptrace.set_time_resolution(unit); + } + + // Internal class access inline VerilatedVcd* spTrace() { return &m_sptrace; } #ifdef VL_TRACE_VCD_OLD_API @@ -385,7 +392,8 @@ public: // Note: These are only for testing for backward compatibility with foreign // code and is not used by Verilator. Do not use these as there is no // guarantee of functionality. - /// Use evcd format + + // Use evcd format void evcd(bool flag) VL_MT_UNSAFE_ONE { m_sptrace.evcd(flag); } #endif }; diff --git a/include/verilated_vcd_sc.cpp b/include/verilated_vcd_sc.cpp index d5ccb0df4..215139022 100644 --- a/include/verilated_vcd_sc.cpp +++ b/include/verilated_vcd_sc.cpp @@ -15,12 +15,10 @@ /// \brief Verilator tracing in VCD Format /// //============================================================================= -// SPDIFF_OFF #include "verilatedos.h" #include "verilated_vcd_sc.h" -// SPDIFF_ON //====================================================================== //====================================================================== diff --git a/include/verilated_vcd_sc.h b/include/verilated_vcd_sc.h index c18840541..2c7fe4cc2 100644 --- a/include/verilated_vcd_sc.h +++ b/include/verilated_vcd_sc.h @@ -15,27 +15,26 @@ /// This class is not threadsafe, as the SystemC kernel is not threadsafe. /// //============================================================================= -// SPDIFF_OFF -#ifndef _VERILATED_VCD_SC_H_ -#define _VERILATED_VCD_SC_H_ 1 +#ifndef VERILATOR_VERILATED_VCD_SC_H_ +#define VERILATOR_VERILATED_VCD_SC_H_ #include "verilatedos.h" #include "verilated_sc.h" #include "verilated_vcd_c.h" -// SPDIFF_ON //============================================================================= // VerilatedVcdSc /// -/// This class is passed to the SystemC simulation kernel, just like a -/// documented SystemC trace format. +/// This class creates a Verilator-friendly VCD trace format with the +/// SystemC simulation kernel, just like a SystemC-documented trace format. class VerilatedVcdSc final : sc_trace_file, public VerilatedVcdC { // CONSTRUCTORS VL_UNCOPYABLE(VerilatedVcdSc); public: + /// Construct a SC trace object, and register with the SystemC kernel VerilatedVcdSc() { sc_get_curr_simcontext()->add_trace_file(this); // We want to avoid a depreciated warning, but still be back compatible. @@ -48,16 +47,17 @@ public: } spTrace()->set_time_resolution(sc_get_time_resolution().to_string()); } + /// Destruct, flush, and close the dump virtual ~VerilatedVcdSc() { close(); } - // METHODS - /// Called by SystemC simulate() + // METHODS - for SC kernel + // Called by SystemC simulate() virtual void cycle(bool delta_cycle) { if (!delta_cycle) this->dump(sc_time_stamp().to_double()); } private: - /// Fake outs for linker + // METHODS - Fake outs for linker #ifdef NC_SYSTEMC // Cadence Incisive has these as abstract functions so we must create them diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index e48809e54..64b16fb83 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -21,7 +21,7 @@ /// //========================================================================= -#define _VERILATED_VPI_CPP_ +#define VERILATOR_VERILATED_VPI_CPP_ #include "verilated.h" #include "verilated_vpi.h" @@ -35,21 +35,21 @@ // Internal constants #define VL_DEBUG_IF_PLI VL_DEBUG_IF -constexpr unsigned VL_VPI_LINE_SIZE = 8192; +constexpr unsigned VL_VPI_LINE_SIZE_ = 8192; //====================================================================== // Internal macros -#define _VL_VPI_INTERNAL VerilatedVpiImp::error_info()->setMessage(vpiInternal)->setMessage -#define _VL_VPI_SYSTEM VerilatedVpiImp::error_info()->setMessage(vpiSystem)->setMessage -#define _VL_VPI_ERROR VerilatedVpiImp::error_info()->setMessage(vpiError)->setMessage -#define _VL_VPI_WARNING VerilatedVpiImp::error_info()->setMessage(vpiWarning)->setMessage -#define _VL_VPI_NOTICE VerilatedVpiImp::error_info()->setMessage(vpiNotice)->setMessage -#define _VL_VPI_ERROR_RESET VerilatedVpiImp::error_info()->resetError +#define VL_VPI_INTERNAL_ VerilatedVpiImp::error_info()->setMessage(vpiInternal)->setMessage +#define VL_VPI_SYSTEM_ VerilatedVpiImp::error_info()->setMessage(vpiSystem)->setMessage +#define VL_VPI_ERROR_ VerilatedVpiImp::error_info()->setMessage(vpiError)->setMessage +#define VL_VPI_WARNING_ VerilatedVpiImp::error_info()->setMessage(vpiWarning)->setMessage +#define VL_VPI_NOTICE_ VerilatedVpiImp::error_info()->setMessage(vpiNotice)->setMessage +#define VL_VPI_ERROR_RESET_ VerilatedVpiImp::error_info()->resetError // Not supported yet -#define _VL_VPI_UNIMP() \ - (_VL_VPI_ERROR(__FILE__, __LINE__, Verilated::catName("Unsupported VPI function: ", VL_FUNC))) +#define VL_VPI_UNIMP_() \ + (VL_VPI_ERROR_(__FILE__, __LINE__, Verilated::catName("Unsupported VPI function: ", VL_FUNC))) //====================================================================== // Implementation @@ -62,6 +62,7 @@ class VerilatedVpio VL_NOT_FINAL { static constexpr vluint32_t activeMagic() { return 0xfeed100f; } // MEM MANGLEMENT + // Internal note: Globals may multi-construct, see verilated.cpp top. static VL_THREAD_LOCAL vluint8_t* t_freeHead; public: @@ -335,8 +336,8 @@ public: virtual const VerilatedRange* rangep() const override { return &(varp()->packed()); } virtual const char* fullname() const override { static VL_THREAD_LOCAL std::string t_out; - char num[20]; - sprintf(num, "%d", m_index); + char num[25]; + VL_SNPRINTF(num, 25, "%d", m_index); t_out = std::string(scopep()->name()) + "." + name() + "[" + num + "]"; return t_out.c_str(); } @@ -504,17 +505,21 @@ class VerilatedVpiImp final { typedef std::list VpioCbList; typedef std::map, VerilatedVpiCbHolder> VpioTimedCbs; + // All only medium-speed, so use singleton function VpioCbList m_cbObjLists[CB_ENUM_MAX_VALUE]; // Callbacks for each supported reason VpioTimedCbs m_timedCbs; // Time based callbacks VerilatedVpiError* m_errorInfop = nullptr; // Container for vpi error info VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread vluint64_t m_nextCallbackId = 1; // Id to identify callback - static VerilatedVpiImp s_s; // Singleton + static VerilatedVpiImp& s() { // Singleton + static VerilatedVpiImp s_s; + return s_s; + } public: - static void assertOneCheck() { s_s.m_assertOne.check(); } - static vluint64_t nextCallbackId() { return ++s_s.m_nextCallbackId; } + static void assertOneCheck() { s().m_assertOne.check(); } + static vluint64_t nextCallbackId() { return ++s().m_nextCallbackId; } static void cbReasonAdd(vluint64_t id, const s_cb_data* cb_data_p) { // The passed cb_data_p was property of the user, so need to recreate @@ -525,20 +530,20 @@ public: cb_data_p->reason, id, cb_data_p->obj);); VerilatedVpioVar* varop = nullptr; if (cb_data_p->reason == cbValueChange) varop = VerilatedVpioVar::castp(cb_data_p->obj); - s_s.m_cbObjLists[cb_data_p->reason].emplace_back(id, cb_data_p, varop); + s().m_cbObjLists[cb_data_p->reason].emplace_back(id, cb_data_p, varop); } static void cbTimedAdd(vluint64_t id, const s_cb_data* cb_data_p, QData time) { // The passed cb_data_p was property of the user, so need to recreate VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d id=%" VL_PRI64 "d delay=%" VL_PRI64 "u\n", cb_data_p->reason, id, time);); - s_s.m_timedCbs.emplace(std::piecewise_construct, + s().m_timedCbs.emplace(std::piecewise_construct, std::forward_as_tuple(std::make_pair(time, id)), std::forward_as_tuple(id, cb_data_p, nullptr)); } static void cbReasonRemove(vluint64_t id, vluint32_t reason) { // Id might no longer exist, if already removed due to call after event, or teardown - VpioCbList& cbObjList = s_s.m_cbObjLists[reason]; + VpioCbList& cbObjList = s().m_cbObjLists[reason]; // We do not remove it now as we may be iterating the list, // instead set to nullptr and will cleanup later for (auto& ir : cbObjList) { @@ -547,13 +552,13 @@ public: } static void cbTimedRemove(vluint64_t id, QData time) { // Id might no longer exist, if already removed due to call after event, or teardown - const auto it = s_s.m_timedCbs.find(std::make_pair(time, id)); - if (VL_LIKELY(it != s_s.m_timedCbs.end())) it->second.invalidate(); + const auto it = s().m_timedCbs.find(std::make_pair(time, id)); + if (VL_LIKELY(it != s().m_timedCbs.end())) it->second.invalidate(); } static void callTimedCbs() VL_MT_UNSAFE_ONE { assertOneCheck(); QData time = VL_TIME_Q(); - for (auto it = s_s.m_timedCbs.begin(); it != s_s.m_timedCbs.end();) { + for (auto it = s().m_timedCbs.begin(); it != s().m_timedCbs.end();) { if (VL_UNLIKELY(it->first.first <= time)) { VerilatedVpiCbHolder& ho = it->second; const auto last_it = it; @@ -564,19 +569,19 @@ public: ho.invalidate(); // Timed callbacks are one-shot (ho.cb_rtnp())(ho.cb_datap()); } - s_s.m_timedCbs.erase(last_it); + s().m_timedCbs.erase(last_it); } else { ++it; } } } static QData cbNextDeadline() { - const auto it = s_s.m_timedCbs.cbegin(); - if (VL_LIKELY(it != s_s.m_timedCbs.cend())) return it->first.first; + const auto it = s().m_timedCbs.cbegin(); + if (VL_LIKELY(it != s().m_timedCbs.cend())) return it->first.first; return ~0ULL; // maxquad } static bool callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE { - VpioCbList& cbObjList = s_s.m_cbObjLists[reason]; + VpioCbList& cbObjList = s().m_cbObjLists[reason]; bool called = false; if (cbObjList.empty()) return called; const auto last = std::prev(cbObjList.end()); // prevent looping over newly added elements @@ -600,7 +605,7 @@ public: } static bool callValueCbs() VL_MT_UNSAFE_ONE { assertOneCheck(); - VpioCbList& cbObjList = s_s.m_cbObjLists[cbValueChange]; + VpioCbList& cbObjList = s().m_cbObjLists[cbValueChange]; bool called = false; typedef std::unordered_set VpioVarSet; VpioVarSet update; // set of objects to update after callbacks @@ -640,12 +645,20 @@ public: static VerilatedVpiError* error_info() VL_MT_UNSAFE_ONE; // getter for vpi error info }; -class VerilatedVpiError final { - //// Container for vpi error info +//====================================================================== +// Statics +// Internal note: Globals may multi-construct, see verilated.cpp top. +VL_THREAD_LOCAL vluint8_t* VerilatedVpio::t_freeHead = nullptr; + +//====================================================================== +// VerilatedVpiError +/// Internal container for vpi error info + +class VerilatedVpiError final { t_vpi_error_info m_errorInfo; bool m_flag = false; - char m_buff[VL_VPI_LINE_SIZE]; + char m_buff[VL_VPI_LINE_SIZE_]; void setError(PLI_BYTE8* message, PLI_BYTE8* code, PLI_BYTE8* file, PLI_INT32 line) { m_errorInfo.message = message; m_errorInfo.file = file; @@ -654,7 +667,7 @@ class VerilatedVpiError final { do_callbacks(); } void do_callbacks() { - if (getError()->level >= vpiError && Verilated::fatalOnVpiError()) { + if (getError()->level >= vpiError && Verilated::threadContextp()->fatalOnVpiError()) { // Stop on vpi error/unsupported vpi_unsupported(); } @@ -708,11 +721,6 @@ public: static const char* strFromVpiProp(PLI_INT32 vpiVal) VL_MT_SAFE; }; -//====================================================================== - -VerilatedVpiImp VerilatedVpiImp::s_s; // Singleton -VL_THREAD_LOCAL vluint8_t* VerilatedVpio::t_freeHead = nullptr; - //====================================================================== // VerilatedVpi implementation @@ -742,8 +750,8 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() { VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE { VerilatedVpiImp::assertOneCheck(); - if (VL_UNLIKELY(!s_s.m_errorInfop)) s_s.m_errorInfop = new VerilatedVpiError(); - return s_s.m_errorInfop; + if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError(); + return s().m_errorInfop; } //====================================================================== @@ -1143,16 +1151,16 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) { // Don't confuse with the callback-activated t_cb_data object handle // which is the object causing the callback rather than the callback itself VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); // cppcheck-suppress nullPointer if (VL_UNLIKELY(!cb_data_p)) { - _VL_VPI_WARNING(__FILE__, __LINE__, "%s : callback data pointer is null", VL_FUNC); + VL_VPI_WARNING_(__FILE__, __LINE__, "%s : callback data pointer is null", VL_FUNC); return nullptr; } switch (cb_data_p->reason) { case cbAfterDelay: { QData time = 0; - if (cb_data_p->time) time = _VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low); + if (cb_data_p->time) time = VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low); QData abstime = VL_TIME_Q() + time; vluint64_t id = VerilatedVpiImp::nextCallbackId(); VerilatedVpioTimedCb* vop = new VerilatedVpioTimedCb{id, abstime}; @@ -1175,7 +1183,7 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) { return vop->castVpiHandle(); } default: - _VL_VPI_WARNING(__FILE__, __LINE__, "%s: Unsupported callback type %s", VL_FUNC, + VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported callback type %s", VL_FUNC, VerilatedVpiError::strFromVpiCallbackReason(cb_data_p->reason)); return nullptr; } @@ -1184,26 +1192,26 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) { PLI_INT32 vpi_remove_cb(vpiHandle cb_obj) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_remove_cb %p\n", cb_obj);); VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); VerilatedVpio* vop = VerilatedVpio::castp(cb_obj); if (VL_UNLIKELY(!vop)) return 0; return vop->dovpi_remove_cb(); } -void vpi_get_cb_info(vpiHandle /*object*/, p_cb_data /*cb_data_p*/) { _VL_VPI_UNIMP(); } +void vpi_get_cb_info(vpiHandle /*object*/, p_cb_data /*cb_data_p*/) { VL_VPI_UNIMP_(); } vpiHandle vpi_register_systf(p_vpi_systf_data /*systf_data_p*/) { - _VL_VPI_UNIMP(); + VL_VPI_UNIMP_(); return nullptr; } void vpi_get_systf_info(vpiHandle /*object*/, p_vpi_systf_data /*systf_data_p*/) { - _VL_VPI_UNIMP(); + VL_VPI_UNIMP_(); } // for obtaining handles vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); if (VL_UNLIKELY(!namep)) return nullptr; VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_name %s %p\n", namep, scope);); const VerilatedVar* varp = nullptr; @@ -1216,7 +1224,7 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { } { // This doesn't yet follow the hierarchy in the proper way - scopep = Verilated::scopeFind(namep); + scopep = Verilated::threadContextp()->scopeFind(namep); if (scopep) { // Whole thing found as a scope if (scopep->type() == VerilatedScope::SCOPE_MODULE) { return (new VerilatedVpioModule(scopep))->castVpiHandle(); @@ -1234,11 +1242,11 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { if (scopename.find('.') == std::string::npos) { // This is a toplevel, hence search in our TOP ports first. - scopep = Verilated::scopeFind("TOP"); + scopep = Verilated::threadContextp()->scopeFind("TOP"); if (scopep) varp = scopep->varFind(baseNamep); } if (!varp) { - scopep = Verilated::scopeFind(scopename.c_str()); + scopep = Verilated::threadContextp()->scopeFind(scopename.c_str()); if (!scopep) return nullptr; varp = scopep->varFind(baseNamep); } @@ -1256,7 +1264,7 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) { // Used to get array entries VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_index %p %d\n", object, indx);); VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); // Memory words are not indexable VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object); if (VL_UNLIKELY(vop)) return nullptr; @@ -1278,7 +1286,7 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) { indx - varop->varp()->unpacked().left())) ->castVpiHandle(); } - _VL_VPI_INTERNAL(__FILE__, __LINE__, "%s : can't resolve handle", VL_FUNC); + VL_VPI_INTERNAL_(__FILE__, __LINE__, "%s : can't resolve handle", VL_FUNC); return nullptr; } @@ -1287,7 +1295,7 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) { vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle %d %p\n", type, object);); VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); switch (type) { case vpiLeftRange: { if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) { @@ -1297,7 +1305,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { if (VL_UNLIKELY(!vop->rangep())) return nullptr; return (new VerilatedVpioConst(vop->rangep()->left()))->castVpiHandle(); } - _VL_VPI_WARNING(__FILE__, __LINE__, + VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned", VL_FUNC, object, VerilatedVpiError::strFromVpiMethod(type)); return nullptr; @@ -1310,7 +1318,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { if (VL_UNLIKELY(!vop->rangep())) return nullptr; return (new VerilatedVpioConst(vop->rangep()->right()))->castVpiHandle(); } - _VL_VPI_WARNING(__FILE__, __LINE__, + VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned", VL_FUNC, object, VerilatedVpiError::strFromVpiMethod(type)); return nullptr; @@ -1331,7 +1339,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { return (new VerilatedVpioVar(vop->varp(), vop->scopep()))->castVpiHandle(); } default: - _VL_VPI_WARNING(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned", + VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned", VL_FUNC, VerilatedVpiError::strFromVpiMethod(type)); return nullptr; } @@ -1339,21 +1347,21 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { vpiHandle vpi_handle_multi(PLI_INT32 /*type*/, vpiHandle /*refHandle1*/, vpiHandle /*refHandle2*/, ...) { - _VL_VPI_UNIMP(); + VL_VPI_UNIMP_(); return nullptr; } vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_iterate %d %p\n", type, object);); VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); switch (type) { case vpiMemoryWord: { VerilatedVpioVar* vop = VerilatedVpioVar::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; if (vop->varp()->dims() < 2) return nullptr; if (vop->varp()->dims() > 2) { - _VL_VPI_WARNING(__FILE__, __LINE__, + VL_VPI_WARNING_(__FILE__, __LINE__, "%s: %s, object %s has unsupported number of indices (%d)", VL_FUNC, VerilatedVpiError::strFromVpiMethod(type), vop->fullname(), vop->varp()->dims()); @@ -1366,7 +1374,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { if (vop->varp()->dims() < 2) return nullptr; // Unsupported is multidim list if (vop->varp()->dims() > 2) { - _VL_VPI_WARNING(__FILE__, __LINE__, + VL_VPI_WARNING_(__FILE__, __LINE__, "%s: %s, object %s has unsupported number of indices (%d)", VL_FUNC, VerilatedVpiError::strFromVpiMethod(type), vop->fullname(), vop->varp()->dims()); @@ -1387,7 +1395,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { return ((new VerilatedVpioModuleIter(it->second))->castVpiHandle()); } default: - _VL_VPI_WARNING(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned", + VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned", VL_FUNC, VerilatedVpiError::strFromVpiObjType(type)); return nullptr; } @@ -1395,7 +1403,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { vpiHandle vpi_scan(vpiHandle object) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_scan %p\n", object);); VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); VerilatedVpio* vop = VerilatedVpio::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; return vop->dovpi_scan(); @@ -1407,14 +1415,15 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) { // Leave this in the header file - in many cases the compiler can constant propagate "object" VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get %d %p\n", property, object);); VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); switch (property) { case vpiTimePrecision: { - return Verilated::timeprecision(); + return Verilated::threadContextp()->timeprecision(); } case vpiTimeUnit: { VerilatedVpioScope* vop = VerilatedVpioScope::castp(object); - if (!vop) return Verilated::timeunit(); // Null asks for global, not unlikely + if (!vop) + return Verilated::threadContextp()->timeunit(); // Null asks for global, not unlikely return vop->scopep()->timeunit(); } case vpiType: { @@ -1440,14 +1449,14 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) { return vop->size(); } default: - _VL_VPI_WARNING(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned", + VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned", VL_FUNC, VerilatedVpiError::strFromVpiProp(property)); return 0; } } PLI_INT64 vpi_get64(PLI_INT32 /*property*/, vpiHandle /*object*/) { - _VL_VPI_UNIMP(); + VL_VPI_UNIMP_(); return 0; } @@ -1455,7 +1464,7 @@ PLI_BYTE8* vpi_get_str(PLI_INT32 property, vpiHandle object) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_str %d %p\n", property, object);); VerilatedVpiImp::assertOneCheck(); VerilatedVpio* vop = VerilatedVpio::castp(object); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); if (VL_UNLIKELY(!vop)) return nullptr; switch (property) { case vpiName: { @@ -1471,7 +1480,7 @@ PLI_BYTE8* vpi_get_str(PLI_INT32 property, vpiHandle object) { return const_cast(VerilatedVpiError::strFromVpiObjType(vop->type())); } default: - _VL_VPI_WARNING(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned", + VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned", VL_FUNC, VerilatedVpiError::strFromVpiProp(property)); return nullptr; } @@ -1479,8 +1488,8 @@ PLI_BYTE8* vpi_get_str(PLI_INT32 property, vpiHandle object) { // delay processing -void vpi_get_delays(vpiHandle /*object*/, p_vpi_delay /*delay_p*/) { _VL_VPI_UNIMP(); } -void vpi_put_delays(vpiHandle /*object*/, p_vpi_delay /*delay_p*/) { _VL_VPI_UNIMP(); } +void vpi_get_delays(vpiHandle /*object*/, p_vpi_delay /*delay_p*/) { VL_VPI_UNIMP_(); } +void vpi_put_delays(vpiHandle /*object*/, p_vpi_delay /*delay_p*/) { VL_VPI_UNIMP_(); } // value processing bool vl_check_format(const VerilatedVar* varp, const p_vpi_value valuep, const char* fullname, @@ -1532,7 +1541,7 @@ bool vl_check_format(const VerilatedVar* varp, const p_vpi_value valuep, const c } else { status = false; } - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, VerilatedVpiError::strFromVpiVal(valuep->format), fullname); return status; } @@ -1592,7 +1601,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, if (bits > t_outStrSz) { // limit maximum size of output to size of buffer to prevent overrun. bits = t_outStrSz; - _VL_VPI_WARNING( + VL_VPI_WARNING_( __FILE__, __LINE__, "%s: Truncating string value of %s for %s" " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", @@ -1613,7 +1622,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, int i; if (chars > t_outStrSz) { // limit maximum size of output to size of buffer to prevent overrun. - _VL_VPI_WARNING( + VL_VPI_WARNING_( __FILE__, __LINE__, "%s: Truncating string value of %s for %s" " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", @@ -1671,7 +1680,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, int i; if (chars > t_outStrSz) { // limit maximum size of output to size of buffer to prevent overrun. - _VL_VPI_WARNING( + VL_VPI_WARNING_( __FILE__, __LINE__, "%s: Truncating string value of %s for %s" " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", @@ -1705,7 +1714,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, int i; if (bytes > t_outStrSz) { // limit maximum size of output to size of buffer to prevent overrun. - _VL_VPI_WARNING( + VL_VPI_WARNING_( __FILE__, __LINE__, "%s: Truncating string value of %s for %s" " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", @@ -1735,14 +1744,14 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, } else if (valuep->format == vpiSuppressVal) { return; } - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", VL_FUNC, + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", VL_FUNC, VerilatedVpiError::strFromVpiVal(valuep->format), fullname); } void vpi_get_value(vpiHandle object, p_vpi_value valuep) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_value %p\n", object);); VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); if (VL_UNLIKELY(!valuep)) return; if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) { @@ -1756,20 +1765,20 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) { valuep->value.integer = vop->num(); return; } - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname()); return; } - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", VL_FUNC, object); + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", VL_FUNC, object); } vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_p*/, PLI_INT32 /*flags*/) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value %p %p\n", object, valuep);); VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); if (VL_UNLIKELY(!valuep)) { - _VL_VPI_WARNING(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer"); + VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer"); return nullptr; } if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) { @@ -1779,7 +1788,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", vop->varp()->datap(), vop->varDatap());); if (VL_UNLIKELY(!vop->varp()->isPublicRW())) { - _VL_VPI_WARNING(__FILE__, __LINE__, + VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value to signal marked read-only," " use public_flat_rw instead: %s", vop->fullname()); @@ -1801,7 +1810,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ = valuep->value.vector[0].aval & vop->mask(); return object; } else if (vop->varp()->vltype() == VLVT_UINT64) { - *(reinterpret_cast(vop->varDatap())) = _VL_SET_QII( + *(reinterpret_cast(vop->varDatap())) = VL_SET_QII( valuep->value.vector[1].aval & vop->mask(), valuep->value.vector[0].aval); return object; } else if (vop->varp()->vltype() == VLVT_WDATA) { @@ -1847,7 +1856,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ if (digit >= '0' && digit <= '7') { val.half = digit - '0'; } else { - _VL_VPI_WARNING(__FILE__, __LINE__, + VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Non octal character '%c' in '%s' as value %s for %s", VL_FUNC, digit, valuep->value.str, VerilatedVpiError::strFromVpiVal(valuep->format), @@ -1883,13 +1892,13 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ unsigned long long val; int success = sscanf(valuep->value.str, "%30llu%15s", &val, remainder); if (success < 1) { - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Parsing failed for '%s' as value %s for %s", + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Parsing failed for '%s' as value %s for %s", VL_FUNC, valuep->value.str, VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname()); return nullptr; } if (success > 1) { - _VL_VPI_WARNING(__FILE__, __LINE__, + VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Trailing garbage '%s' in '%s' as value %s for %s", VL_FUNC, remainder, valuep->value.str, VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname()); @@ -1927,7 +1936,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ } else if (digit >= 'A' && digit <= 'F') { hex = digit - 'A' + 10; } else { - _VL_VPI_WARNING(__FILE__, __LINE__, + VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Non hex character '%c' in '%s' as value %s for %s", VL_FUNC, digit, valuep->value.str, VerilatedVpiError::strFromVpiVal(valuep->format), @@ -1969,39 +1978,39 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ return object; } } - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", VL_FUNC, VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname()); return nullptr; } else if (VerilatedVpioParam* vop = VerilatedVpioParam::castp(object)) { - _VL_VPI_WARNING(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiParameter: %s", + VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiParameter: %s", VL_FUNC, vop->fullname()); return nullptr; } else if (VerilatedVpioConst* vop = VerilatedVpioConst::castp(object)) { - _VL_VPI_WARNING(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiConstant: %s", + VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiConstant: %s", VL_FUNC, vop->fullname()); return nullptr; } - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", VL_FUNC, object); + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", VL_FUNC, object); return nullptr; } void vpi_get_value_array(vpiHandle /*object*/, p_vpi_arrayvalue /*arrayvalue_p*/, PLI_INT32* /*index_p*/, PLI_UINT32 /*num*/) { - _VL_VPI_UNIMP(); + VL_VPI_UNIMP_(); } void vpi_put_value_array(vpiHandle /*object*/, p_vpi_arrayvalue /*arrayvalue_p*/, PLI_INT32* /*index_p*/, PLI_UINT32 /*num*/) { - _VL_VPI_UNIMP(); + VL_VPI_UNIMP_(); } // time processing void vpi_get_time(vpiHandle object, p_vpi_time time_p) { VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); // cppcheck-suppress nullPointer if (VL_UNLIKELY(!time_p)) { - _VL_VPI_WARNING(__FILE__, __LINE__, "Ignoring vpi_get_time with nullptr value pointer"); + VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_get_time with nullptr value pointer"); return; } if (time_p->type == vpiSimTime) { @@ -2014,39 +2023,40 @@ void vpi_get_time(vpiHandle object, p_vpi_time time_p) { } else if (time_p->type == vpiScaledRealTime) { double dtime = VL_TIME_D(); if (VerilatedVpioScope* vop = VerilatedVpioScope::castp(object)) { - int scalePow10 = Verilated::timeprecision() - vop->scopep()->timeunit(); + int scalePow10 + = Verilated::threadContextp()->timeprecision() - vop->scopep()->timeunit(); double scale = vl_time_multiplier(scalePow10); // e.g. 0.0001 dtime *= scale; } time_p->real = dtime; return; } - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported type (%d)", VL_FUNC, time_p->type); + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%d)", VL_FUNC, time_p->type); } // I/O routines PLI_UINT32 vpi_mcd_open(PLI_BYTE8* filenamep) { VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); return VL_FOPEN_NN(filenamep, "wb"); } PLI_UINT32 vpi_mcd_close(PLI_UINT32 mcd) { VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); VL_FCLOSE_I(mcd); return 0; } PLI_BYTE8* vpi_mcd_name(PLI_UINT32 /*mcd*/) { - _VL_VPI_UNIMP(); + VL_VPI_UNIMP_(); return nullptr; } PLI_INT32 vpi_mcd_printf(PLI_UINT32 mcd, PLI_BYTE8* formatp, ...) { VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); va_list ap; va_start(ap, formatp); int chars = vpi_mcd_vprintf(mcd, formatp, ap); @@ -2056,7 +2066,7 @@ PLI_INT32 vpi_mcd_printf(PLI_UINT32 mcd, PLI_BYTE8* formatp, ...) { PLI_INT32 vpi_printf(PLI_BYTE8* formatp, ...) { VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); va_list ap; va_start(ap, formatp); int chars = vpi_vprintf(formatp, ap); @@ -2066,14 +2076,14 @@ PLI_INT32 vpi_printf(PLI_BYTE8* formatp, ...) { PLI_INT32 vpi_vprintf(PLI_BYTE8* formatp, va_list ap) { VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); return VL_VPRINTF(formatp, ap); } PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, PLI_BYTE8* format, va_list ap) { VerilatedVpiImp::assertOneCheck(); FILE* fp = VL_CVT_I_FP(mcd); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); // cppcheck-suppress nullPointer if (VL_UNLIKELY(!fp)) return 0; int chars = vfprintf(fp, format, ap); @@ -2082,7 +2092,7 @@ PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, PLI_BYTE8* format, va_list ap) { PLI_INT32 vpi_flush(void) { VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); Verilated::runFlushCallbacks(); return 0; } @@ -2090,7 +2100,7 @@ PLI_INT32 vpi_flush(void) { PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) { VerilatedVpiImp::assertOneCheck(); FILE* fp = VL_CVT_I_FP(mcd); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); if (VL_UNLIKELY(!fp)) return 1; fflush(fp); return 0; @@ -2099,7 +2109,7 @@ PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) { // utility routines PLI_INT32 vpi_compare_objects(vpiHandle /*object1*/, vpiHandle /*object2*/) { - _VL_VPI_UNIMP(); + VL_VPI_UNIMP_(); return 0; } PLI_INT32 vpi_chk_error(p_vpi_error_info error_info_p) { @@ -2123,7 +2133,7 @@ PLI_INT32 vpi_release_handle(vpiHandle object) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_release_handle %p\n", object);); VerilatedVpiImp::assertOneCheck(); VerilatedVpio* vop = VerilatedVpio::castp(object); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); if (VL_UNLIKELY(!vop)) return 0; VL_DO_DANGLING(delete vop, vop); return 1; @@ -2131,9 +2141,10 @@ PLI_INT32 vpi_release_handle(vpiHandle object) { PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info vlog_info_p) VL_MT_SAFE { VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); - vlog_info_p->argc = Verilated::getCommandArgs()->argc; - vlog_info_p->argv = const_cast(Verilated::getCommandArgs()->argv); + VL_VPI_ERROR_RESET_(); + auto argc_argv = Verilated::threadContextp()->impp()->argc_argv(); + vlog_info_p->argc = argc_argv.first; + vlog_info_p->argv = argc_argv.second; vlog_info_p->product = const_cast(Verilated::productName()); vlog_info_p->version = const_cast(Verilated::productVersion()); return 1; @@ -2142,26 +2153,26 @@ PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info vlog_info_p) VL_MT_SAFE { // routines added with 1364-2001 PLI_INT32 vpi_get_data(PLI_INT32 /*id*/, PLI_BYTE8* /*dataLoc*/, PLI_INT32 /*numOfBytes*/) { - _VL_VPI_UNIMP(); + VL_VPI_UNIMP_(); return 0; } PLI_INT32 vpi_put_data(PLI_INT32 /*id*/, PLI_BYTE8* /*dataLoc*/, PLI_INT32 /*numOfBytes*/) { - _VL_VPI_UNIMP(); + VL_VPI_UNIMP_(); return 0; } void* vpi_get_userdata(vpiHandle /*obj*/) { - _VL_VPI_UNIMP(); + VL_VPI_UNIMP_(); return nullptr; } PLI_INT32 vpi_put_userdata(vpiHandle /*obj*/, void* /*userdata*/) { - _VL_VPI_UNIMP(); + VL_VPI_UNIMP_(); return 0; } PLI_INT32 vpi_control(PLI_INT32 operation, ...) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_control %d\n", operation);); VerilatedVpiImp::assertOneCheck(); - _VL_VPI_ERROR_RESET(); + VL_VPI_ERROR_RESET_(); switch (operation) { case vpiFinish: { VL_FINISH_MT("", 0, "*VPI*"); @@ -2172,7 +2183,7 @@ PLI_INT32 vpi_control(PLI_INT32 operation, ...) { return 1; // LCOV_EXCL_LINE } default: { - _VL_VPI_WARNING(__FILE__, __LINE__, "%s: Unsupported type %s, ignoring", VL_FUNC, + VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, ignoring", VL_FUNC, VerilatedVpiError::strFromVpiProp(operation)); return 0; } @@ -2181,6 +2192,6 @@ PLI_INT32 vpi_control(PLI_INT32 operation, ...) { vpiHandle vpi_handle_by_multi_index(vpiHandle /*obj*/, PLI_INT32 /*num_index*/, PLI_INT32* /*index_array*/) { - _VL_VPI_UNIMP(); + VL_VPI_UNIMP_(); return nullptr; } diff --git a/include/verilated_vpi.h b/include/verilated_vpi.h index 9efa044e8..2e2d125a5 100644 --- a/include/verilated_vpi.h +++ b/include/verilated_vpi.h @@ -19,8 +19,8 @@ /// //========================================================================= -#ifndef _VERILATED_VPI_H_ -#define _VERILATED_VPI_H_ 1 ///< Header Guard +#ifndef VERILATOR_VERILATED_VPI_H_ +#define VERILATOR_VERILATED_VPI_H_ ///< Header Guard #include "verilatedos.h" #include "verilated.h" @@ -47,7 +47,8 @@ public: /// Returns time of the next registered VPI callback, or /// ~(0) if none are registered static QData cbNextDeadline() VL_MT_UNSAFE_ONE; - /// Self test, for internal use only + + // Self test, for internal use only static void selfTest() VL_MT_UNSAFE_ONE; }; diff --git a/include/verilatedos.h b/include/verilatedos.h index b83d5f1bc..c8d6a9dc3 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -22,8 +22,8 @@ /// //************************************************************************* -#ifndef _VERILATEDOS_H_ -#define _VERILATEDOS_H_ 1 ///< Header Guard +#ifndef VERILATOR_VERILATEDOS_H_ +#define VERILATOR_VERILATEDOS_H_ ///< Header Guard // Current clang-format versions botch #ifdef inclusion, so // clang-format off @@ -39,7 +39,9 @@ # define VL_ATTR_PRINTF(fmtArgNum) __attribute__((format(printf, (fmtArgNum), (fmtArgNum) + 1))) # define VL_ATTR_PURE __attribute__((pure)) # define VL_ATTR_UNUSED __attribute__((unused)) -# define VL_ATTR_WEAK __attribute__((weak)) +# if !defined(_WIN32) && !defined(__MINGW32__) +# define VL_ATTR_WEAK __attribute__((weak)) +# endif # define VL_FUNC __func__ # if defined(__clang__) && defined(VL_THREADED) # define VL_ACQUIRE(...) __attribute__((acquire_capability(__VA_ARGS__))) @@ -146,6 +148,7 @@ #define VL_MT_SAFE ///< Comment tag that function is threadsafe when VL_THREADED #define VL_MT_SAFE_POSTINIT ///< Comment tag that function is threadsafe when VL_THREADED, only ///< during normal operation (post-init) +#define VL_MT_SAFE_EXCLUDES(mutex) VL_EXCLUDES(mutex) ///< Threadsafe and uses given mutex #define VL_MT_UNSAFE ///< Comment tag that function is not threadsafe when VL_THREADED #define VL_MT_UNSAFE_ONE ///< Comment tag that function is not threadsafe when VL_THREADED, ///< protected to make sure single-caller @@ -479,7 +482,7 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type #endif //========================================================================= -// String related OS-specific functions +// String/time related OS-specific functions #ifdef _MSC_VER # define VL_STRCASECMP _stricmp @@ -487,6 +490,12 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type # define VL_STRCASECMP strcasecmp #endif +#ifdef _MSC_VER +# define VL_LOCALTIME_R(timep, tmp) localtime_c((tmp), (timep)) +#else +# define VL_LOCALTIME_R(timep, tmp) localtime_r((timep), (tmp)) +#endif + //========================================================================= // Macros controlling target specific optimizations diff --git a/nodist/code_coverage b/nodist/code_coverage index eebc34d03..44efa6d0f 100755 --- a/nodist/code_coverage +++ b/nodist/code_coverage @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# pylint: disable=C0103,C0114,C0115,C0116,R0912,R0914,R0915,W0125,W0621,exec-used ###################################################################### import argparse @@ -7,6 +8,7 @@ import multiprocessing import os import re import subprocess +import sys RealPath = os.path.dirname(os.path.realpath(__file__)) Exclude_Branch_Regexps = [] @@ -36,9 +38,23 @@ def test(): ci_fold_start("configure") print("Stage 1: configure (coverage on)") run("autoconf") - # Exceptions can pollute the branch coverage data - run("./configure --enable-longtests CXX='g++ --coverage -fno-exceptions -DVL_GCOV'" - ) + cxx_flags = ( + "--coverage" + # Otherwise inline may not show as uncovered + # If we use this then e.g. verilated.h functions properly show up + # if unused. + # However, VerilatedSerialize::write then changes from covered + # to uncovered (in G++ 9.3.0) even with all inlining turned off. + # Having false negative coverage is more effort then missing negatives. + # Also this seems to explode the runtime (since a lot more data). + #+ " -fkeep-inline-functions" + # Otherwise static may not show as uncovered + + " -fkeep-static-functions" + # Exceptions can pollute the branch coverage data + + " -fno-exceptions" + # Define-out some impossible stuff + + " -DVL_GCOV") + run("./configure --enable-longtests CXX='g++ " + cxx_flags + "'") ci_fold_end() if Args.stage_enabled[2]: @@ -123,8 +139,10 @@ def test(): # Must run in root directory to find all files os.makedirs(cc_dir, exist_ok=True) run(RealPath + "/fastcov.py -b -c src/obj_dbg -X --lcov" + - " --exclude /usr --exclude test_regress" + " -o " + cc_dir + - "/app_total.info") + # " --exclude /usr --exclude test_regress" + " -o " + cc_dir + + " -o " + cc_dir + "/app_total.info") + # For debug to convert single .gcna/.gcno in a directory to cov.info: + # lcov -c -d . -o cov.info ci_fold_end() if Args.stage_enabled[6]: @@ -223,7 +241,7 @@ def clone_sources(cc_dir): if done: break if re.search(regexp, line): - #print("%s:%d: %s" % (infile, lineno, line) + # print("%s:%d: %s" % (infile, lineno, line) line += " //code_coverage: // LCOV_EXCL_LINE LCOV_EXCL_BR_LINE" excluded_lines += 1 excluded_br_lines += 1 @@ -233,7 +251,7 @@ def clone_sources(cc_dir): if done: break if re.search(regexp, line): - #print("%s:%d: %s" % (infile, lineno, line) + # print("%s:%d: %s" % (infile, lineno, line) line += " //code_coverage: // LCOV_EXCL_BR_LINE" excluded_br_lines += 1 done = True @@ -256,7 +274,7 @@ def cleanup_abs_paths_info(cc_dir, infile, outfile): count=1) line = re.sub(cc_dir + '/', '', line, count=1) line = re.sub(r'obj_dbg/verilog.y$', 'verilog.y', line) - #print("Remaining SF: "+line) + # print("Remaining SF: "+line) lines.append(line) with open(outfile, "w") as ofh: @@ -397,7 +415,7 @@ if True: elif match_from: start = int(match_from.group(1)) else: - os.exit("%Error: --stages not understood: " + Args.stages) + sys.exit("%Error: --stages not understood: " + Args.stages) for n in range(0, 100): Args.stage_enabled[n] = False for n in range(start, end + 1): @@ -406,6 +424,6 @@ if True: test() ###################################################################### -### Local Variables: -### compile-command: "cd .. ; nodist/code_coverage " -### End: +# Local Variables: +# compile-command: "cd .. ; nodist/code_coverage " +# End: diff --git a/nodist/dot_importer b/nodist/dot_importer index 2ff96c4e3..fb6d6661a 100755 --- a/nodist/dot_importer +++ b/nodist/dot_importer @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# pylint: disable=C0103,C0114,C0115,C0116,C0301 ###################################################################### import argparse @@ -104,6 +105,6 @@ dotread(Args.filename) cwrite("graph_export.cpp") ###################################################################### -### Local Variables: -### compile-command: "./dot_importer ../test_regress/obj_vlt/t_EXAMPLE/*orderg_o*.dot && cat graph_export.cpp" -### End: +# Local Variables: +# compile-command: "./dot_importer ../test_regress/obj_vlt/t_EXAMPLE/*orderg_o*.dot && cat graph_export.cpp" +# End: diff --git a/nodist/fuzzer/actual_fail b/nodist/fuzzer/actual_fail index 4ddcc9c06..8a8f2fdc8 100755 --- a/nodist/fuzzer/actual_fail +++ b/nodist/fuzzer/actual_fail @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# pylint: disable=C0103,C0114,C0115,C0116,C0321,R0911 ###################################################################### # DESCRIPTION: Fuzzer result checker # diff --git a/nodist/fuzzer/generate_dictionary b/nodist/fuzzer/generate_dictionary index b030f23ea..546e48fcb 100755 --- a/nodist/fuzzer/generate_dictionary +++ b/nodist/fuzzer/generate_dictionary @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# pylint: disable=C0103,C0114,C0115,C0116,C0321 ###################################################################### # DESCRIPTION: Fuzzer dictionary generator # @@ -50,7 +51,7 @@ def write_file(filename, contents): def parse_line(s): # str->maybe str - if len(s) == 0: return + if len(s) == 0: return None part = skip_while(lambda x: x != '"', s) if len(part) == 0 or part[0] != '"': return None literal_part = take_while(lambda x: x != '"', part[1:]) @@ -67,7 +68,7 @@ def main(): dirname = 'dictionary' r = system('mkdir -p ' + dirname) - assert (r == 0) + assert r == 0 for i, token in enumerate(tokens): write_file(dirname + '/' + str(i), token) diff --git a/nodist/install_test b/nodist/install_test index 1d2464fad..faa2cf1ac 100755 --- a/nodist/install_test +++ b/nodist/install_test @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# pylint: disable=C0103,C0114,C0115,C0116,R0801,R0915 ###################################################################### import argparse @@ -6,6 +7,7 @@ import multiprocessing import os import shutil import subprocess +import sys ###################################################################### @@ -51,43 +53,43 @@ def test(): # run a test using just the path if Args.stage <= 2: print("== stage 2") - dir = testdirp - run("/bin/rm -rf " + dir) - run("/bin/mkdir -p " + dir) + odir = testdirp + run("/bin/rm -rf " + odir) + run("/bin/mkdir -p " + odir) path = prefix + "/bin" + ":" + prefix + "/share/bin" - write_verilog(dir) - run("cd " + dir + " && PATH=" + path + + write_verilog(odir) + run("cd " + odir + " && PATH=" + path + ":$PATH verilator --cc top.v --exe sim_main.cpp") - run("cd " + dir + "/obj_dir && PATH=" + path + + run("cd " + odir + "/obj_dir && PATH=" + path + ":$PATH make -f Vtop.mk") - run("cd " + dir + " && PATH=" + path + ":$PATH obj_dir/Vtop") + run("cd " + odir + " && PATH=" + path + ":$PATH obj_dir/Vtop") # run a test using exact path to binary if Args.stage <= 3: print("== stage 3") - dir = testdirn - run("/bin/rm -rf " + dir) - run("/bin/mkdir -p " + dir) - write_verilog(dir) + odir = testdirn + run("/bin/rm -rf " + odir) + run("/bin/mkdir -p " + odir) + write_verilog(odir) bin1 = prefix + "/bin" - run("cd " + dir + " && " + bin1 + + run("cd " + odir + " && " + bin1 + "/verilator --cc top.v --exe sim_main.cpp") - run("cd " + dir + "/obj_dir && make -f Vtop.mk") - run("cd " + dir + "/obj_dir && ./Vtop") + run("cd " + odir + "/obj_dir && make -f Vtop.mk") + run("cd " + odir + "/obj_dir && ./Vtop") if Args.stage <= 9: print("*-* All Finished *-*") -def write_verilog(dir): - shutil.copy2("examples/make_hello_c/top.v", dir + "/top.v") - shutil.copy2("examples/make_hello_c/sim_main.cpp", dir + "/sim_main.cpp") +def write_verilog(odir): + shutil.copy2("examples/make_hello_c/top.v", odir + "/top.v") + shutil.copy2("examples/make_hello_c/sim_main.cpp", odir + "/sim_main.cpp") def cleanenv(): for var in os.environ: - if (var == "VERILATOR_ROOT" or var == "VERILATOR_INCLUDE" - or var == "VERILATOR_NO_OPT_BUILD"): + if var in ('VERILATOR_ROOT', 'VERILATOR_INCLUDE', + 'VERILATOR_NO_OPT_BUILD'): print("unset %s # Was '%s'" % (var, os.environ[var])) del os.environ[var] @@ -135,6 +137,6 @@ Args = parser.parse_args() test() ###################################################################### -### Local Variables: -### compile-command: "cd .. ; nodist/install_test" -### End: +# Local Variables: +# compile-command: "cd .. ; nodist/install_test" +# End: diff --git a/src/V3Active.h b/src/V3Active.h index bec002ce1..31fca4e4f 100644 --- a/src/V3Active.h +++ b/src/V3Active.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3ACTIVE_H_ -#define _V3ACTIVE_H_ 1 +#ifndef VERILATOR_V3ACTIVE_H_ +#define VERILATOR_V3ACTIVE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3ActiveTop.h b/src/V3ActiveTop.h index 567d29314..b17427244 100644 --- a/src/V3ActiveTop.h +++ b/src/V3ActiveTop.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3ACTIVETOP_H_ -#define _V3ACTIVETOP_H_ 1 +#ifndef VERILATOR_V3ACTIVETOP_H_ +#define VERILATOR_V3ACTIVETOP_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 8ed6d686a..5ee211b09 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -96,7 +96,8 @@ private: : // If assertions are off, have constant propagation rip them out later // This allows syntax errors and such to be detected normally. (v3Global.opt.assertOn() - ? static_cast(new AstCMath(fl, "Verilated::assertOn()", 1)) + ? static_cast( + new AstCMath(fl, "vlSymsp->_vm_contextp__->assertOn()", 1)) : static_cast(new AstConst(fl, AstConst::BitFalse())))), nodep, nullptr); newp->user1(true); // Don't assert/cover this if diff --git a/src/V3Assert.h b/src/V3Assert.h index 2738ddbf2..29a600628 100644 --- a/src/V3Assert.h +++ b/src/V3Assert.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3ASSERT_H_ -#define _V3ASSERT_H_ 1 +#ifndef VERILATOR_V3ASSERT_H_ +#define VERILATOR_V3ASSERT_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3AssertPre.h b/src/V3AssertPre.h index 0b5acb522..7c277d76e 100644 --- a/src/V3AssertPre.h +++ b/src/V3AssertPre.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3ASSERTPRE_H_ -#define _V3ASSERTPRE_H_ 1 +#ifndef VERILATOR_V3ASSERTPRE_H_ +#define VERILATOR_V3ASSERTPRE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 31f0b4d42..7be1e7a4d 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -120,9 +120,9 @@ string AstNode::encodeName(const string& namein) { // We also do *NOT* use __DOT__ etc, as we search for those // in some replacements, and don't want to mangle the user's names. unsigned val = pos[0] & 0xff; // Mask to avoid sign extension - char hex[10]; - sprintf(hex, "__0%02X", val); - out += hex; + std::stringstream hex; + hex << std::setfill('0') << std::setw(2) << std::hex << val; + out += "__0" + hex.str(); } } // Shorten names diff --git a/src/V3Ast.h b/src/V3Ast.h index 258fb40c5..fa9e03d79 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3AST_H_ -#define _V3AST_H_ 1 +#ifndef VERILATOR_V3AST_H_ +#define VERILATOR_V3AST_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3AstConstOnly.h b/src/V3AstConstOnly.h index c686d429c..58def6b0a 100644 --- a/src/V3AstConstOnly.h +++ b/src/V3AstConstOnly.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3ASTCONSTONLY_H_ -#define _V3ASTCONSTONLY_H_ 1 +#ifndef VERILATOR_V3ASTCONSTONLY_H_ +#define VERILATOR_V3ASTCONSTONLY_H_ // Include only in visitors that do not not edit nodes, so should use constant iterators #define iterateAndNext error_use_iterateAndNextConst diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index efaca0cec..ae7a23f27 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -14,10 +14,10 @@ // //************************************************************************* -#ifndef _V3ASTNODES_H_ -#define _V3ASTNODES_H_ 1 +#ifndef VERILATOR_V3ASTNODES_H_ +#define VERILATOR_V3ASTNODES_H_ -#ifndef _V3AST_H_ +#ifndef VERILATOR_V3AST_H_ #error "Use V3Ast.h as the include" #endif diff --git a/src/V3Begin.h b/src/V3Begin.h index 3962ce5e8..b1ca0c3fe 100644 --- a/src/V3Begin.h +++ b/src/V3Begin.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3BEGIN_H_ -#define _V3BEGIN_H_ 1 +#ifndef VERILATOR_V3BEGIN_H_ +#define VERILATOR_V3BEGIN_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Branch.h b/src/V3Branch.h index fd3a0736f..d693f6488 100644 --- a/src/V3Branch.h +++ b/src/V3Branch.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3BRANCH_H_ -#define _V3BRANCH_H_ 1 +#ifndef VERILATOR_V3BRANCH_H_ +#define VERILATOR_V3BRANCH_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Broken.h b/src/V3Broken.h index 88d87ce3e..16103baa0 100644 --- a/src/V3Broken.h +++ b/src/V3Broken.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3BROKEN_H_ -#define _V3BROKEN_H_ 1 +#ifndef VERILATOR_V3BROKEN_H_ +#define VERILATOR_V3BROKEN_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3CCtors.h b/src/V3CCtors.h index c5590ae52..f48652ed9 100644 --- a/src/V3CCtors.h +++ b/src/V3CCtors.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3CCTORS_H_ -#define _V3CCTORS_H_ 1 +#ifndef VERILATOR_V3CCTORS_H_ +#define VERILATOR_V3CCTORS_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3CUse.h b/src/V3CUse.h index ec5028c37..f2ec4afbb 100644 --- a/src/V3CUse.h +++ b/src/V3CUse.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3CUSE_H_ -#define _V3CUSE_H_ 1 +#ifndef VERILATOR_V3CUSE_H_ +#define VERILATOR_V3CUSE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Case.h b/src/V3Case.h index 001c063ea..0c503d8bc 100644 --- a/src/V3Case.h +++ b/src/V3Case.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3CASE_H_ -#define _V3CASE_H_ 1 +#ifndef VERILATOR_V3CASE_H_ +#define VERILATOR_V3CASE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Cast.h b/src/V3Cast.h index 8d35af3ef..b73cb52da 100644 --- a/src/V3Cast.h +++ b/src/V3Cast.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3CAST_H_ -#define _V3CAST_H_ 1 +#ifndef VERILATOR_V3CAST_H_ +#define VERILATOR_V3CAST_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Cdc.h b/src/V3Cdc.h index 549474d30..cc3470d30 100644 --- a/src/V3Cdc.h +++ b/src/V3Cdc.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3CDC_H_ -#define _V3CDC_H_ 1 +#ifndef VERILATOR_V3CDC_H_ +#define VERILATOR_V3CDC_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Changed.h b/src/V3Changed.h index 2646a1084..48d96a58b 100644 --- a/src/V3Changed.h +++ b/src/V3Changed.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3CHANGED_H_ -#define _V3CHANGED_H_ 1 +#ifndef VERILATOR_V3CHANGED_H_ +#define VERILATOR_V3CHANGED_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Class.h b/src/V3Class.h index d9da9dcb3..10902fe06 100644 --- a/src/V3Class.h +++ b/src/V3Class.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3CLASS_H_ -#define _V3CLASS_H_ 1 +#ifndef VERILATOR_V3CLASS_H_ +#define VERILATOR_V3CLASS_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Clean.h b/src/V3Clean.h index 4ca00a58a..6ba7364ba 100644 --- a/src/V3Clean.h +++ b/src/V3Clean.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3CLEAN_H_ -#define _V3CLEAN_H_ 1 +#ifndef VERILATOR_V3CLEAN_H_ +#define VERILATOR_V3CLEAN_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Clock.h b/src/V3Clock.h index cdda29e18..4186ec1f6 100644 --- a/src/V3Clock.h +++ b/src/V3Clock.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3CLOCK_H_ -#define _V3CLOCK_H_ 1 +#ifndef VERILATOR_V3CLOCK_H_ +#define VERILATOR_V3CLOCK_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Combine.h b/src/V3Combine.h index fb2b99c07..6e510dd05 100644 --- a/src/V3Combine.h +++ b/src/V3Combine.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3COMBINE_H_ -#define _V3COMBINE_H_ 1 +#ifndef VERILATOR_V3COMBINE_H_ +#define VERILATOR_V3COMBINE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Config.h b/src/V3Config.h index c86c60e83..470827fdf 100644 --- a/src/V3Config.h +++ b/src/V3Config.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3CONFIG_H_ -#define _V3CONFIG_H_ 1 +#ifndef VERILATOR_V3CONFIG_H_ +#define VERILATOR_V3CONFIG_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Const.cpp b/src/V3Const.cpp index a8f7b5722..a73085e37 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -104,6 +104,7 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { public: // METHODS bool hasConstantResult() const { return m_constResult >= 0; } + bool sameVarAs(const AstNodeVarRef* otherp) const { return m_refp->sameGateTree(otherp); } void setPolarity(bool compBit, int bit) { UASSERT_OBJ(!hasConstantResult(), m_refp, "Already has result of " << m_constResult); if (m_bitPolarity.bitIsX(bit)) { // The bit is not yet set @@ -222,6 +223,9 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { if (!varInfop) { varInfop = new VarInfo{this, ref.m_refp}; m_varInfos[idx] = varInfop; + } else { + if (!varInfop->sameVarAs(ref.m_refp)) + CONST_BITOP_SET_FAILED("different var (scope?)", ref.m_refp); } return *varInfop; } @@ -687,6 +691,8 @@ private: return true; } bool matchBitOpTree(AstNode* nodep) { + if (!v3Global.opt.oConstBitOpTree()) return false; + AstNode* newp = nullptr; bool tried = false; if (AstAnd* andp = VN_CAST(nodep, And)) { // 1 & BitOpTree @@ -703,14 +709,14 @@ private: } if (newp) { UINFO(4, "Transformed leaf of bit tree to " << newp << std::endl); - if (debug() >= 9) { + if (debug() >= 9) { // LCOV_EXCL_START static int c = 0; std::cout << "Call matchBitOpTree[" << c << "]\n"; nodep->dumpTree(std::cout); std::cout << "\nResult:\n"; newp->dumpTree(std::cout); ++c; - } + } // LCOV_EXCL_STOP nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); } diff --git a/src/V3Const.h b/src/V3Const.h index 6ccd67713..11e50cc03 100644 --- a/src/V3Const.h +++ b/src/V3Const.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3CONST_H_ -#define _V3CONST_H_ 1 +#ifndef VERILATOR_V3CONST_H_ +#define VERILATOR_V3CONST_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Coverage.h b/src/V3Coverage.h index 12c20e76a..1b2cc7c40 100644 --- a/src/V3Coverage.h +++ b/src/V3Coverage.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3COVERAGE_H_ -#define _V3COVERAGE_H_ 1 +#ifndef VERILATOR_V3COVERAGE_H_ +#define VERILATOR_V3COVERAGE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3CoverageJoin.h b/src/V3CoverageJoin.h index ef1a7fc7e..5b52eb3ff 100644 --- a/src/V3CoverageJoin.h +++ b/src/V3CoverageJoin.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3COVERAGEJOIN_H_ -#define _V3COVERAGEJOIN_H_ 1 +#ifndef VERILATOR_V3COVERAGEJOIN_H_ +#define VERILATOR_V3COVERAGEJOIN_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Dead.h b/src/V3Dead.h index c0f656dee..409b7ac52 100644 --- a/src/V3Dead.h +++ b/src/V3Dead.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3DEAD_H_ -#define _V3DEAD_H_ 1 +#ifndef VERILATOR_V3DEAD_H_ +#define VERILATOR_V3DEAD_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Delayed.h b/src/V3Delayed.h index 4e8080bb5..745dbb8c6 100644 --- a/src/V3Delayed.h +++ b/src/V3Delayed.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3DELAYED_H_ -#define _V3DELAYED_H_ 1 +#ifndef VERILATOR_V3DELAYED_H_ +#define VERILATOR_V3DELAYED_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Depth.h b/src/V3Depth.h index 355848da3..09f43c33c 100644 --- a/src/V3Depth.h +++ b/src/V3Depth.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3DEPTH_H_ -#define _V3DEPTH_H_ 1 +#ifndef VERILATOR_V3DEPTH_H_ +#define VERILATOR_V3DEPTH_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3DepthBlock.h b/src/V3DepthBlock.h index 964c91234..bbc04d7a6 100644 --- a/src/V3DepthBlock.h +++ b/src/V3DepthBlock.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3DEPTHBLOCK_H_ -#define _V3DEPTHBLOCK_H_ 1 +#ifndef VERILATOR_V3DEPTHBLOCK_H_ +#define VERILATOR_V3DEPTHBLOCK_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Descope.h b/src/V3Descope.h index 07896ce16..9f3fc6ce6 100644 --- a/src/V3Descope.h +++ b/src/V3Descope.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3DESCOPE_H_ -#define _V3DESCOPE_H_ 1 +#ifndef VERILATOR_V3DESCOPE_H_ +#define VERILATOR_V3DESCOPE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 00fe1eeeb..c82d39dec 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -306,6 +306,7 @@ public: puts("II"); emitIQW(nodep->rhsp()); puts("("); + puts(cvtToStr(selp->fromp()->widthMin()) + ","); puts(cvtToStr(nodep->widthMin()) + ","); iterateAndNextNull(selp->lsbp()); puts(", "); @@ -513,7 +514,7 @@ public: virtual void visit(AstDumpCtl* nodep) override { switch (nodep->ctlType()) { case VDumpCtlType::FILE: - puts("vl_dumpctl_filenamep(true, "); + puts("vlSymsp->_vm_contextp__->dumpfile("); emitCvtPackStr(nodep->exprp()); puts(");\n"); break; @@ -847,7 +848,7 @@ public: putsQuoted(protect(nodep->name())); puts(", "); putsQuoted(nodep->timeunit().ascii()); - puts(");\n"); + puts(", vlSymsp->_vm_contextp__);\n"); } virtual void visit(AstRand* nodep) override { emitOpName(nodep, nodep->emitC(), nodep->seedp(), nullptr, nullptr); @@ -875,7 +876,7 @@ public: emitCvtPackStr(nodep->suffixp()); puts(", "); iterateAndNextNull(nodep->widthp()); - puts(");\n"); + puts(", vlSymsp->_vm_contextp__);\n"); } virtual void visit(AstNodeSimpleText* nodep) override { if (nodep->tracking() || m_trackText) { @@ -1856,6 +1857,7 @@ class EmitCImp final : EmitCStmts { void emitImp(AstNodeModule* modp); void emitSettleLoop(const std::string& eval_call, bool initial); void emitWrapEval(AstNodeModule* modp); + void emitWrapFast(AstNodeModule* modp); void emitMTaskState(); void emitMTaskVertexCtors(bool* firstp); void emitIntTop(AstNodeModule* modp); @@ -2428,11 +2430,15 @@ void EmitCImp::emitCtorImp(AstNodeModule* modp) { modp->v3fatalSrc("constructors should be AstCFuncs instead"); } else if (optSystemC() && modp->isTop()) { puts(prefixNameProtect(modp) + "::" + prefixNameProtect(modp) + "(sc_module_name)"); + } else if (modp->isTop()) { + puts(prefixNameProtect(modp) + "::" + prefixNameProtect(modp) + + "(VerilatedContext* _vcontextp__, const char* _vcname__)\n"); + puts(" : VerilatedModule{_vcname__}\n"); + first = false; // printed the first ':' } else { puts(prefixNameProtect(modp) + "::" + prefixNameProtect(modp) - + "(const char* __VCname)\n"); - puts(" : VerilatedModule(__VCname)\n"); - first = false; // printed the first ':' + + "(const char* _vcname__)\n"); + puts(" : VerilatedModule(_vcname__)\n"); } emitVarCtors(&first); if (modp->isTop() && v3Global.opt.mtasks()) emitMTaskVertexCtors(&first); @@ -2472,8 +2478,8 @@ void EmitCImp::emitCtorImp(AstNodeModule* modp) { // Note we create N-1 threads in the thread pool. The thread // that calls eval() becomes the final Nth thread for the // duration of the eval call. - + cvtToStr(v3Global.opt.threads() - 1) + ", " + cvtToStr(v3Global.opt.profThreads()) - + ");\n"); + + string("vlSymsp->_vm_contextp__, ") + cvtToStr(v3Global.opt.threads() - 1) + ", " + + cvtToStr(v3Global.opt.profThreads()) + ");\n"); if (v3Global.opt.profThreads()) { puts("__Vm_profile_cycle_start = 0;\n"); @@ -2492,12 +2498,12 @@ void EmitCImp::emitConfigureImp(AstNodeModule* modp) { puts("if (false && this->__VlSymsp) {} // Prevent unused\n"); if (v3Global.opt.coverage()) { puts(protect("_configure_coverage") + "(vlSymsp, first);\n"); } if (modp->isTop() && !v3Global.rootp()->timeunit().isNone()) { - puts("Verilated::timeunit(" + cvtToStr(v3Global.rootp()->timeunit().powerOfTen()) - + ");\n"); + puts("vlSymsp->_vm_contextp__->timeunit(" + + cvtToStr(v3Global.rootp()->timeunit().powerOfTen()) + ");\n"); } if (modp->isTop() && !v3Global.rootp()->timeprecision().isNone()) { - puts("Verilated::timeprecision(" + cvtToStr(v3Global.rootp()->timeprecision().powerOfTen()) - + ");\n"); + puts("vlSymsp->_vm_contextp__->timeprecision(" + + cvtToStr(v3Global.rootp()->timeprecision().powerOfTen()) + ");\n"); } puts("}\n"); splitSizeInc(10); @@ -2524,7 +2530,7 @@ void EmitCImp::emitCoverageImp(AstNodeModule*) { // Used for second++ instantiation of identical bin puts("if (!enable) count32p = &fake_zero_count;\n"); puts("*count32p = 0;\n"); - puts("VL_COVER_INSERT(count32p,"); + puts("VL_COVER_INSERT(__VlSymsp->_vm_contextp__->coveragep(), count32p,"); puts(" \"filename\",filenamep,"); puts(" \"lineno\",lineno,"); puts(" \"column\",column,\n"); @@ -2584,9 +2590,15 @@ void EmitCImp::emitSavableImp(AstNodeModule* modp) { if (de) { puts("os.readAssert(__Vcheckval);\n"); } else { - puts("os<<__Vcheckval;\n"); + puts("os << __Vcheckval;\n"); } + // Save context + // If multiple models save the same context we'll save it multiple + // times, but is harmless, and doing it otherwise would break + // backwards compatibility. + puts("os " + op + " __VlSymsp->_vm_contextp__;\n"); + // Save all members if (v3Global.opt.inhibitSim()) puts("os" + op + "__Vm_inhibitSim;\n"); for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { @@ -2661,8 +2673,9 @@ void EmitCImp::emitTextSection(AstType type) { void EmitCImp::emitCellCtors(AstNodeModule* modp) { if (modp->isTop()) { // Must be before other constructors, as __vlCoverInsert calls it - puts(EmitCBaseVisitor::symClassVar() + " = __VlSymsp = new " + symClassName() - + "(this, name());\n"); + // Note _vcontextp__ may be nullptr, VerilatedSyms::VerilatedSyms cleans it up + puts(EmitCBaseVisitor::symClassVar() + " = __VlSymsp = new " + symClassName() + "(" + + (optSystemC() ? "nullptr" : "_vcontextp__") + ", this, name());\n"); puts(EmitCBaseVisitor::symTopAssign() + "\n"); } for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { @@ -2735,6 +2748,12 @@ void EmitCImp::emitSettleLoop(const std::string& eval_call, bool initial) { puts("} while (VL_UNLIKELY(__Vchange));\n"); } +void EmitCImp::emitWrapFast(AstNodeModule* modp) { + puts("\nVerilatedContext* " + prefixNameProtect(modp) + "::contextp() {\n"); + puts(/**/ "return __VlSymsp->_vm_contextp__;\n"); + puts("}\n"); +} + void EmitCImp::emitWrapEval(AstNodeModule* modp) { puts("\nvoid " + prefixNameProtect(modp) + "::eval_step() {\n"); puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+++++TOP Evaluate " + prefixNameProtect(modp) @@ -2758,9 +2777,10 @@ void EmitCImp::emitWrapEval(AstNodeModule* modp) { } if (v3Global.opt.mtasks() && v3Global.opt.profThreads()) { - puts("if (VL_UNLIKELY((Verilated::profThreadsStart() != __Vm_profile_time_finished)\n"); - puts(" && (VL_TIME_Q() > Verilated::profThreadsStart())\n"); - puts(" && (Verilated::profThreadsWindow() >= 1))) {\n"); + puts("if (VL_UNLIKELY((vlSymsp->_vm_contextp__->profThreadsStart() != " + "__Vm_profile_time_finished)\n"); + puts(" && (VL_TIME_Q() > vlSymsp->_vm_contextp__->profThreadsStart())\n"); + puts(" && (vlSymsp->_vm_contextp__->profThreadsWindow() >= 1))) {\n"); // Within a profile (either starting, middle, or end) puts("if (vlTOPp->__Vm_profile_window_ct == 0) {\n"); // Opening file? // Start profile on this cycle. We'll capture a window worth, then @@ -2769,10 +2789,12 @@ void EmitCImp::emitWrapEval(AstNodeModule* modp) { // by the time we hit the second window, we hope. puts("vlTOPp->__Vm_profile_cycle_start = VL_RDTSC_Q();\n"); // "* 2" as first half is warmup, second half is collection - puts("vlTOPp->__Vm_profile_window_ct = Verilated::profThreadsWindow() * 2 + 1;\n"); + puts("vlTOPp->__Vm_profile_window_ct = vlSymsp->_vm_contextp__->profThreadsWindow() * 2 + " + "1;\n"); puts("}\n"); puts("--vlTOPp->__Vm_profile_window_ct;\n"); - puts("if (vlTOPp->__Vm_profile_window_ct == (Verilated::profThreadsWindow())) {\n"); + puts("if (vlTOPp->__Vm_profile_window_ct == " + "(vlSymsp->_vm_contextp__->profThreadsWindow())) {\n"); // This barrier record in every threads' profile demarcates the // cache-warm-up cycles before the barrier from the actual profile // cycles afterward. @@ -2783,12 +2805,13 @@ void EmitCImp::emitWrapEval(AstNodeModule* modp) { puts("else if (vlTOPp->__Vm_profile_window_ct == 0) {\n"); // Ending file. puts("vluint64_t elapsed = VL_RDTSC_Q() - vlTOPp->__Vm_profile_cycle_start;\n"); - puts("vlTOPp->__Vm_threadPoolp->profileDump(Verilated::profThreadsFilenamep(), " - "elapsed);\n"); + puts( + "vlTOPp->__Vm_threadPoolp->profileDump(vlSymsp->_vm_contextp__->profThreadsFilename()." + "c_str(), elapsed);\n"); // This turns off the test to enter the profiling code, but still // allows the user to collect another profile by changing // profThreadsStart - puts("__Vm_profile_time_finished = Verilated::profThreadsStart();\n"); + puts("__Vm_profile_time_finished = vlSymsp->_vm_contextp__->profThreadsStart();\n"); puts("vlTOPp->__Vm_profile_cycle_start = 0;\n"); puts("}\n"); puts("}\n"); @@ -3192,21 +3215,27 @@ void EmitCImp::emitInt(AstNodeModule* modp) { ofp()->putsPrivate(false); // public: if (modp->isTop()) { puts("/// Construct the model; called by application code\n"); - puts("/// The special name " - " may be used to make a wrapper with a\n"); + puts("/// If contextp is null, then the model will use the default global context\n"); + puts("/// If name is \"\", then makes a wrapper with a\n"); puts("/// single model invisible with respect to DPI scope names.\n"); - } - if (VN_IS(modp, Class)) { - // TODO move all constructor definition to e.g. V3CUse - puts(prefixNameProtect(modp) + "();\n"); + puts(prefixNameProtect(modp) + "(VerilatedContext* contextp," + + " const char* name = \"TOP\");\n"); + puts(prefixNameProtect(modp) + "(const char* name = \"TOP\")\n"); + puts(" : " + prefixNameProtect(modp) + "(nullptr, name) {}\n"); } else { - puts(prefixNameProtect(modp) + "(const char* name = \"TOP\");\n"); + if (VN_IS(modp, Class)) { + // TODO move all constructor definition to e.g. V3CUse + puts(prefixNameProtect(modp) + "();\n"); + } else { + puts(prefixNameProtect(modp) + "(const char* name = \"TOP\");\n"); + } } if (modp->isTop()) { puts("/// Destroy the model; called (often implicitly) by application code\n"); } puts("~" + prefixNameProtect(modp) + "();\n"); } + if (v3Global.opt.trace() && modp->isTop()) { puts("/// Trace signals in the model; called by application code\n"); puts("void trace(" + v3Global.opt.traceClassBase() @@ -3222,6 +3251,10 @@ void EmitCImp::emitInt(AstNodeModule* modp) { if (modp->isTop()) { puts("\n// API METHODS\n"); + puts("/// Return current simulation context for this model.\n"); + puts("/// Used to get to e.g. simulation time via contextp()->time()\n"); + puts("VerilatedContext* contextp();\n"); + string callEvalEndStep = (v3Global.needTraceDumper() && !optSystemC()) ? "eval_end_step(); " : ""; if (optSystemC()) { @@ -3339,7 +3372,10 @@ void EmitCImp::emitImp(AstNodeModule* modp) { if (m_fast) { emitTextSection(AstType::atScImp); - if (modp->isTop()) emitWrapEval(modp); + if (modp->isTop()) { + emitWrapFast(modp); + emitWrapEval(modp); + } } // Blocks @@ -3493,10 +3529,9 @@ class EmitCTrace final : EmitCStmts { puts("const VerilatedLockGuard lock(__VlSymsp->__Vm_dumperMutex);\n"); puts("if (VL_UNLIKELY(!__VlSymsp->__Vm_dumperp)) {\n"); puts("__VlSymsp->__Vm_dumperp = new " + v3Global.opt.traceClassLang() + "();\n"); - puts("const char* cp = vl_dumpctl_filenamep();\n"); puts("trace(__VlSymsp->__Vm_dumperp, 0, 0);\n"); - puts("__VlSymsp->__Vm_dumperp->open(vl_dumpctl_filenamep());\n"); - puts("__VlSymsp->__Vm_dumperp->changeThread();\n"); + puts("std::string dumpfile = __VlSymsp->_vm_contextp__->dumpfile();\n"); + puts("__VlSymsp->__Vm_dumperp->open(dumpfile.c_str());\n"); puts("__VlSymsp->__Vm_dumping = true;\n"); puts("}\n"); puts("}\n"); @@ -3523,7 +3558,7 @@ class EmitCTrace final : EmitCStmts { + v3Global.opt.traceClassBase() + "* tracep, uint32_t code) {\n"); putsDecoration("// Callback from tracep->open()\n"); puts(symClassVar() + " = static_cast<" + symClassName() + "*>(userp);\n"); - puts("if (!Verilated::calcUnusedSigs()) {\n"); + puts("if (!vlSymsp->_vm_contextp__->calcUnusedSigs()) {\n"); puts("VL_FATAL_MT(__FILE__, __LINE__, __FILE__,\n"); puts(" \"Turning on wave traces requires Verilated::traceEverOn(true) call " "before time 0.\");\n"); diff --git a/src/V3EmitC.h b/src/V3EmitC.h index fb75a1626..0e4207c6a 100644 --- a/src/V3EmitC.h +++ b/src/V3EmitC.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3EMITC_H_ -#define _V3EMITC_H_ 1 +#ifndef VERILATOR_V3EMITC_H_ +#define VERILATOR_V3EMITC_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index cc75859f9..6b448916a 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3EMITCBASE_H_ -#define _V3EMITCBASE_H_ 1 +#ifndef VERILATOR_V3EMITCBASE_H_ +#define VERILATOR_V3EMITCBASE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3EmitCMain.cpp b/src/V3EmitCMain.cpp index 768694416..a7260fb8f 100644 --- a/src/V3EmitCMain.cpp +++ b/src/V3EmitCMain.cpp @@ -45,6 +45,9 @@ private: V3OutCFile cf(filename); m_ofp = &cf; + // Not defining main_time/vl_time_stamp, so + v3Global.opt.addCFlags("-DVL_TIME_CONTEXT"); // On MSVC++ anyways + // Heavly 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"); @@ -55,20 +58,16 @@ private: puts("\n//======================\n\n"); - puts("// Requires -DVL_TIME_STAMP64\n"); - v3Global.opt.addCFlags("-DVL_TIME_STAMP64"); - puts("vluint64_t main_time = 0;\n"); - puts("vluint64_t vl_time_stamp64() { return main_time; }\n"); - puts("\n"); - puts("int main(int argc, char** argv, char**) {\n"); - puts("// Setup defaults and parse command line\n"); + puts("// Setup context, defaults, and parse command line\n"); puts("Verilated::debug(0);\n"); - puts("Verilated::commandArgs(argc, argv);\n"); + puts("const std::unique_ptr contextp{new VerilatedContext};\n"); + puts("contextp->commandArgs(argc, argv);\n"); puts("\n"); puts("// Construct the Verilated model, from Vtop.h generated from Verilating\n"); - puts("const std::unique_ptr<" + topClassName() + "> topp{new " + topClassName() + "};\n"); + puts("const std::unique_ptr<" + topClassName() + "> topp{new " + topClassName() + + "{contextp.get()}};\n"); puts("\n"); puts("// Evaluate initials\n"); @@ -76,15 +75,16 @@ private: puts("\n"); puts("// Simulate until $finish\n"); - puts("while (!Verilated::gotFinish()) {\n"); + puts("while (!contextp->gotFinish()) {\n"); puts(/**/ "// Evaluate model\n"); puts(/**/ "topp->eval();\n"); puts(/**/ "// Advance time\n"); - puts(/**/ "++main_time;\n"); + puts(/**/ "contextp->timeInc(1);\n"); + puts("}\n"); puts("\n"); - puts("if (!Verilated::gotFinish()) {\n"); + puts("if (!contextp->gotFinish()) {\n"); puts(/**/ "VL_DEBUG_IF(VL_PRINTF(\"+ Exiting without $finish; no events left\\n\"););\n"); puts("}\n"); puts("\n"); diff --git a/src/V3EmitCMain.h b/src/V3EmitCMain.h index d5232baaa..e9f936ea5 100644 --- a/src/V3EmitCMain.h +++ b/src/V3EmitCMain.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3EMITCMAIN_H_ -#define _V3EMITCMAIN_H_ 1 +#ifndef VERILATOR_V3EMITCMAIN_H_ +#define VERILATOR_V3EMITCMAIN_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3EmitCMake.h b/src/V3EmitCMake.h index 3401887f4..d62d73d42 100644 --- a/src/V3EmitCMake.h +++ b/src/V3EmitCMake.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3EMITCMAKE_H_ -#define _V3EMITCMAKE_H_ 1 +#ifndef VERILATOR_V3EMITCMAKE_H_ +#define VERILATOR_V3EMITCMAKE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index d9c0882b0..352b3292b 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -467,7 +467,8 @@ void EmitCSyms::emitSymHdr() { } puts("\n// CREATORS\n"); - puts(symClassName() + "(" + topClassName() + "* topp, const char* namep);\n"); + puts(symClassName() + "(VerilatedContext* contextp, " + topClassName() + + "* topp, const char* namep);\n"); puts(string("~") + symClassName() + "();\n"); for (const auto& i : m_usesVfinal) { @@ -643,10 +644,11 @@ void EmitCSyms::emitSymImp() { puts("{\n"); emitScopeHier(true); puts("}\n\n"); - puts(symClassName() + "::" + symClassName() + "(" + topClassName() + puts(symClassName() + "::" + symClassName() + "(VerilatedContext* contextp, " + topClassName() + "* topp, const char* namep)\n"); puts(" // Setup locals\n"); - puts(" : __Vm_namep(namep)\n"); // No leak, as gets destroyed when the top is destroyed + puts(" : VerilatedSyms{contextp}\n"); + puts(" , __Vm_namep(namep)\n"); // No leak, as gets destroyed when the top is destroyed if (v3Global.needTraceDumper()) { puts(" , __Vm_dumping(false)\n"); puts(" , __Vm_dumperp(nullptr)\n"); @@ -922,8 +924,9 @@ void EmitCSyms::emitDpiImp() { for (AstCFunc* nodep : m_dpis) { if (nodep->dpiExportWrapper()) { - puts("#ifndef _VL_DPIDECL_" + nodep->name() + "\n"); - puts("#define _VL_DPIDECL_" + nodep->name() + "\n"); + // Prevent multi-definition if used by multiple models + puts("#ifndef VL_DPIDECL_" + nodep->name() + "_\n"); + puts("#define VL_DPIDECL_" + nodep->name() + "_\n"); puts(nodep->rtnTypeVoid() + " " + nodep->name() + "(" + cFuncArgs(nodep) + ") {\n"); puts("// DPI export" + ifNoProtect(" at " + nodep->fileline()->ascii()) + "\n"); puts("return " + topClassName() + "::" + nodep->name() + "("); diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index ab5ebabc2..6d090de60 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -167,7 +167,7 @@ public: of.puts(string("SYSTEMC_LIBDIR ?= ") + V3Options::getenvSYSTEMC_LIBDIR() + "\n"); // Only check it if we really need the value - if (v3Global.opt.usingSystemCLibs() && !V3Options::systemCFound()) { + if (v3Global.opt.systemC() && !V3Options::systemCFound()) { v3fatal("Need $SYSTEMC_INCLUDE in environment or when Verilator configured,\n" "and need $SYSTEMC_LIBDIR in environment or when Verilator configured\n" "Probably System-C isn't installed, see http://www.systemc.org\n"); diff --git a/src/V3EmitMk.h b/src/V3EmitMk.h index c9208059d..26a01e786 100644 --- a/src/V3EmitMk.h +++ b/src/V3EmitMk.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3EMITMK_H_ -#define _V3EMITMK_H_ 1 +#ifndef VERILATOR_V3EMITMK_H_ +#define VERILATOR_V3EMITMK_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 238341569..a79cb32d4 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -310,13 +310,15 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { } virtual void visit(AstNodeFor* nodep) override { putfs(nodep, "for ("); - m_suppressSemi = true; - iterateAndNextNull(nodep->initsp()); - puts(";"); - iterateAndNextNull(nodep->condp()); - puts(";"); - iterateAndNextNull(nodep->incsp()); - m_suppressSemi = false; + { + VL_RESTORER(m_suppressSemi); + m_suppressSemi = true; + iterateAndNextNull(nodep->initsp()); + puts(";"); + iterateAndNextNull(nodep->condp()); + puts(";"); + iterateAndNextNull(nodep->incsp()); + } puts(") begin\n"); iterateAndNextNull(nodep->bodysp()); putqs(nodep, "end\n"); @@ -381,9 +383,13 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { } virtual void visit(AstTextBlock* nodep) override { visit(VN_CAST(nodep, NodeSimpleText)); - for (AstNode* childp = nodep->nodesp(); childp; childp = childp->nextp()) { - iterate(childp); - if (nodep->commas() && childp->nextp()) puts(", "); + { + VL_RESTORER(m_suppressSemi); + m_suppressVarSemi = nodep->commas(); + for (AstNode* childp = nodep->nodesp(); childp; childp = childp->nextp()) { + iterate(childp); + if (nodep->commas() && childp->nextp()) puts(", "); + } } } virtual void visit(AstScopeName* nodep) override {} @@ -659,11 +665,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { puts(cvtToStr(unpackp->rangep()->rightConst())); puts("]"); } - if (!m_suppressVarSemi) { - puts(";\n"); - } else { - puts("\n"); - } + puts(m_suppressVarSemi ? "\n" : ";\n"); } virtual void visit(AstActive* nodep) override { m_sensesp = nodep->sensesp(); @@ -711,12 +713,10 @@ class EmitVFileVisitor final : public EmitVBaseVisitor { virtual void putsNoTracking(const string& str) override { ofp()->putsNoTracking(str); } public: - EmitVFileVisitor(AstNode* nodep, V3OutFile* ofp, bool trackText, bool suppressVarSemi, - bool suppressUnknown) + EmitVFileVisitor(AstNode* nodep, V3OutFile* ofp, bool trackText, bool suppressUnknown) : EmitVBaseVisitor{suppressUnknown, nullptr} { m_ofp = ofp; m_trackText = trackText; - m_suppressVarSemi = suppressVarSemi; iterate(nodep); } virtual ~EmitVFileVisitor() override = default; @@ -840,7 +840,7 @@ void V3EmitV::emitvFiles() { V3OutVFile of(vfilep->name()); of.puts("// DESCR" "IPTION: Verilator generated Verilog\n"); - EmitVFileVisitor visitor(vfilep->tblockp(), &of, true, true, false); + EmitVFileVisitor visitor(vfilep->tblockp(), &of, true, false); } } } @@ -849,5 +849,5 @@ void V3EmitV::debugEmitV(const string& stage) { UINFO(2, __FUNCTION__ << ": " << endl); string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__" + stage + ".v"; V3OutVFile of(filename); - EmitVFileVisitor visitor(v3Global.rootp(), &of, true, false, true); + EmitVFileVisitor visitor(v3Global.rootp(), &of, true, true); } diff --git a/src/V3EmitV.h b/src/V3EmitV.h index 17cb790ad..8c3008b67 100644 --- a/src/V3EmitV.h +++ b/src/V3EmitV.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3EMITV_H_ -#define _V3EMITV_H_ 1 +#ifndef VERILATOR_V3EMITV_H_ +#define VERILATOR_V3EMITV_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3EmitXml.h b/src/V3EmitXml.h index e1b790736..a1ac36b1f 100644 --- a/src/V3EmitXml.h +++ b/src/V3EmitXml.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3EMITXML_H_ -#define _V3EMITXML_H_ 1 +#ifndef VERILATOR_V3EMITXML_H_ +#define VERILATOR_V3EMITXML_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Error.cpp b/src/V3Error.cpp index 95a34a7f0..b8c0e6484 100644 --- a/src/V3Error.cpp +++ b/src/V3Error.cpp @@ -16,7 +16,7 @@ // clang-format off #include "V3Error.h" -#ifndef _V3ERROR_NO_GLOBAL_ +#ifndef V3ERROR_NO_GLOBAL_ # include "V3Ast.h" # include "V3Global.h" # include "V3Stats.h" @@ -174,7 +174,7 @@ void V3Error::vlAbort() { // Global Functions void V3Error::suppressThisWarning() { -#ifndef _V3ERROR_NO_GLOBAL_ +#ifndef V3ERROR_NO_GLOBAL_ V3Stats::addStatSum(string("Warnings, Suppressed ") + s_errorCode.ascii(), 1); #endif s_errorSuppressed = true; @@ -215,7 +215,7 @@ void V3Error::v3errorEnd(std::ostringstream& sstr, const string& locationStr) { } // Output if ( -#ifndef _V3ERROR_NO_GLOBAL_ +#ifndef V3ERROR_NO_GLOBAL_ !(v3Global.opt.quietExit() && s_errorCode == V3ErrorCode::EC_FATALEXIT) #else true @@ -265,7 +265,7 @@ void V3Error::v3errorEnd(std::ostringstream& sstr, const string& locationStr) { << endl; s_tellManual = 2; } -#ifndef _V3ERROR_NO_GLOBAL_ +#ifndef V3ERROR_NO_GLOBAL_ if (debug()) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("final.tree", 990)); if (s_errorExitCb) s_errorExitCb(); diff --git a/src/V3Error.h b/src/V3Error.h index 374bc4415..89e9878c1 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3ERROR_H_ -#define _V3ERROR_H_ 1 +#ifndef VERILATOR_V3ERROR_H_ +#define VERILATOR_V3ERROR_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Expand.cpp b/src/V3Expand.cpp index 541069ee4..2ec552cfe 100644 --- a/src/V3Expand.cpp +++ b/src/V3Expand.cpp @@ -488,9 +488,21 @@ private: maskold.edataWord(w)), oldvalp); } - addWordAssign(nodep, w, destp, - new AstOr(lhsp->fileline(), oldvalp, - newWordGrabShift(lhsp->fileline(), w, rhsp, lsb))); + + // Appropriate word of new value to insert: + AstNode* newp = newWordGrabShift(lhsp->fileline(), w, rhsp, lsb); + + // Apply cleaning at the top word of the destination + // (no cleaning to do if dst's width is a whole number + // of words). + if (w == destp->widthWords() - 1 && VL_BITBIT_E(destp->widthMin()) != 0) { + V3Number cleanmask(nodep, VL_EDATASIZE); + cleanmask.setMask(VL_BITBIT_E(destp->widthMin())); + newp = new AstAnd(lhsp->fileline(), newp, + new AstConst(lhsp->fileline(), cleanmask)); + } + + addWordAssign(nodep, w, destp, new AstOr(lhsp->fileline(), oldvalp, newp)); } } VL_DO_DANGLING(rhsp->deleteTree(), rhsp); @@ -506,15 +518,22 @@ private: oldvalp = new AstAnd(lhsp->fileline(), new AstConst(lhsp->fileline(), maskold), oldvalp); } - AstNode* newp = new AstOr(lhsp->fileline(), oldvalp, - new AstShiftL(lhsp->fileline(), rhsp, - new AstConst(lhsp->fileline(), lsb), - destp->width())); + + // The bit-select can refer to bits outside the width of nodep + // which we aren't allowed to assign to. This is a mask of the + // valid range of nodep which we apply to the new shifted RHS. + V3Number cleanmask(nodep, destp->widthMin()); + cleanmask.setMask(destp->widthMin()); + AstNode* shifted = new AstShiftL( + lhsp->fileline(), rhsp, new AstConst(lhsp->fileline(), lsb), destp->width()); + AstNode* cleaned = new AstAnd(lhsp->fileline(), shifted, + new AstConst(lhsp->fileline(), cleanmask)); + AstNode* newp = new AstOr(lhsp->fileline(), oldvalp, cleaned); newp = new AstAssign(nodep->fileline(), destp, newp); insertBefore(nodep, newp); } return true; - } else { // non-const RHS + } else { // non-const select offset if (destwide && lhsp->widthConst() == 1) { UINFO(8, " ASSIGNSEL(varlsb,wide,1bit) " << nodep << endl); AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); @@ -579,11 +598,21 @@ private: lhsp->lsbp()->cloneTree(true), destp->width())), oldvalp); } - AstNode* newp - = new AstOr(lhsp->fileline(), oldvalp, - new AstShiftL(lhsp->fileline(), rhsp, - lhsp->lsbp()->cloneTree(true), destp->width())); - newp = new AstAssign(nodep->fileline(), destp, newp); + AstNode* newp = new AstShiftL(lhsp->fileline(), rhsp, + lhsp->lsbp()->cloneTree(true), destp->width()); + // Apply cleaning to the new value being inserted. Mask is + // slightly wider than necessary to avoid an AND with all ones + // being optimized out. No need to clean if destp is + // quad-sized as there are no extra bits to contaminate + if (destp->widthMin() != 64) { + V3Number cleanmask(nodep, destp->widthMin() + 1); + cleanmask.setMask(destp->widthMin()); + newp = new AstAnd(lhsp->fileline(), newp, + new AstConst(lhsp->fileline(), cleanmask)); + } + + newp = new AstAssign(nodep->fileline(), destp, + new AstOr(lhsp->fileline(), oldvalp, newp)); // newp->dumpTree(cout, "- new: "); insertBefore(nodep, newp); return true; diff --git a/src/V3Expand.h b/src/V3Expand.h index d405479fa..5ea9e9a3d 100644 --- a/src/V3Expand.h +++ b/src/V3Expand.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3EXPAND_H_ -#define _V3EXPAND_H_ 1 +#ifndef VERILATOR_V3EXPAND_H_ +#define VERILATOR_V3EXPAND_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3File.cpp b/src/V3File.cpp index 9b39bd425..fbcd02eac 100644 --- a/src/V3File.cpp +++ b/src/V3File.cpp @@ -873,9 +873,7 @@ string V3OutFormatter::quoteNameControls(const string& namein, V3OutFormatter::L } else if (isprint(c)) { out += c; } else { - char decimal[10]; - sprintf(decimal, "&#%u;", (unsigned char)c); - out += decimal; + out += string("&#") + cvtToStr((unsigned int)(c & 0xff)) + ";"; } } } else { @@ -893,9 +891,8 @@ string V3OutFormatter::quoteNameControls(const string& namein, V3OutFormatter::L out += c; } else { // This will also cover \a etc - // Can't use %03o as messes up when signed - char octal[10]; - sprintf(octal, "\\%o%o%o", (c >> 6) & 3, (c >> 3) & 7, c & 7); + string octal = string("\\") + cvtToStr((c >> 6) & 3) + cvtToStr((c >> 3) & 7) + + cvtToStr(c & 7); out += octal; } } @@ -907,10 +904,11 @@ string V3OutFormatter::quoteNameControls(const string& namein, V3OutFormatter::L // Simple wrappers void V3OutFormatter::printf(const char* fmt...) { - char sbuff[5000]; + constexpr size_t bufsize = 5000; + char sbuff[bufsize]; va_list ap; va_start(ap, fmt); - vsprintf(sbuff, fmt, ap); + VL_VSNPRINTF(sbuff, bufsize, fmt, ap); va_end(ap); this->puts(sbuff); } @@ -938,7 +936,7 @@ void V3OutFile::putsForceIncs() { void V3OutCFile::putsGuard() { UASSERT(!m_guard, "Already called putsGuard in emit file"); m_guard = true; - string var = VString::upcase(string("_") + V3Os::filenameNonDir(filename()) + "_"); + string var = VString::upcase(string("VERILATED_") + V3Os::filenameNonDir(filename()) + "_"); for (char& c : var) { if (!isalnum(c)) c = '_'; } diff --git a/src/V3File.h b/src/V3File.h index c94404224..35df9efbe 100644 --- a/src/V3File.h +++ b/src/V3File.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3FILE_H_ -#define _V3FILE_H_ 1 +#ifndef VERILATOR_V3FILE_H_ +#define VERILATOR_V3FILE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3FileLine.cpp b/src/V3FileLine.cpp index 272277948..6f8f5073b 100644 --- a/src/V3FileLine.cpp +++ b/src/V3FileLine.cpp @@ -21,7 +21,7 @@ #include "V3Error.h" #include "V3FileLine.h" #include "V3String.h" -#ifndef _V3ERROR_NO_GLOBAL_ +#ifndef V3ERROR_NO_GLOBAL_ # include "V3Global.h" # include "V3Config.h" # include "V3File.h" @@ -165,11 +165,8 @@ string FileLine::xmlDetailedLocation() const { } string FileLine::lineDirectiveStrg(int enterExit) const { - char numbuf[20]; - sprintf(numbuf, "%d", lastLineno()); - char levelbuf[20]; - sprintf(levelbuf, "%d", enterExit); - return (string("`line ") + numbuf + " \"" + filename() + "\" " + levelbuf + "\n"); + return std::string("`line ") + cvtToStr(lastLineno()) + " \"" + filename() + "\" " + + cvtToStr(enterExit) + "\n"; } void FileLine::lineDirective(const char* textp, int& enterExitRef) { @@ -228,7 +225,7 @@ void FileLine::forwardToken(const char* textp, size_t size, bool trackLines) { if (*sp == '\n') { if (trackLines) linenoInc(); m_lastColumn = 1; - } else if (*sp == '\r') { + } else if (VL_UNCOVERABLE(*sp == '\r')) { // Generally stripped by preproc } else { // Tabs are considered one column; hence column means number of chars ++m_lastColumn; } @@ -240,7 +237,7 @@ FileLine* FileLine::copyOrSameFileLine() { // Return this, or a copy of this // There are often more than one token per line, thus we use the // same pointer as long as we're on the same line, file & warn state. -#ifndef _V3ERROR_NO_GLOBAL_ +#ifndef V3ERROR_NO_GLOBAL_ V3Config::applyIgnores(this); // Toggle warnings based on global config file #endif static FileLine* lastNewp = nullptr; diff --git a/src/V3FileLine.h b/src/V3FileLine.h index bbc0c2de2..983554aaf 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3FILELINE_H_ -#define _V3FILELINE_H_ 1 +#ifndef VERILATOR_V3FILELINE_H_ +#define VERILATOR_V3FILELINE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Gate.h b/src/V3Gate.h index 1cc55cc9b..4aa2d351e 100644 --- a/src/V3Gate.h +++ b/src/V3Gate.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3GATE_H_ -#define _V3GATE_H_ 1 +#ifndef VERILATOR_V3GATE_H_ +#define VERILATOR_V3GATE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3GenClk.h b/src/V3GenClk.h index 031f76b3c..3ac306bc5 100644 --- a/src/V3GenClk.h +++ b/src/V3GenClk.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3GENCLK_H_ -#define _V3GENCLK_H_ 1 +#ifndef VERILATOR_V3GENCLK_H_ +#define VERILATOR_V3GENCLK_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Global.cpp b/src/V3Global.cpp index 73115a7b3..73e78711a 100644 --- a/src/V3Global.cpp +++ b/src/V3Global.cpp @@ -80,6 +80,18 @@ void V3Global::readFiles() { } } +string V3Global::debugFilename(const string& nameComment, int newNumber) { + ++m_debugFileNumber; + if (newNumber) m_debugFileNumber = newNumber; + return opt.hierTopDataDir() + "/" + opt.prefix() + "_" + digitsFilename(m_debugFileNumber) + + "_" + nameComment; +} +string V3Global::digitsFilename(int number) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(3) << number; + return ss.str(); +} + void V3Global::dumpCheckGlobalTree(const string& stagename, int newNumber, bool doDump) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename(stagename + ".tree", newNumber), false, doDump); diff --git a/src/V3Global.h b/src/V3Global.h index a8da68308..693b213a3 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3GLOBAL_H_ -#define _V3GLOBAL_H_ 1 +#ifndef VERILATOR_V3GLOBAL_H_ +#define VERILATOR_V3GLOBAL_H_ // clang-format off #include "config_build.h" @@ -140,13 +140,8 @@ public: void widthMinUsage(const VWidthMinUsage& flag) { m_widthMinUsage = flag; } bool constRemoveXs() const { return m_constRemoveXs; } void constRemoveXs(bool flag) { m_constRemoveXs = flag; } - string debugFilename(const string& nameComment, int newNumber = 0) { - ++m_debugFileNumber; - if (newNumber) m_debugFileNumber = newNumber; - char digits[100]; - sprintf(digits, "%03d", m_debugFileNumber); - return opt.hierTopDataDir() + "/" + opt.prefix() + "_" + digits + "_" + nameComment; - } + string debugFilename(const string& nameComment, int newNumber = 0); + static string digitsFilename(int number); bool needHeavy() const { return m_needHeavy; } void needHeavy(bool flag) { m_needHeavy = flag; } bool needTraceDumper() const { return m_needTraceDumper; } diff --git a/src/V3Graph.h b/src/V3Graph.h index a12ce811f..399a93982 100644 --- a/src/V3Graph.h +++ b/src/V3Graph.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3GRAPH_H_ -#define _V3GRAPH_H_ 1 +#ifndef VERILATOR_V3GRAPH_H_ +#define VERILATOR_V3GRAPH_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3GraphAlg.h b/src/V3GraphAlg.h index f3003f08a..f87936830 100644 --- a/src/V3GraphAlg.h +++ b/src/V3GraphAlg.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3GRAPHALG_H_ -#define _V3GRAPHALG_H_ 1 +#ifndef VERILATOR_V3GRAPHALG_H_ +#define VERILATOR_V3GRAPHALG_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3GraphDfa.h b/src/V3GraphDfa.h index 26f31ef01..1a102bf47 100644 --- a/src/V3GraphDfa.h +++ b/src/V3GraphDfa.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3GRAPHDFA_H_ -#define _V3GRAPHDFA_H_ 1 +#ifndef VERILATOR_V3GRAPHDFA_H_ +#define VERILATOR_V3GRAPHDFA_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3GraphPathChecker.h b/src/V3GraphPathChecker.h index b239a1bca..16eda4764 100644 --- a/src/V3GraphPathChecker.h +++ b/src/V3GraphPathChecker.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3GRAPHPATHCHECKER_H_ -#define _V3GRAPHPATHCHECKER_H_ +#ifndef VERILATOR_V3GRAPHPATHCHECKER_H_ +#define VERILATOR_V3GRAPHPATHCHECKER_H_ #include "V3Error.h" #include "V3Graph.h" diff --git a/src/V3GraphStream.h b/src/V3GraphStream.h index b58ea458a..84c6c3eec 100644 --- a/src/V3GraphStream.h +++ b/src/V3GraphStream.h @@ -15,8 +15,8 @@ // //************************************************************************* -#ifndef _V3GRAPHSTREAM_H_ -#define _V3GRAPHSTREAM_H_ +#ifndef VERILATOR_V3GRAPHSTREAM_H_ +#define VERILATOR_V3GRAPHSTREAM_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Hashed.h b/src/V3Hashed.h index bad5c246c..9ee62f099 100644 --- a/src/V3Hashed.h +++ b/src/V3Hashed.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3HASHED_H_ -#define _V3HASHED_H_ 1 +#ifndef VERILATOR_V3HASHED_H_ +#define VERILATOR_V3HASHED_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3HierBlock.h b/src/V3HierBlock.h index e3dda62ae..7a7044b25 100644 --- a/src/V3HierBlock.h +++ b/src/V3HierBlock.h @@ -18,8 +18,8 @@ // //************************************************************************* -#ifndef _V3HIERBLOCK_H_ -#define _V3HIERBLOCK_H_ 1 +#ifndef VERILATOR_V3HIERBLOCK_H_ +#define VERILATOR_V3HIERBLOCK_H_ #include "verilatedos.h" diff --git a/src/V3Inline.h b/src/V3Inline.h index 44bbfc327..495870bc0 100644 --- a/src/V3Inline.h +++ b/src/V3Inline.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3INLINE_H_ -#define _V3INLINE_H_ 1 +#ifndef VERILATOR_V3INLINE_H_ +#define VERILATOR_V3INLINE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index 055c710ad..fb4d3625d 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -396,7 +396,7 @@ private: AstNode* prevPinp = nullptr; // Clone the var referenced by the pin, and clone each var referenced by the varref // Clone pin varp: - for (int in = 0; in < pinArrp->elementsConst(); ++in) { + for (int in = 0; in < pinArrp->elementsConst(); ++in) { // 0 = leftmost int i = pinArrp->left() + in * pinArrp->declRange().leftToRightInc(); string varNewName = pinVarp->name() + "__BRA__" + cvtToStr(i) + "__KET__"; AstVar* varNewp = nullptr; @@ -430,19 +430,22 @@ private: newp->modVarp(varNewp); newp->name(newp->name() + "__BRA__" + cvtToStr(i) + "__KET__"); // And replace exprp with a new varxref - const AstVarRef* varrefp = VN_CAST(newp->exprp(), VarRef); - int offset = 0; - if (varrefp) { - } else if (AstSliceSel* slicep = VN_CAST(newp->exprp(), SliceSel)) { + const AstVarRef* varrefp = VN_CAST(newp->exprp(), VarRef); // Maybe null + int expr_i = i; + if (AstSliceSel* slicep = VN_CAST(newp->exprp(), SliceSel)) { varrefp = VN_CAST(slicep->fromp(), VarRef); UASSERT(VN_IS(slicep->rhsp(), Const), "Slices should be constant"); - offset = VN_CAST(slicep->rhsp(), Const)->toSInt(); + int slice_index + = slicep->declRange().left() + in * slicep->declRange().leftToRightInc(); + auto* exprArrp = VN_CAST(varrefp->dtypep(), UnpackArrayDType); + UASSERT_OBJ(exprArrp, slicep, "Slice of non-array"); + expr_i = slice_index + exprArrp->lo(); + } else if (!varrefp) { + newp->exprp()->v3error("Unexpected connection to arrayed port"); + } else if (auto* exprArrp = VN_CAST(varrefp->dtypep(), UnpackArrayDType)) { + expr_i = exprArrp->left() + in * exprArrp->declRange().leftToRightInc(); } - int expr_i = i; - if (auto* exprArrp = VN_CAST(newp->exprp()->dtypep(), UnpackArrayDType)) - expr_i = exprArrp->left() - + (in + offset) * exprArrp->declRange().leftToRightInc(); - if (!varrefp) newp->exprp()->v3error("Unexpected connection to arrayed port"); + string newname = varrefp->name() + "__BRA__" + cvtToStr(expr_i) + "__KET__"; AstVarXRef* newVarXRefp = new AstVarXRef(nodep->fileline(), newname, "", VAccess::WRITE); diff --git a/src/V3Inst.h b/src/V3Inst.h index 3b7c4c333..1b49a9dd0 100644 --- a/src/V3Inst.h +++ b/src/V3Inst.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3INST_H_ -#define _V3INST_H_ 1 +#ifndef VERILATOR_V3INST_H_ +#define VERILATOR_V3INST_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3InstrCount.h b/src/V3InstrCount.h index fa7c53086..b344b33b4 100644 --- a/src/V3InstrCount.h +++ b/src/V3InstrCount.h @@ -15,8 +15,8 @@ // //************************************************************************* -#ifndef _V3INSTRCOUNT_H_ -#define _V3INSTRCOUNT_H_ 1 +#ifndef VERILATOR_V3INSTRCOUNT_H_ +#define VERILATOR_V3INSTRCOUNT_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3LangCode.h b/src/V3LangCode.h index ccb996836..afad9f30c 100644 --- a/src/V3LangCode.h +++ b/src/V3LangCode.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3LANGCODE_H_ -#define _V3LANGCODE_H_ 1 +#ifndef VERILATOR_V3LANGCODE_H_ +#define VERILATOR_V3LANGCODE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3LanguageWords.h b/src/V3LanguageWords.h index f9c3c92a1..dfcd6f83f 100644 --- a/src/V3LanguageWords.h +++ b/src/V3LanguageWords.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3LANGUAGEWORDS_H_ -#define _V3LANGUAGEWORDS_H_ 1 +#ifndef VERILATOR_V3LANGUAGEWORDS_H_ +#define VERILATOR_V3LANGUAGEWORDS_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Life.h b/src/V3Life.h index 5328503ed..b783ee665 100644 --- a/src/V3Life.h +++ b/src/V3Life.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3LIFE_H_ -#define _V3LIFE_H_ 1 +#ifndef VERILATOR_V3LIFE_H_ +#define VERILATOR_V3LIFE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3LifePost.h b/src/V3LifePost.h index 990f155ac..f36b64c0e 100644 --- a/src/V3LifePost.h +++ b/src/V3LifePost.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3LIFEPOST_H_ -#define _V3LIFEPOST_H_ 1 +#ifndef VERILATOR_V3LIFEPOST_H_ +#define VERILATOR_V3LIFEPOST_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3LinkCells.h b/src/V3LinkCells.h index 7999147c8..a2c8d5cc1 100644 --- a/src/V3LinkCells.h +++ b/src/V3LinkCells.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3LINKCELLS_H_ -#define _V3LINKCELLS_H_ 1 +#ifndef VERILATOR_V3LINKCELLS_H_ +#define VERILATOR_V3LINKCELLS_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index f6dafdc1b..e4be8719b 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2452,8 +2452,9 @@ private: // Generally set by parse, but might be an import nodep->classOrPackagep(foundp->classOrPackagep()); } - if (!nodep->varp()) { - nodep->v3error("Can't find definition of signal, again: " << nodep->prettyNameQ()); + if (VL_UNCOVERABLE(!nodep->varp())) { + nodep->v3error("Can't find definition of signal, again: " // LCOV_EXCL_LINE + << nodep->prettyNameQ()); } } } diff --git a/src/V3LinkDot.h b/src/V3LinkDot.h index cfeaf100d..6b650384a 100644 --- a/src/V3LinkDot.h +++ b/src/V3LinkDot.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3LINKDOT_H_ -#define _V3LINKDOT_H_ 1 +#ifndef VERILATOR_V3LINKDOT_H_ +#define VERILATOR_V3LINKDOT_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3LinkInc.h b/src/V3LinkInc.h index 051babbba..5fc9f41a1 100644 --- a/src/V3LinkInc.h +++ b/src/V3LinkInc.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3LINKINC_H_ -#define _V3LINKINC_H_ 1 +#ifndef VERILATOR_V3LINKINC_H_ +#define VERILATOR_V3LINKINC_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3LinkJump.h b/src/V3LinkJump.h index 36a5d3742..c64a68f76 100644 --- a/src/V3LinkJump.h +++ b/src/V3LinkJump.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3LINKJUMP_H_ -#define _V3LINKJUMP_H_ 1 +#ifndef VERILATOR_V3LINKJUMP_H_ +#define VERILATOR_V3LINKJUMP_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3LinkLValue.h b/src/V3LinkLValue.h index 8c0327bd5..41e43e428 100644 --- a/src/V3LinkLValue.h +++ b/src/V3LinkLValue.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3LINKLVALUE_H_ -#define _V3LINKLVALUE_H_ 1 +#ifndef VERILATOR_V3LINKLVALUE_H_ +#define VERILATOR_V3LINKLVALUE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3LinkLevel.h b/src/V3LinkLevel.h index e141d0351..4f52f7a60 100644 --- a/src/V3LinkLevel.h +++ b/src/V3LinkLevel.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3LINKLEVEL_H_ -#define _V3LINKLEVEL_H_ 1 +#ifndef VERILATOR_V3LINKLEVEL_H_ +#define VERILATOR_V3LINKLEVEL_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3LinkParse.h b/src/V3LinkParse.h index 0abf4e798..1d68d3d3e 100644 --- a/src/V3LinkParse.h +++ b/src/V3LinkParse.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3LINKPARSE_H_ -#define _V3LINKPARSE_H_ 1 +#ifndef VERILATOR_V3LINKPARSE_H_ +#define VERILATOR_V3LINKPARSE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3LinkResolve.h b/src/V3LinkResolve.h index 92e351df1..1e9c36bb4 100644 --- a/src/V3LinkResolve.h +++ b/src/V3LinkResolve.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3LINKRESOLVE_H_ -#define _V3LINKRESOLVE_H_ 1 +#ifndef VERILATOR_V3LINKRESOLVE_H_ +#define VERILATOR_V3LINKRESOLVE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3List.h b/src/V3List.h index 99090bc9e..62f89c942 100644 --- a/src/V3List.h +++ b/src/V3List.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3LIST_H_ -#define _V3LIST_H_ 1 +#ifndef VERILATOR_V3LIST_H_ +#define VERILATOR_V3LIST_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Localize.h b/src/V3Localize.h index 9132145ce..c488a133d 100644 --- a/src/V3Localize.h +++ b/src/V3Localize.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3LOCALIZE_H_ -#define _V3LOCALIZE_H_ 1 +#ifndef VERILATOR_V3LOCALIZE_H_ +#define VERILATOR_V3LOCALIZE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3MergeCond.h b/src/V3MergeCond.h index 47c88918d..4a3e9b29d 100644 --- a/src/V3MergeCond.h +++ b/src/V3MergeCond.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3MERGECOND_H_ -#define _V3MERGECOND_H_ 1 +#ifndef VERILATOR_V3MERGECOND_H_ +#define VERILATOR_V3MERGECOND_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Name.h b/src/V3Name.h index 2c759178e..cc0d6b069 100644 --- a/src/V3Name.h +++ b/src/V3Name.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3NAME_H_ -#define _V3NAME_H_ 1 +#ifndef VERILATOR_V3NAME_H_ +#define VERILATOR_V3NAME_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 91a52ece2..b6bd7937b 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -696,7 +696,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const { case 'g': case '^': { // Realtime char tmp[MAX_SPRINTF_DOUBLE_SIZE]; - sprintf(tmp, vformat.c_str(), toDouble()); + VL_SNPRINTF(tmp, MAX_SPRINTF_DOUBLE_SIZE, vformat.c_str(), toDouble()); return tmp; } // 'l' // Library - converted to text by V3LinkResolve diff --git a/src/V3Number.h b/src/V3Number.h index c2fef325b..92693fcd8 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3NUMBER_H_ -#define _V3NUMBER_H_ 1 +#ifndef VERILATOR_V3NUMBER_H_ +#define VERILATOR_V3NUMBER_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Number_test.cpp b/src/V3Number_test.cpp index 07ba971e8..1e3b142f0 100644 --- a/src/V3Number_test.cpp +++ b/src/V3Number_test.cpp @@ -16,7 +16,7 @@ // CHEAT! #define V3NUMBER_ASCII_BINARY -#define _V3ERROR_NO_GLOBAL_ 1 +#define V3ERROR_NO_GLOBAL_ #include #include "verilatedos.h" diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 99f6489e2..c54149d81 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1045,6 +1045,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char } else if (onoff(sw, "-ignc", flag /*ref*/)) { m_ignc = flag; } else if (onoff(sw, "-inhibit-sim", flag /*ref*/)) { + fl->v3warn(DEPRECATED, "-inhibit-sim option is deprecated"); m_inhibitSim = flag; } else if (onoff(sw, "-lint-only", flag /*ref*/)) { m_lintOnly = flag; @@ -1158,7 +1159,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char case 'k': m_oSubstConst = flag; break; case 'l': m_oLife = flag; break; case 'm': m_oAssemble = flag; break; - // n o + // n + case 'o': m_oConstBitOpTree = flag; break; // Can remove ~2022-01 when stable case 'p': m_public = !flag; break; // With -Op so flag=0, we want public on so few optimizations done @@ -1444,8 +1446,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char // Processed only in bin/verilator shell } else if (!strcmp(sw, "-gdbbt")) { // Processed only in bin/verilator shell - } else if (!strcmp(sw, "-quiet-exit")) { - // Processed only in bin/verilator shell } else if (!strcmp(sw, "-mod-prefix") && (i + 1) < argc) { shift; m_modPrefix = argv[i]; @@ -1865,6 +1865,7 @@ void V3Options::optimize(int level) { m_oCase = flag; m_oCombine = flag; m_oConst = flag; + m_oConstBitOpTree = flag; m_oDedupe = flag; m_oExpand = flag; m_oGate = flag; diff --git a/src/V3Options.h b/src/V3Options.h index 85d372a87..4db85011a 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3OPTIONS_H_ -#define _V3OPTIONS_H_ 1 +#ifndef VERILATOR_V3OPTIONS_H_ +#define VERILATOR_V3OPTIONS_H_ #include "config_build.h" #include "verilatedos.h" @@ -37,7 +37,7 @@ class VOptionBool final { // Class to track options that are either not specified (and default // true/false), versus user setting the option to true or false public: - enum en : uint8_t { OPT_DEFAULT_FALSE = 0, OPT_DEFAULT_TRUE, OPT_TRUE, OPT_FALSE, _ENUM_END }; + enum en : uint8_t { OPT_DEFAULT_FALSE = 0, OPT_DEFAULT_TRUE, OPT_TRUE, OPT_FALSE }; enum en m_e; inline VOptionBool() : m_e{OPT_DEFAULT_FALSE} {} @@ -49,23 +49,15 @@ public: operator en() const { return m_e; } bool isDefault() const { return m_e == OPT_DEFAULT_FALSE || m_e == OPT_DEFAULT_TRUE; } bool isTrue() const { return m_e == OPT_TRUE || m_e == OPT_DEFAULT_TRUE; } - bool isFalse() const { return m_e == OPT_FALSE || m_e == OPT_DEFAULT_FALSE; } bool isSetTrue() const { return m_e == OPT_TRUE; } bool isSetFalse() const { return m_e == OPT_FALSE; } void setTrueOrFalse(bool flag) { m_e = flag ? OPT_TRUE : OPT_FALSE; } - const char* ascii() const { - static const char* const names[] = {"DEFAULT_FALSE", "DEFAULT_TRUE", "TRUE", "FALSE"}; - return names[m_e]; - } }; inline bool operator==(const VOptionBool& lhs, const VOptionBool& rhs) { return lhs.m_e == rhs.m_e; } inline bool operator==(const VOptionBool& lhs, VOptionBool::en rhs) { return lhs.m_e == rhs; } inline bool operator==(VOptionBool::en lhs, const VOptionBool& rhs) { return lhs == rhs.m_e; } -inline std::ostream& operator<<(std::ostream& os, const VOptionBool& rhs) { - return os << rhs.ascii(); -} //###################################################################### @@ -97,43 +89,11 @@ public: VTimescale(const string& value, bool& badr); VTimescale(double value, bool& badr) { badr = false; - if (value == 10e2) { - m_e = TS_100S; - } else if (value == 1e1) { - m_e = TS_10S; - } else if (value == 1e0) { - m_e = TS_1S; - } else if (value == 1e-1) { - m_e = TS_100MS; - } else if (value == 1e-2) { - m_e = TS_10MS; - } else if (value == 1e-3) { - m_e = TS_1MS; - } else if (value == 1e-4) { - m_e = TS_100US; - } else if (value == 1e-5) { - m_e = TS_10US; - } else if (value == 1e-6) { - m_e = TS_1US; - } else if (value == 1e-7) { - m_e = TS_100NS; - } else if (value == 1e-8) { - m_e = TS_10NS; - } else if (value == 1e-9) { - m_e = TS_1NS; - } else if (value == 1e-10) { - m_e = TS_100PS; - } else if (value == 1e-11) { - m_e = TS_10PS; - } else if (value == 1e-12) { - m_e = TS_1PS; - } else if (value == 1e-13) { - m_e = TS_100FS; - } else if (value == 1e-14) { - m_e = TS_10FS; - } else if (value == 1e-15) { - m_e = TS_1FS; - } else { + for (int i = TS_100S; i < _ENUM_END; ++i) { + m_e = static_cast(i); + if (multiplier() == value) break; + } + if (multiplier() != value) { m_e = NONE; badr = true; } @@ -381,6 +341,7 @@ private: bool m_oCase; // main switch: -Oe: case tree conversion bool m_oCombine; // main switch: -Ob: common icode packing bool m_oConst; // main switch: -Oc: constant folding + bool m_oConstBitOpTree; // main switch: -Oo: constant bit op tree bool m_oDedupe; // main switch: -Od: logic deduplication bool m_oExpand; // main switch: -Ox: expansion of C macros bool m_oGate; // main switch: -Og: gate wire elimination @@ -450,7 +411,6 @@ public: string bin() const { return m_bin; } string flags() const { return m_flags; } bool systemC() const { return m_systemC; } - bool usingSystemCLibs() const { return !lintOnly() && systemC(); } bool savable() const { return m_savable; } bool stats() const { return m_stats; } bool statsVars() const { return m_statsVars; } @@ -562,7 +522,6 @@ public: string modPrefix() const { return m_modPrefix; } string pipeFilter() const { return m_pipeFilter; } string prefix() const { return m_prefix; } - string protectKey() const { return m_protectKey; } string protectKeyDefaulted(); // Set default key if not set by user string protectLib() const { return m_protectLib; } string protectLibName(bool shared) { @@ -589,7 +548,6 @@ public: const V3StringSet& libraryFiles() const { return m_libraryFiles; } const V3StringList& vFiles() const { return m_vFiles; } const V3StringList& forceIncs() const { return m_forceIncs; } - const V3LangCode& defaultLanguage() const { return m_defaultLanguage; } bool hasParameter(const string& name); string parameter(const string& name); @@ -606,6 +564,7 @@ public: bool oCase() const { return m_oCase; } bool oCombine() const { return m_oCombine; } bool oConst() const { return m_oConst; } + bool oConstBitOpTree() const { return m_oConstBitOpTree; } bool oDedupe() const { return m_oDedupe; } bool oExpand() const { return m_oExpand; } bool oGate() const { return m_oGate; } diff --git a/src/V3Order.h b/src/V3Order.h index 1d55815aa..13e762c14 100644 --- a/src/V3Order.h +++ b/src/V3Order.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3ORDER_H_ -#define _V3ORDER_H_ 1 +#ifndef VERILATOR_V3ORDER_H_ +#define VERILATOR_V3ORDER_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3OrderGraph.h b/src/V3OrderGraph.h index c235b8bb9..69051b511 100644 --- a/src/V3OrderGraph.h +++ b/src/V3OrderGraph.h @@ -34,8 +34,8 @@ // OrderPreCutEdge //************************************************************************* -#ifndef _V3ORDERGRAPH_H_ -#define _V3ORDERGRAPH_H_ +#ifndef VERILATOR_V3ORDERGRAPH_H_ +#define VERILATOR_V3ORDERGRAPH_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Os.h b/src/V3Os.h index 6c519f8b2..69c90e887 100644 --- a/src/V3Os.h +++ b/src/V3Os.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3OS_H_ -#define _V3OS_H_ 1 +#ifndef VERILATOR_V3OS_H_ +#define VERILATOR_V3OS_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Param.h b/src/V3Param.h index a785602fb..abc07216f 100644 --- a/src/V3Param.h +++ b/src/V3Param.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3PARAM_H_ -#define _V3PARAM_H_ 1 +#ifndef VERILATOR_V3PARAM_H_ +#define VERILATOR_V3PARAM_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Parse.h b/src/V3Parse.h index 5fe39055d..f1638cdb1 100644 --- a/src/V3Parse.h +++ b/src/V3Parse.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3PARSE_H_ -#define _V3PARSE_H_ 1 +#ifndef VERILATOR_V3PARSE_H_ +#define VERILATOR_V3PARSE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index 5b4bf7c48..d556d628e 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3PARSEIMP_H_ -#define _V3PARSEIMP_H_ 1 +#ifndef VERILATOR_V3PARSEIMP_H_ +#define VERILATOR_V3PARSEIMP_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3ParseSym.h b/src/V3ParseSym.h index 80412379d..8c8361c03 100644 --- a/src/V3ParseSym.h +++ b/src/V3ParseSym.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3PARSESYM_H_ -#define _V3PARSESYM_H_ 1 +#ifndef VERILATOR_V3PARSESYM_H_ +#define VERILATOR_V3PARSESYM_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Partition.h b/src/V3Partition.h index 32006e89e..3405f1b7f 100644 --- a/src/V3Partition.h +++ b/src/V3Partition.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3PARTITION_H_ -#define _V3PARTITION_H_ +#ifndef VERILATOR_V3PARTITION_H_ +#define VERILATOR_V3PARTITION_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3PartitionGraph.h b/src/V3PartitionGraph.h index 0585f92f7..d824950ce 100644 --- a/src/V3PartitionGraph.h +++ b/src/V3PartitionGraph.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3PARTITIONGRAPH_H_ -#define _V3PARTITIONGRAPH_H_ +#ifndef VERILATOR_V3PARTITIONGRAPH_H_ +#define VERILATOR_V3PARTITIONGRAPH_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3PreLex.h b/src/V3PreLex.h index c72ea0592..b5b239a6a 100644 --- a/src/V3PreLex.h +++ b/src/V3PreLex.h @@ -18,8 +18,8 @@ // It is not intended for user applications. //************************************************************************* -#ifndef _VPRELEX_H_ // Guard -#define _VPRELEX_H_ 1 +#ifndef VERILATOR_VPRELEX_H_ // Guard +#define VERILATOR_VPRELEX_H_ #include "V3Error.h" #include "V3FileLine.h" diff --git a/src/V3PreLex.l b/src/V3PreLex.l index dbe22e3f6..0ad5c5a61 100644 --- a/src/V3PreLex.l +++ b/src/V3PreLex.l @@ -125,8 +125,8 @@ bom [\357\273\277] rtnfile += '"'; yytext = (char*)rtnfile.c_str(); yyleng = rtnfile.length(); return VP_STRING; } "`__LINE__" { FL_FWDC; - static char buf[10]; - sprintf(buf, "%d", LEXP->curFilelinep()->lastLineno()); + static char buf[25]; + VL_SNPRINTF(buf, 25, "%d", LEXP->curFilelinep()->lastLineno()); yytext = buf; yyleng = strlen(yytext); return VP_TEXT; } @@ -348,7 +348,8 @@ void V3PreLex::pushStateIncFilename() { } void V3PreLex::debug(int level) { - yy_flex_debug = level; } + yy_flex_debug = level; // Use --debugi-V3PreShell, if level<5 this level is 0 +} int V3PreLex::debug() { return yy_flex_debug; } @@ -396,8 +397,8 @@ again: string forceOut = endOfStream(again /*ref*/); streamp = curStreamp(); // May have been updated if (forceOut != "") { - if (forceOut.length() > max_size) { - yyerrorf("Output buffer too small for a `line"); + if (forceOut.length() > max_size) { // LCOV_EXCL_LINE + yyerrorf("Output buffer too small for a `line"); // LCOV_EXCL_LINE } else { got = forceOut.length(); strncpy(buf, forceOut.c_str(), got); diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp index 4fadcb2b8..d2cb6814f 100644 --- a/src/V3PreProc.cpp +++ b/src/V3PreProc.cpp @@ -272,7 +272,7 @@ public: m_lexp->m_keepComments = keepComments(); m_lexp->m_keepWhitespace = keepWhitespace(); m_lexp->m_pedantic = pedantic(); - m_lexp->debug(debug() >= 5 ? debug() : 0); // See also V3PreProc::debug() method + debug(debug()); // Set lexer debug via V3PreProc::debug() method } ~V3PreProcImp() override { if (m_lexp) VL_DO_CLEAR(delete m_lexp, m_lexp = nullptr); @@ -486,6 +486,12 @@ void V3PreProcImp::comment(const string& text) { //************************************************************************* // VPreProc Methods. +void V3PreProc::debug(int level) { + m_debug = level; + V3PreProcImp* idatap = static_cast(this); + if (idatap->m_lexp) idatap->m_lexp->debug(debug() >= 5 ? debug() : 0); +} + FileLine* V3PreProc::fileline() { V3PreProcImp* idatap = static_cast(this); return idatap->m_lexp->m_tokFilelinep; diff --git a/src/V3PreProc.h b/src/V3PreProc.h index 43d8f3a19..6af036d3b 100644 --- a/src/V3PreProc.h +++ b/src/V3PreProc.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3PREPROC_H_ -#define _V3PREPROC_H_ 1 +#ifndef VERILATOR_V3PREPROC_H_ +#define VERILATOR_V3PREPROC_H_ #include "config_build.h" #include "verilatedos.h" @@ -61,7 +61,7 @@ public: virtual void insertUnreadback(const string& text) = 0; int debug() const { return m_debug; } - void debug(int level) { m_debug = level; } + void debug(int level); FileLine* fileline(); ///< File/Line number for last getline call diff --git a/src/V3PreShell.h b/src/V3PreShell.h index b0f9986aa..97198899d 100644 --- a/src/V3PreShell.h +++ b/src/V3PreShell.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3PRESHELL_H_ -#define _V3PRESHELL_H_ 1 +#ifndef VERILATOR_V3PRESHELL_H_ +#define VERILATOR_V3PRESHELL_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Premit.h b/src/V3Premit.h index d2ea432dd..cfeb48fe2 100644 --- a/src/V3Premit.h +++ b/src/V3Premit.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3PREMIT_H_ -#define _V3PREMIT_H_ 1 +#ifndef VERILATOR_V3PREMIT_H_ +#define VERILATOR_V3PREMIT_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3ProtectLib.cpp b/src/V3ProtectLib.cpp index 2432ffada..b7327c2be 100644 --- a/src/V3ProtectLib.cpp +++ b/src/V3ProtectLib.cpp @@ -263,7 +263,7 @@ private: m_seqAssignsp = new AstTextBlock(fl, "if (last_seq_seqnum__V > " "last_combo_seqnum__V) begin\n"); txtp->addNodep(m_seqAssignsp); - m_comboAssignsp = new AstTextBlock(fl, "end else begin\n"); + m_comboAssignsp = new AstTextBlock(fl, "end\nelse begin\n"); txtp->addNodep(m_comboAssignsp); txtp->addText(fl, "end\n"); } else { @@ -437,7 +437,6 @@ private: AstVar* newVarp = new AstVar(varp->fileline(), AstVarType::VAR, varp->name() + suffix, varp->dtypep()); textp->addNodep(newVarp); - textp->addText(varp->fileline(), ";\n"); } void handleOutput(AstVar* varp) { diff --git a/src/V3ProtectLib.h b/src/V3ProtectLib.h index 39131d307..7f2028141 100644 --- a/src/V3ProtectLib.h +++ b/src/V3ProtectLib.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3PROTECTLIB_H_ -#define _V3PROTECTLIB_H_ 1 +#ifndef VERILATOR_V3PROTECTLIB_H_ +#define VERILATOR_V3PROTECTLIB_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Randomize.h b/src/V3Randomize.h index 53c74b182..9cb4df8de 100644 --- a/src/V3Randomize.h +++ b/src/V3Randomize.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3RANDOMIZE_METHOD_H_ -#define _V3RANDOMIZE_METHOD_H_ 1 +#ifndef VERILATOR_V3RANDOMIZE_METHOD_H_ +#define VERILATOR_V3RANDOMIZE_METHOD_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Reloop.h b/src/V3Reloop.h index 64c17e50a..4a3302adb 100644 --- a/src/V3Reloop.h +++ b/src/V3Reloop.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3RELOOP_H_ -#define _V3RELOOP_H_ 1 +#ifndef VERILATOR_V3RELOOP_H_ +#define VERILATOR_V3RELOOP_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Scope.h b/src/V3Scope.h index 9589aa1e8..ef754c534 100644 --- a/src/V3Scope.h +++ b/src/V3Scope.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3SCOPE_H_ -#define _V3SCOPE_H_ 1 +#ifndef VERILATOR_V3SCOPE_H_ +#define VERILATOR_V3SCOPE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Scoreboard.h b/src/V3Scoreboard.h index 44297978e..f0370f59d 100644 --- a/src/V3Scoreboard.h +++ b/src/V3Scoreboard.h @@ -21,8 +21,8 @@ // //************************************************************************* -#ifndef _V3SCOREBOARD_H_ -#define _V3SCOREBOARD_H_ +#ifndef VERILATOR_V3SCOREBOARD_H_ +#define VERILATOR_V3SCOREBOARD_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3SenTree.h b/src/V3SenTree.h index 22f212707..a04d68b2f 100644 --- a/src/V3SenTree.h +++ b/src/V3SenTree.h @@ -16,8 +16,8 @@ // AstSenTree related utilities. //************************************************************************* -#ifndef _V3SENTREE_H_ -#define _V3SENTREE_H_ +#ifndef VERILATOR_V3SENTREE_H_ +#define VERILATOR_V3SENTREE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Simulate.h b/src/V3Simulate.h index de00d6cb5..0112b8673 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -29,8 +29,8 @@ // //************************************************************************* -#ifndef _V3SIMULATE_H_ -#define _V3SIMULATE_H_ 1 +#ifndef VERILATOR_V3SIMULATE_H_ +#define VERILATOR_V3SIMULATE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Slice.h b/src/V3Slice.h index 40c94c04d..cbc544ad3 100644 --- a/src/V3Slice.h +++ b/src/V3Slice.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3SLICE_H_ -#define _V3SLICE_H_ 1 +#ifndef VERILATOR_V3SLICE_H_ +#define VERILATOR_V3SLICE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Split.h b/src/V3Split.h index 9bf7ab68d..1fd5f5fb0 100644 --- a/src/V3Split.h +++ b/src/V3Split.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3SPLIT_H_ -#define _V3SPLIT_H_ 1 +#ifndef VERILATOR_V3SPLIT_H_ +#define VERILATOR_V3SPLIT_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3SplitAs.h b/src/V3SplitAs.h index 3c2886c3b..a0004c4e0 100644 --- a/src/V3SplitAs.h +++ b/src/V3SplitAs.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3SPLITAS_H_ -#define _V3SPLITAS_H_ 1 +#ifndef VERILATOR_V3SPLITAS_H_ +#define VERILATOR_V3SPLITAS_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3SplitVar.h b/src/V3SplitVar.h index 243120195..b952f8f5a 100644 --- a/src/V3SplitVar.h +++ b/src/V3SplitVar.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3SPLITVAR_H_ -#define _V3SPLITVAR_H_ 1 +#ifndef VERILATOR_V3SPLITVAR_H_ +#define VERILATOR_V3SPLITVAR_H_ //============================================================================ diff --git a/src/V3Stats.h b/src/V3Stats.h index 9cebbd057..1b6c785c7 100644 --- a/src/V3Stats.h +++ b/src/V3Stats.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3STATS_H_ -#define _V3STATS_H_ 1 +#ifndef VERILATOR_V3STATS_H_ +#define VERILATOR_V3STATS_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3StatsReport.cpp b/src/V3StatsReport.cpp index 0eb3b9961..530a06901 100644 --- a/src/V3StatsReport.cpp +++ b/src/V3StatsReport.cpp @@ -208,9 +208,7 @@ void V3Stats::statsStage(const string& name) { static double lastWallTime = -1; static int fileNumber = 0; - char digits[100]; - sprintf(digits, "%03d", ++fileNumber); - const string digitName = string(digits) + "_" + name; + const string digitName = V3Global::digitsFilename(++fileNumber) + "_" + name; double wallTime = V3Os::timeUsecs() / 1.0e6; if (lastWallTime < 0) lastWallTime = wallTime; diff --git a/src/V3String.cpp b/src/V3String.cpp index 2328f8cfc..5e4bcada6 100644 --- a/src/V3String.cpp +++ b/src/V3String.cpp @@ -460,7 +460,7 @@ VSpellCheck::EditDistance VSpellCheck::cutoffDistance(size_t goal_len, size_t ca return (max_length + 2) / 3; } -string VSpellCheck::bestCandidateInfo(const string& goal, EditDistance& distancer) { +string VSpellCheck::bestCandidateInfo(const string& goal, EditDistance& distancer) const { string bestCandidate; size_t gLen = goal.length(); distancer = LENGTH_LIMIT * 10; diff --git a/src/V3String.h b/src/V3String.h index 71d811ec0..60b4d03b2 100644 --- a/src/V3String.h +++ b/src/V3String.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3STRING_H_ -#define _V3STRING_H_ 1 +#ifndef VERILATOR_V3STRING_H_ +#define VERILATOR_V3STRING_H_ #include "config_build.h" #include "verilatedos.h" @@ -208,12 +208,12 @@ public: if (m_candidates.size() < NUM_CANDIDATE_LIMIT) m_candidates.push_back(s); } // Return candidate is closest to provided string, or "" for none - string bestCandidate(const string& goal) { + string bestCandidate(const string& goal) const { EditDistance dist; return bestCandidateInfo(goal, dist /*ref*/); } // Return friendly message - string bestCandidateMsg(const string& goal) { + string bestCandidateMsg(const string& goal) const { string candidate = bestCandidate(goal); if (candidate.empty()) { return ""; @@ -226,7 +226,7 @@ public: private: static EditDistance editDistance(const string& s, const string& t); static EditDistance cutoffDistance(size_t goal_len, size_t candidate_len); - string bestCandidateInfo(const string& goal, EditDistance& distancer); + string bestCandidateInfo(const string& goal, EditDistance& distancer) const; static void selfTestDistanceOne(const string& a, const string& b, EditDistance expected); static void selfTestSuggestOne(bool matches, const string& c, const string& goal, EditDistance dist); diff --git a/src/V3Subst.h b/src/V3Subst.h index a2dd17202..a4a8a8b91 100644 --- a/src/V3Subst.h +++ b/src/V3Subst.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3SUBST_H_ -#define _V3SUBST_H_ 1 +#ifndef VERILATOR_V3SUBST_H_ +#define VERILATOR_V3SUBST_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3SymTable.h b/src/V3SymTable.h index e1e11151a..304a2f882 100644 --- a/src/V3SymTable.h +++ b/src/V3SymTable.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3SYMTABLE_H_ -#define _V3SYMTABLE_H_ 1 +#ifndef VERILATOR_V3SYMTABLE_H_ +#define VERILATOR_V3SYMTABLE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3TSP.h b/src/V3TSP.h index ec81061ea..cb81103f7 100644 --- a/src/V3TSP.h +++ b/src/V3TSP.h @@ -15,8 +15,8 @@ // //************************************************************************* -#ifndef _V3TSP_H_ -#define _V3TSP_H_ 1 +#ifndef VERILATOR_V3TSP_H_ +#define VERILATOR_V3TSP_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Table.h b/src/V3Table.h index 471bc7f05..a5574294a 100644 --- a/src/V3Table.h +++ b/src/V3Table.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3TABLE_H_ -#define _V3TABLE_H_ 1 +#ifndef VERILATOR_V3TABLE_H_ +#define VERILATOR_V3TABLE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Task.h b/src/V3Task.h index 9491d62fc..03de4ea32 100644 --- a/src/V3Task.h +++ b/src/V3Task.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3TASK_H_ -#define _V3TASK_H_ 1 +#ifndef VERILATOR_V3TASK_H_ +#define VERILATOR_V3TASK_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Trace.h b/src/V3Trace.h index 95691031b..209ea7582 100644 --- a/src/V3Trace.h +++ b/src/V3Trace.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3TRACE_H_ -#define _V3TRACE_H_ 1 +#ifndef VERILATOR_V3TRACE_H_ +#define VERILATOR_V3TRACE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3TraceDecl.h b/src/V3TraceDecl.h index 2c0ec73eb..f60f05f9a 100644 --- a/src/V3TraceDecl.h +++ b/src/V3TraceDecl.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3TRACEDECL_H_ -#define _V3TRACEDECL_H_ 1 +#ifndef VERILATOR_V3TRACEDECL_H_ +#define VERILATOR_V3TRACEDECL_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Tristate.h b/src/V3Tristate.h index 079c44a27..ffbe63602 100644 --- a/src/V3Tristate.h +++ b/src/V3Tristate.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3TRISTATE_H_ -#define _V3TRISTATE_H_ 1 +#ifndef VERILATOR_V3TRISTATE_H_ +#define VERILATOR_V3TRISTATE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Undriven.h b/src/V3Undriven.h index 52d4361ce..98ddc041b 100644 --- a/src/V3Undriven.h +++ b/src/V3Undriven.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3UNDRIVEN_H_ -#define _V3UNDRIVEN_H_ 1 +#ifndef VERILATOR_V3UNDRIVEN_H_ +#define VERILATOR_V3UNDRIVEN_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Unknown.h b/src/V3Unknown.h index b203c7720..0ad1a942c 100644 --- a/src/V3Unknown.h +++ b/src/V3Unknown.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3UNKNOWN_H_ -#define _V3UNKNOWN_H_ 1 +#ifndef VERILATOR_V3UNKNOWN_H_ +#define VERILATOR_V3UNKNOWN_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Unroll.h b/src/V3Unroll.h index e38658d97..710c292f4 100644 --- a/src/V3Unroll.h +++ b/src/V3Unroll.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3UNROLL_H_ -#define _V3UNROLL_H_ 1 +#ifndef VERILATOR_V3UNROLL_H_ +#define VERILATOR_V3UNROLL_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3Waiver.h b/src/V3Waiver.h index 165ae1f9f..8be22fbec 100644 --- a/src/V3Waiver.h +++ b/src/V3Waiver.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3WAIVER_H_ -#define _V3WAIVER_H_ 1 +#ifndef VERILATOR_V3WAIVER_H_ +#define VERILATOR_V3WAIVER_H_ #include "V3Error.h" diff --git a/src/V3Width.cpp b/src/V3Width.cpp index f9a8fbe6c..1c5bbdfc2 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -77,7 +77,7 @@ #include // More code; this file was getting too large; see actions there -#define _V3WIDTH_CPP_ +#define VERILATOR_V3WIDTH_CPP_ #include "V3WidthCommit.h" //###################################################################### diff --git a/src/V3Width.h b/src/V3Width.h index db2d0cccc..6b7f1d5ca 100644 --- a/src/V3Width.h +++ b/src/V3Width.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3WIDTH_H_ -#define _V3WIDTH_H_ 1 +#ifndef VERILATOR_V3WIDTH_H_ +#define VERILATOR_V3WIDTH_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/V3WidthCommit.h b/src/V3WidthCommit.h index 7ffb9fb8d..1892ac2d0 100644 --- a/src/V3WidthCommit.h +++ b/src/V3WidthCommit.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _V3WIDTHCOMMIT_H_ -#define _V3WIDTHCOMMIT_H_ 1 +#ifndef VERILATOR_V3WIDTHCOMMIT_H_ +#define VERILATOR_V3WIDTHCOMMIT_H_ #include "config_build.h" #include "verilatedos.h" @@ -24,7 +24,7 @@ #include "V3Ast.h" // clang-format off -#ifndef _V3WIDTH_CPP_ +#ifndef VERILATOR_V3WIDTH_CPP_ # error "V3WidthCommit for V3Width internal use only" #endif // clang-format on diff --git a/src/VlcBucket.h b/src/VlcBucket.h index b59f1d6c2..e68483af2 100644 --- a/src/VlcBucket.h +++ b/src/VlcBucket.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _VLCBUCKET_H_ -#define _VLCBUCKET_H_ 1 +#ifndef VERILATOR_VLCBUCKET_H_ +#define VERILATOR_VLCBUCKET_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/VlcMain.cpp b/src/VlcMain.cpp index 45be06f0c..3df97d3cb 100644 --- a/src/VlcMain.cpp +++ b/src/VlcMain.cpp @@ -24,7 +24,7 @@ #include "verilatedos.h" // Cheat for speed and compile .cpp files into one object -#define _V3ERROR_NO_GLOBAL_ 1 +#define V3ERROR_NO_GLOBAL_ #include "V3Error.cpp" #include "V3String.cpp" #include "V3Os.cpp" diff --git a/src/VlcOptions.h b/src/VlcOptions.h index fc999c7fb..29b88f1e1 100644 --- a/src/VlcOptions.h +++ b/src/VlcOptions.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _VLCOPTIONS_H_ -#define _VLCOPTIONS_H_ 1 +#ifndef VERILATOR_VLCOPTIONS_H_ +#define VERILATOR_VLCOPTIONS_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/VlcPoint.h b/src/VlcPoint.h index e8b11d197..f05dfecb2 100644 --- a/src/VlcPoint.h +++ b/src/VlcPoint.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _VLCPOINT_H_ -#define _VLCPOINT_H_ 1 +#ifndef VERILATOR_VLCPOINT_H_ +#define VERILATOR_VLCPOINT_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/VlcSource.h b/src/VlcSource.h index 18abda529..7cb274f35 100644 --- a/src/VlcSource.h +++ b/src/VlcSource.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _VLCSOURCE_H_ -#define _VLCSOURCE_H_ 1 +#ifndef VERILATOR_VLCSOURCE_H_ +#define VERILATOR_VLCSOURCE_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/VlcTest.h b/src/VlcTest.h index 71d8a8f52..e2aa8adc0 100644 --- a/src/VlcTest.h +++ b/src/VlcTest.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _VLCTEST_H_ -#define _VLCTEST_H_ 1 +#ifndef VERILATOR_VLCTEST_H_ +#define VERILATOR_VLCTEST_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/VlcTop.h b/src/VlcTop.h index 28c6ce972..106191b1a 100644 --- a/src/VlcTop.h +++ b/src/VlcTop.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef _VLCTOP_H_ -#define _VLCTOP_H_ 1 +#ifndef VERILATOR_VLCTOP_H_ +#define VERILATOR_VLCTOP_H_ #include "config_build.h" #include "verilatedos.h" diff --git a/src/astgen b/src/astgen index 98e108f03..a78973055 100755 --- a/src/astgen +++ b/src/astgen @@ -1,11 +1,12 @@ #!/usr/bin/env python3 +# pylint: disable=C0103,C0114,C0115,C0116,C0123,C0301,R0902,R0913,R0914,R0912,R0915,W0621 ###################################################################### import argparse import glob import re import sys -#from pprint import pprint, pformat +# from pprint import pprint, pformat Types = [] Classes = {} @@ -17,10 +18,13 @@ Stages = {} class Cpt: def __init__(self): self.did_out_tree = False - self.out_lines = [] + self.in_filename = "" + self.in_linenum = 1 + self.out_filename = "" self.out_linenum = 1 - self.treeop = {} + self.out_lines = [] self.tree_skip_visit = {} + self.treeop = {} self._exec_nsyms = 0 self._exec_syms = {} @@ -98,7 +102,7 @@ class Cpt: doflag = match.group(2) fromn = match.group(3) to = match.group(4) - #self.print("// $fromn $to\n") + # self.print("// $fromn $to\n") if not self.did_out_tree: self.did_out_tree = True self.output_func(lambda self: self.tree_match_base()) @@ -169,9 +173,9 @@ class Cpt: self.error("Unknown astgen op: " + func) @staticmethod - def add_nodep(str): - str = re.sub(r'\$([a-zA-Z0-9]+)', r'nodep->\1()', str) - return str + def add_nodep(strg): + strg = re.sub(r'\$([a-zA-Z0-9]+)', r'nodep->\1()', strg) + return strg def _exec_syms_recurse(self, aref): for sym in aref: @@ -206,8 +210,6 @@ class Cpt: outl = re.sub(r'\$([a-zA-Z0-9]+)', r'nodep->\1()', func) out += outl + ";" elif re.match(r'^\s*Ast([a-zA-Z0-9]+)\s*\{\s*(.*)\s*\}$', func): - nargs = 0 - argnums = [] # Number for each argument name aref = None # Recursive array with structure to form astack = [] @@ -215,9 +217,9 @@ class Cpt: argtext = func + "\000" # EOF character for tok in argtext: if tok == "\000": - None + None # pylint: disable=pointless-statement elif re.match(r'\s+', tok): - None + None # pylint: disable=pointless-statement elif tok == "{": newref = [forming] if not aref: @@ -260,7 +262,7 @@ class Cpt: elif func == "NEVER": out += "nodep->v3fatalSrc(\"Executing transform that was NEVERed\");" elif func == "DONE": - None + None # pylint: disable=pointless-statement else: self.error("Unknown execution function format: " + func + "\n") return out @@ -300,14 +302,14 @@ class Cpt: bases = subclasses_of(typen) bases.append(typen) for base in bases: - if not base in self.treeop: + if base not in self.treeop: continue for typefunc in self.treeop[base]: lines = [ " if (" + typefunc['match_func'] + "(nodep)) return;\n" ] - if (typefunc['short_circuit']): # short-circuit match fn + if typefunc['short_circuit']: # short-circuit match fn out_for_type_sc.extend(lines) else: # Standard match fn if typefunc[ @@ -365,7 +367,7 @@ def read_types(filename): match = re.search(r':\s*public\s+(\S+)', line) if match: inh = match.group(1) - #print("class "+classn+" : "+inh) + # print("class "+classn+" : "+inh) if classn == "AstNode": inh = "" if re.search(r'Ast', inh) or classn == "AstNode": @@ -425,7 +427,7 @@ def subclasses_of(typen): cllist = [] subclass = Classes[typen] while True: - if not subclass in Classes: + if subclass not in Classes: break cllist.append(subclass) subclass = Classes[subclass] @@ -448,7 +450,7 @@ def children_of(typen): return cllist -#--------------------------------------------------------------------- +# --------------------------------------------------------------------- def write_report(filename): @@ -473,7 +475,7 @@ def write_report(filename): if subclass != 'Node': fh.write("Ast%-12s " % subclass) fh.write("\n") - if ("Ast" + typen) in ClassRefs: + if ("Ast" + typen) in ClassRefs: # pylint: disable=superfluous-parens refs = ClassRefs["Ast" + typen] fh.write(" newed: ") for stage in sorted(refs['newed'].keys(), @@ -715,6 +717,6 @@ for cpt in Args.infiles: out_filename=cpt + "__gen.cpp") ###################################################################### -### Local Variables: -### compile-command: "cd obj_dbg && ../astgen -I.. V3Const.cpp" -### End: +# Local Variables: +# compile-command: "cd obj_dbg && ../astgen -I.. V3Const.cpp" +# End: diff --git a/src/bisonpre b/src/bisonpre index 2d1c79934..7407f024f 100755 --- a/src/bisonpre +++ b/src/bisonpre @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# pylint: disable=C0103,C0114,C0115,C0116,R0912,R0914,R0915,R1702,W0125 ###################################################################### import argparse @@ -6,7 +7,7 @@ import os import re import subprocess import sys -from pprint import pprint, pformat +# from pprint import pprint, pformat ###################################################################### @@ -55,8 +56,7 @@ def output_prefix(): if Args.output: o = re.sub(r'\.[^.]*$', '', Args.output) return o - else: - return Args.file_prefix + ".tab" + return Args.file_prefix + ".tab" def unlink_ok(filename): @@ -90,7 +90,7 @@ def bison_version_check(): if v < 1.875: sys.exit("bisonpre: %Error: '" + Args.yacc + "' is version " + v + "; version 1.875 or newer is required\n") - global Bison_Version + global Bison_Version # pylint: disable=global-variable-undefined Bison_Version = v return @@ -111,13 +111,13 @@ def clean_output(filename, outname, is_output, is_c): if is_output: state_line = {} - l = 0 + lineno = 0 for line in lines: - l += 1 + lineno += 1 # We add a colon so it's easy to search for the definition match = re.match(r'^state (\d+)\s*', line) if match: - state_line[match.group(1)] = l + state_line[match.group(1)] = lineno out = [] for line in lines: match = re.match(r'^State (\d+) (conflicts)', line) @@ -182,7 +182,7 @@ def warning_check(filename): def clean_input(filename, outname): print(" edit " + filename + " " + outname) - global Filename + global Filename # pylint: disable=global-variable-undefined Filename = filename with open(filename) as fh: @@ -190,7 +190,7 @@ def clean_input(filename, outname): # Find "%tokens:" # Find "rule:" and replace with just "rule:" - global Rules + global Rules # pylint: disable=global-variable-undefined Rules = {} types = {} @@ -200,12 +200,12 @@ def clean_input(filename, outname): if True: linesin = lines lines = [] - l = 0 + lineno = 0 for line in linesin: - l += 1 + lineno += 1 # ^/ to prevent comments from matching if re.match(r'^[a-zA-Z0-9_<>]+:[^/]*[a-zA-Z]', line): - sys.exit("%Error: " + filename + ":" + str(l) + + sys.exit("%Error: " + filename + ":" + str(lineno) + ": Move text on rule line to next line: " + line + "\n") @@ -223,7 +223,7 @@ def clean_input(filename, outname): dtype = matcha.group(2) line = name + matcha.group(3) if name in Rules: - sys.exit("%Error: " + filename + ":" + str(l) + + sys.exit("%Error: " + filename + ":" + str(lineno) + ": Redeclaring '" + name + "': " + line) if dtype not in types: types[dtype] = {} @@ -235,14 +235,14 @@ def clean_input(filename, outname): 'subrules': {} } if last_rule: - sys.exit("%Error: " + filename + ":" + str(l) + + sys.exit("%Error: " + filename + ":" + str(lineno) + ": Unterminated previous rule\n") last_rule = name elif matchb: name = matchb.group(1) - if name != 'public' and name != 'private': + if name not in ('public', 'private'): if name in Rules: - sys.exit("%Error: " + filename + ":" + str(l) + + sys.exit("%Error: " + filename + ":" + str(lineno) + ": Redeclaring '" + name + "': " + line) Rules[name] = { 'name': name, @@ -251,17 +251,16 @@ def clean_input(filename, outname): 'subrules': {} } if last_rule: - sys.exit("%Error: " + filename + ":" + str(l) + + sys.exit("%Error: " + filename + ":" + str(lineno) + ": Unterminated previous rule\n") last_rule = name lines.append(line) # Now clean the line and extract some more info cline = re.sub(r'//.*$', '\n', line) - rline = re.sub(r'//.*$', '\n', line) if re.match(r'^\s*;', cline): if not last_rule: - sys.exit("%Error: " + filename + ":" + str(l) + + sys.exit("%Error: " + filename + ":" + str(lineno) + ": Stray semicolon\n") last_rule = None elif last_rule: @@ -272,7 +271,7 @@ def clean_input(filename, outname): dtype = match.group(1) tok = match.group(2) if tok in tokens: - sys.exit("%Error: " + filename + ":" + str(l) + + sys.exit("%Error: " + filename + ":" + str(lineno) + ": Redeclaring '" + tok + "': " + line) tokens[tok] = dtype @@ -281,22 +280,22 @@ def clean_input(filename, outname): # print("TT "+last_rule+" "+tok+"\n") Rules[last_rule]['subrules'][tok] = 1 - #pprint(Rules) + # pprint(Rules) # Replace BISONPRE_VERSION(ver,,...) with expanded list if True: linesin = lines lines = [] - l = 0 + lineno = 0 for line in linesin: - l += 1 + lineno += 1 if _enaline(line) and re.search(r'BISONPRE_VERSION', line): # 1 2 3 4 match = re.search( r'BISONPRE_VERSION\((\S+)\s*,\s*((\S+)\s*,)?\s*([^\),]+)\)\s*$', line) if not match: - sys.exit("%Error: " + filename + ":" + str(l) + + sys.exit("%Error: " + filename + ":" + str(lineno) + ": Bad form of BISONPRE_VERSION: " + line) ver = match.group(1) ver_max = match.group(3) @@ -312,28 +311,28 @@ def clean_input(filename, outname): if True: linesin = lines lines = [] - l = 0 + lineno = 0 for line in linesin: - l += 1 + lineno += 1 if _enaline(line) and re.search(r'BISONPRE_NOT', line): match = re.search( r'(.*)BISONPRE_NOT\((\S+)\)\s*(\{[^}]+})\s*(.*)$', line, flags=re.DOTALL) if not match: - sys.exit("%Error: " + filename + ":" + str(l) + + sys.exit("%Error: " + filename + ":" + str(lineno) + ": Bad form of BISONPRE_NOT: " + line) line = match.group(1) + match.group(4) endtok = match.group(2) action = match.group(3) endtoks = endtok.split(',') for etok in endtoks: - if not etok in tokens: - sys.exit("%Error: " + filename + ":" + str(l) + + if etok not in tokens: + sys.exit("%Error: " + filename + ":" + str(lineno) + ": Can't find definition for token: " + etok + "\n") # Push it all onto one line to avoid error messages changing - bar = "" + pipe = "" for tok in sorted(tokens.keys()): hit = False for etok in endtoks: @@ -341,8 +340,8 @@ def clean_input(filename, outname): hit = True break if not hit and endtok != tok: - line += "\t" + bar + " " + tok + " " + action - bar = "|" + line += "\t" + pipe + " " + tok + " " + action + pipe = "|" line += "\n" lines.append(line) @@ -350,20 +349,20 @@ def clean_input(filename, outname): if True: linesin = lines lines = [] - l = 0 + lineno = 0 for line in linesin: - l += 1 + lineno += 1 if _enaline(line) and re.search(r'BISONPRE_COPY', line): - line = _bisonpre_copy(line, l, 0) + line = _bisonpre_copy(line, lineno, 0) lines.append(line) # Replace ~[x]~ - must be after BISONPRE_COPY expansion if True: linesin = lines lines = [] - l = 0 + lineno = 0 for line in linesin: - l += 1 + lineno += 1 line = re.sub(r'~[a-zA-Z0-9_]+~', '', line) lines.append(line) @@ -371,17 +370,17 @@ def clean_input(filename, outname): if True: linesin = lines lines = [] - l = 0 + lineno = 0 needmore = 0 for line in linesin: - l += 1 + lineno += 1 if _enaline(line) and re.search(r'//BISONPRE_TYPES', line): lines.append(line) - for type in sorted(types.keys()): - if not type: + for typen in sorted(types.keys()): + if not typen: continue - line = "%type<" + type + ">\t" - for rule in sorted(types[type].keys()): + line = "%type<" + typen + ">\t" + for rule in sorted(types[typen].keys()): line += " " + rule line += "\n" lines.append(line) @@ -391,7 +390,7 @@ def clean_input(filename, outname): line = re.sub(r'^\s*//.*$', '', line) if not re.match(r'^\s*$', line): sys.exit( - "%Error: " + filename + ":" + str(l) + ": Need " + + "%Error: " + filename + ":" + str(lineno) + ": Need " + needmore + " more blank lines to keep line numbers are constant\n" ) @@ -404,7 +403,7 @@ def clean_input(filename, outname): fh.write(line) -def _bisonpre_copy(text, l, depth): +def _bisonpre_copy(text, lineno, depth): while re.search(r'BISONPRE_COPY', text): match = re.match( # 1 2 3 4 5 @@ -412,14 +411,14 @@ def _bisonpre_copy(text, l, depth): text, flags=re.DOTALL) if not match: - sys.exit("%Error: " + Filename + ":" + str(l) + + sys.exit("%Error: " + Filename + ":" + str(lineno) + ": Bad form of BISONPRE_NOT: " + text) text = match.group(1) + '{HERE}' + match.group(5) once = match.group(2) rule = match.group(3) code = match.group(4) - if not rule in Rules: - sys.exit("%Error: " + Filename + ":" + str(l) + + if rule not in Rules: + sys.exit("%Error: " + Filename + ":" + str(lineno) + ": Can't find definition for rule: " + rule) if depth > 0 and once: # _ONCE means don't inherit @@ -430,20 +429,17 @@ def _bisonpre_copy(text, l, depth): insert = Rules[rule]['rules_and_productions'] insert = re.sub(r'^\S+:', '', insert) # Strip rule name # Recurse so BISONPRE under B - #print "COPY $l code $code\n" - #print "COPY $l in $insert\n" for op in code.split(';'): if re.match(r'^\s*$', op): continue match = re.match(r'^\s*s/(.*?)/(.*?)/g\s*$', op) if not match: - sys.exit("%Error: " + Filename + ":" + str(l) + + sys.exit("%Error: " + Filename + ":" + str(lineno) + ": Didn't understand replacement: " + op) left = match.group(1) right = match.group(2) insert = re.sub(left, right, insert) - #print "COPY $l out $insert\n" insert = re.sub(r'[ \t\n]+\n', "\n", insert) insert = re.sub(r'\n', " ", insert) # Optional - preserve line numbering @@ -556,6 +552,6 @@ Args = parser.parse_args() process() ###################################################################### -### Local Variables: -### compile-command: "./bisonpre " -### End: +# Local Variables: +# compile-command: "./bisonpre " +# End: diff --git a/src/config_rev b/src/config_rev index a3dde7222..50c477e55 100755 --- a/src/config_rev +++ b/src/config_rev @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# pylint: disable=C0103,C0114 ###################################################################### # # Copyright 2005-2021 by Wilson Snyder. This program is free software; you diff --git a/src/cppcheck_filtered b/src/cppcheck_filtered index 4e0f88128..c1011c00b 100755 --- a/src/cppcheck_filtered +++ b/src/cppcheck_filtered @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# pylint: disable=C0103,C0114,C0115,C0116,R0911,R0912,R0915,W0621 ###################################################################### import argparse @@ -88,8 +89,8 @@ def process(cppcheck_args): file = match.group(1) linenum = match.group(2) match = re.search(r' id="([^"]+)"', last_error) - id = match.group(1) if match else '?' - if _suppress(file, linenum, id): + eid = match.group(1) if match else '?' + if _suppress(file, linenum, eid): suppress = True if file == "*": suppress = True @@ -109,9 +110,9 @@ def process(cppcheck_args): ###################################################################### -def _suppress(filename, linenum, id): +def _suppress(filename, linenum, eid): if Args.debug: - print("-Suppression search %s %s %s" % (filename, linenum, id)) + print("-Suppression search %s %s %s" % (filename, linenum, eid)) if filename == "*": return False @@ -120,17 +121,17 @@ def _suppress(filename, linenum, id): filename = re.sub(r'^\.\./(.*)', r'src/\1', filename) # Specific suppressions - if id == 'missingInclude' and re.search(r'systemc.h', filename): + if eid == 'missingInclude' and re.search(r'systemc.h', filename): return True - if id == 'missingInclude' and re.search(r'svdpi.h', filename): + if eid == 'missingInclude' and re.search(r'svdpi.h', filename): return True - if id == 'unusedFunction' and re.search(r'verilated_dpi.cpp', filename): + if eid == 'unusedFunction' and re.search(r'verilated_dpi.cpp', filename): return True - if id == 'unusedFunction' and re.search(r'verilated_vpi.cpp', filename): + if eid == 'unusedFunction' and re.search(r'verilated_vpi.cpp', filename): return True - if id == 'unreachableCode' and re.search(r'V3ParseBison.c', filename): + if eid == 'unreachableCode' and re.search(r'V3ParseBison.c', filename): return True - if id == 'variableScope' and re.search(r'fstapi.c', filename): + if eid == 'variableScope' and re.search(r'fstapi.c', filename): return True if not os.path.exists(filename): @@ -139,15 +140,15 @@ def _suppress(filename, linenum, id): return False with open(filename) as fh: - l = 0 + lineno = 0 for line in fh: - l += 1 - if (l + 1 == linenum): + lineno += 1 + if lineno + 1 == linenum: match = re.search(r'cppcheck-suppress((\s+\S+)+)', line) if match: for supid in match.group(1).split(): - if (supid == id or - (id in SuppressMap and supid == SuppressMap[id])): + if (supid == eid or (eid in SuppressMap + and supid == SuppressMap[eid])): return True return False @@ -178,6 +179,6 @@ Args, cppcheck_args = parser.parse_known_args() process(cppcheck_args) ###################################################################### -### Local Variables: -### compile-command: "cd .. ; src/cppcheck_filtered cppcheck --xml --enable=all src/V3Width.cpp" -### End: +# Local Variables: +# compile-command: "cd .. ; src/cppcheck_filtered cppcheck --xml --enable=all src/V3Width.cpp" +# End: diff --git a/src/flexfix b/src/flexfix index c83cba61f..476907fb1 100755 --- a/src/flexfix +++ b/src/flexfix @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# pylint: disable=C0114,C0301 ###################################################################### # # Copyright 2002-2021 by Wilson Snyder. This program is free software; you @@ -10,7 +11,6 @@ ###################################################################### # DESCRIPTION: Edits flex output to get around various broken flex issues. -import argparse import re import sys @@ -29,7 +29,7 @@ for line in sys.stdin: r' (int)number_to_move == (int)YY_MORE_ADJ ', line) # Fix flex 2.5.4 namespace omission line = re.sub(r'^class istream;', - '\#include \nusing namespace std;\n', line) + '#include \nusing namespace std;\n', line) # Fix flex 2.5.31 redefinition line = re.sub(r'(\#define\s+yyFlexLexer\s+yyFlexLexer)', r'//flexfix: \1', line) diff --git a/src/vlcovgen b/src/vlcovgen index a665e071c..a62b8253f 100755 --- a/src/vlcovgen +++ b/src/vlcovgen @@ -1,11 +1,11 @@ #!/usr/bin/env python3 +# pylint: disable=C0103,C0114,C0116,eval-used ###################################################################### import argparse import re -import os import sys -from pprint import pprint +# from pprint import pprint Items = [] @@ -65,9 +65,7 @@ def write_keys(filename): elif re.search(r'VLCOVGEN_.*AUTO_EDIT_END', line): deleting = False out.append(line) - elif deleting: - None - else: + elif not deleting: out.append(line) ok = "".join(out) == "".join(orig) @@ -105,6 +103,6 @@ lint() write_keys(Args.srcdir + "/../include/verilated_cov_key.h") ###################################################################### -### Local Variables: -### compile-command: "./vlcovgen --srcdir ." -### End: +# Local Variables: +# compile-command: "./vlcovgen --srcdir ." +# End: diff --git a/test_regress/driver.pl b/test_regress/driver.pl index 48fd12a05..2796baabf 100755 --- a/test_regress/driver.pl +++ b/test_regress/driver.pl @@ -474,8 +474,10 @@ sub sprint_summary { my $delta = time() - $::Start; my $leftmsg = $::Have_Forker ? $self->{left_cnt} : "NO-FORKER"; my $pct = int(100*($self->{left_cnt} / ($self->{all_cnt} + 0.001)) + 0.999); - my $eta = ($self->{all_cnt} - * ($delta / (($self->{all_cnt} - $self->{left_cnt})+0.001))) - $delta; + # Fudge of 120% works out about right so ETA correctly predicts completion time + my $eta = 1.2 * (($self->{all_cnt} + * ($delta / (($self->{all_cnt} - $self->{left_cnt})+0.001))) + - $delta); $eta = 0 if $delta < 10; my $out = ""; $out .= "Left $leftmsg " if $self->{left_cnt}; @@ -1009,6 +1011,7 @@ sub compile { cmd=>[($ENV{VERILATOR_VCS}||"vcs"), @{$param{vcs_flags}}, @{$param{vcs_flags2}}, + ($opt_verbose ? " -CFLAGS -DTEST_VERBOSE=1":""), @{$param{v_flags}}, @{$param{v_flags2}}, $param{top_filename}, @@ -1157,7 +1160,7 @@ sub compile { entering => "$self->{obj_dir}", cmd => [$ENV{MAKE}, "-C ".$self->{obj_dir}, - "-f ".$::RealBin."/Makefile_obj", + "-f ".$FindBin::RealBin."/Makefile_obj", ($self->{verbose} ? "" : "--no-print-directory"), "VM_PREFIX=$self->{VM_PREFIX}", "TEST_OBJ_DIR=$self->{obj_dir}", @@ -1330,6 +1333,7 @@ sub execute { %param, expect=>$param{expect}, # backward compatible name expect_filename=>$param{expect_filename}, # backward compatible name + verilator_run => 1, ); } else { @@ -1545,23 +1549,28 @@ sub _run { #entering => # Print entering directory information #verilator_run => # Move gcov data to parallel area @_); + my $command = join(' ',@{$param{cmd}}); $command = "time $command" if $opt_benchmark && $command !~ /^cd /; - print "\t$command"; - print " > $param{logfile}" if $param{logfile}; - print "\n"; if ($param{verilator_run}) { # Gcov fails when parallel jobs write same data file, - # so we make sure output dir is unique across all running jobs. - # We can't just put each one in obj_dir as it uses too much disk. + # so we make sure .gcda output dir is unique across all running jobs. + # We can't just put each one in a unique obj_dir as it uses too much disk. + # Must use absolute path as some execute()s have different PWD $ENV{GCOV_PREFIX_STRIP} = 99; - $ENV{GCOV_PREFIX} = "$self->{t_dir}/obj_dist/gcov_$self->{running_id}"; + $ENV{GCOV_PREFIX} = File::Spec->rel2abs("$FindBin::RealBin/obj_dist/gcov_$self->{running_id}"); + mkdir $ENV{GCOV_PREFIX}; + print "export GCOV_PREFIX_STRIP=99 GCOV_PREFIX=$ENV{GCOV_PREFIX}\n" if $self->{verbose}; } else { delete $ENV{GCOV_PREFIX_STRIP}; delete $ENV{GCOV_PREFIX}; } + print "\t$command"; + print " > $param{logfile}" if $param{logfile}; + print "\n"; + # Execute command redirecting output, keeping order between stderr and stdout. # Must do low-level IO so GCC interaction works (can't be line-based) my $status; @@ -1735,15 +1744,6 @@ sub _make_main { print $fh "#include \"verilated_save.h\"\n" if $self->{savable}; print $fh "std::unique_ptr<$VM_PREFIX> topp;\n"; - if (!$self->sc) { - if ($self->{vl_time_stamp64}) { - print $fh "vluint64_t main_time = 0;\n"; - print $fh "vluint64_t vl_time_stamp64() { return main_time; }\n"; - } else { - print $fh "double main_time = 0;\n"; - print $fh "double sc_time_stamp() { return main_time; }\n"; - } - } if ($self->{savable}) { $fh->print("\n"); @@ -1751,7 +1751,6 @@ sub _make_main { $fh->print(" VL_PRINTF(\"Saving model to '%s'\\n\", filenamep);\n"); $fh->print(" VerilatedSave os;\n"); $fh->print(" os.open(filenamep);\n"); - $fh->print(" os << main_time;\n"); $fh->print(" os << *topp;\n"); $fh->print(" os.close();\n"); $fh->print("}\n"); @@ -1760,7 +1759,6 @@ sub _make_main { $fh->print(" VL_PRINTF(\"Restoring model from '%s'\\n\", filenamep);\n"); $fh->print(" VerilatedRestore os;\n"); $fh->print(" os.open(filenamep);\n"); - $fh->print(" os >> main_time;\n"); $fh->print(" os >> *topp;\n"); $fh->print(" os.close();\n"); $fh->print("}\n"); @@ -1776,14 +1774,16 @@ sub _make_main { print $fh " sc_time sim_time($self->{sim_time}, $Self->{sc_time_resolution});\n"; } else { print $fh "int main(int argc, char** argv, char** env) {\n"; - print $fh " double sim_time = $self->{sim_time};\n"; + print $fh " vluint64_t sim_time = $self->{sim_time};\n"; } - print $fh " Verilated::commandArgs(argc, argv);\n"; - print $fh " Verilated::debug(".($self->{verilated_debug}?1:0).");\n"; + + print $fh " const std::unique_ptr contextp{new VerilatedContext};\n"; + print $fh " contextp->commandArgs(argc, argv);\n"; + print $fh " contextp->debug(".($self->{verilated_debug}?1:0).");\n"; print $fh " srand48(5);\n"; # Ensure determinism - print $fh " Verilated::randReset(".$self->{verilated_randReset}.");\n" if defined $self->{verilated_randReset}; + print $fh " contextp->randReset(".$self->{verilated_randReset}.");\n" if defined $self->{verilated_randReset}; print $fh " topp.reset(new $VM_PREFIX(\"top\"));\n"; - print $fh " Verilated::internalsDump()\n;" if $self->{verilated_debug}; + print $fh " contextp->internalsDump()\n;" if $self->{verilated_debug}; my $set; if ($self->sc) { @@ -1798,22 +1798,22 @@ sub _make_main { if ($self->{trace}) { $fh->print("\n"); $fh->print("#if VM_TRACE\n"); - $fh->print(" Verilated::traceEverOn(true);\n"); + $fh->print(" contextp->traceEverOn(true);\n"); $fh->print(" std::unique_ptr tfp{new VerilatedFstC};\n") if $self->{trace_format} eq 'fst-c'; $fh->print(" std::unique_ptr tfp{new VerilatedVcdC};\n") if $self->{trace_format} eq 'vcd-c'; $fh->print(" std::unique_ptr tfp{new VerilatedVcdSc};\n") if $self->{trace_format} eq 'vcd-sc'; $fh->print(" topp->trace(tfp.get(), 99);\n"); $fh->print(" tfp->open(\"".$self->trace_filename."\");\n"); if ($self->{trace} && !$self->sc) { - $fh->print(" if (tfp) tfp->dump(main_time);\n"); + $fh->print(" if (tfp) tfp->dump(contextp->time());\n"); } $fh->print("#endif\n"); } if ($self->{savable}) { - $fh->print(" const char* save_time_strp = Verilated::commandArgsPlusMatch(\"save_time=\");\n"); + $fh->print(" const char* save_time_strp = contextp->commandArgsPlusMatch(\"save_time=\");\n"); $fh->print(" unsigned int save_time = !save_time_strp[0] ? 0 : atoi(save_time_strp+strlen(\"+save_time=\"));\n"); - $fh->print(" const char* save_restore_strp = Verilated::commandArgsPlusMatch(\"save_restore=\");\n"); + $fh->print(" const char* save_restore_strp = contextp->commandArgsPlusMatch(\"save_restore=\");\n"); $fh->print(" unsigned int save_restore = !save_restore_strp[0] ? 0 : 1;\n"); } @@ -1829,8 +1829,11 @@ sub _make_main { _print_advance_time($self, $fh, 10); print $fh " }\n"; - print $fh " while ((sc_time_stamp() < sim_time * MAIN_TIME_MULTIPLIER)\n"; - print $fh " && !Verilated::gotFinish()) {\n"; + my $time = $self->sc ? "sc_time_stamp()" : "contextp->time()"; + + print $fh " while ((${time} < sim_time * MAIN_TIME_MULTIPLIER)\n"; + print $fh " && !contextp->gotFinish()) {\n"; + for (my $i=0; $i<5; $i++) { my $action = 0; if ($self->{inputs}{fastclk}) { @@ -1842,7 +1845,7 @@ sub _make_main { $action = 1; } if ($self->{savable}) { - $fh->print(" if (sc_time_stamp() == save_time && save_time) {\n"); + $fh->print(" if (save_time && ${time} == save_time) {\n"); $fh->print(" save_model(\"$self->{obj_dir}/saved.vltsv\");\n"); $fh->print(" printf(\"Exiting after save_model\\n\");\n"); $fh->print(" return 0;\n"); @@ -1851,7 +1854,7 @@ sub _make_main { _print_advance_time($self, $fh, 1, $action); } print $fh " }\n"; - print $fh " if (!Verilated::gotFinish()) {\n"; + print $fh " if (!contextp->gotFinish()) {\n"; print $fh ' vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");',"\n"; print $fh " }\n"; print $fh " topp->final();\n"; @@ -1892,11 +1895,11 @@ sub _print_advance_time { print $fh " ${set}eval();\n"; if ($self->{trace} && !$self->sc) { $fh->print("#if VM_TRACE\n"); - $fh->print(" if (tfp) tfp->dump(main_time);\n"); + $fh->print(" if (tfp) tfp->dump(contextp->time());\n"); $fh->print("#endif // VM_TRACE\n"); } } - print $fh " main_time += ${time} * MAIN_TIME_MULTIPLIER;\n"; + print $fh " contextp->timeInc(${time} * MAIN_TIME_MULTIPLIER);\n"; } } @@ -2599,7 +2602,7 @@ can be used: This can be particularly useful if checking that the Verilator model has not unexpectedly terminated. - if (Verilated::gotFinish()) { + if (contextp->gotFinish()) { vl_fatal(__FILE__, __LINE__, "dut", ""); exit(1); } diff --git a/test_regress/t/TestCheck.h b/test_regress/t/TestCheck.h new file mode 100644 index 000000000..947afaed9 --- /dev/null +++ b/test_regress/t/TestCheck.h @@ -0,0 +1,73 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2013-2017 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#ifndef TEST_CHECK_H_ +#define TEST_CHECK_H_ + +extern int errors; + +#ifdef TEST_VERBOSE +static const bool verbose = true; +#else +static const bool verbose = false; +#endif + +//====================================================================== + +// Use cout to avoid issues with %d/%lx etc +#define TEST_CHECK(got, exp, test) \ + do { \ + if (!(test)) { \ + std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ \ + << ": GOT = " << (got) << " EXP = " << (exp) << std::endl; \ + ++errors; \ + } \ + } while (0) + +#define TEST_CHECK_EQ(got, exp) TEST_CHECK(got, exp, ((got) == (exp))); +#define TEST_CHECK_NE(got, exp) TEST_CHECK(got, exp, ((got) != (exp))); +#define TEST_CHECK_CSTR(got, exp) TEST_CHECK(got, exp, 0 == strcmp((got), (exp))); + +#define TEST_CHECK_HEX_EQ(got, exp) \ + do { \ + if ((got) != (exp)) { \ + std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << std::hex \ + << ": GOT=" << (got) << " EXP=" << (exp) << std::endl; \ + ++errors; \ + } \ + } while (0) + +#define TEST_CHECK_HEX_NE(got, exp) \ + do { \ + if ((got) == (exp)) { \ + std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << std::hex \ + << ": GOT=" << (got) << " EXP!=" << (exp) << std::endl; \ + ++errors; \ + } \ + } while (0) + +#define TEST_CHECK_NZ(got) \ + do { \ + if (!(got)) { \ + std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << std::hex \ + << ": GOT= NULL EXP!=NULL" << std::endl; \ + ++errors; \ + } \ + } while (0) + +//====================================================================== + +#define TEST_VERBOSE_PRINTF(format, ...) \ + do { \ + if (verbose) printf(format, ##__VA_ARGS__); \ + } while (0) + +#endif // Guard diff --git a/test_regress/t/t_assign_slice_overflow.pl b/test_regress/t/t_assign_slice_overflow.pl new file mode 100755 index 000000000..2cb5eeaff --- /dev/null +++ b/test_regress/t/t_assign_slice_overflow.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 2021 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_assign_slice_overflow.v b/test_regress/t/t_assign_slice_overflow.v new file mode 100644 index 000000000..8cee5b0d3 --- /dev/null +++ b/test_regress/t/t_assign_slice_overflow.v @@ -0,0 +1,179 @@ +// DESCRIPTION: Test that slice assignment overflows are handled correctly, +// i.e. that if you assign to a slice such that some of the bits you assign to +// do not actually exist, that those bits get correctly discarded. +// Issue #2803 existed in a number number of different codepaths in +// verilated.h and V3Expand.cpp. This test should cover all of these cases +// when run both with and without the -Ox flag to verilator. +// - Select offset constant, insert IData into CData +// - Select offset constant, insert IData into SData +// - Select offset constant, insert IData into IData +// - Select offset constant, insert QData into QData +// - Select offset constant, insert IData into WData within a word +// - Select offset constant, insert IData into WData crossing a word boundary +// - Select offset constant, insert IData into WData whole word insertion +// - Select offset constant, insert QData into WData +// - Select offset constant, insert WData into WData, several whole words +// - Select offset constant, insert WData into WData, starting at word-offset +// - Select offset constant, insert WData into WData, all other cases +// - Select offset is non-constant, destination is wide, bit-select width == 1 +// - Select offset is non-constant, destination is wide, bit-select width != 1 +// - Select offset is non-constant, destination is narrow +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2021 by David Turner. +// SPDX-License-Identifier: CC0-1.0 + +module t(/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + integer cyc=0; + // Non-constant offsets + reg varoffset1; + reg [6:0] varoffset2; + reg [6:0] varoffset3; + + // Destinations for variable-offset assignments + reg [69:0] dstwide1; + reg [69:0] dstwide2; + reg [1:0] dstnarrow; + + // Constant offsets + reg [6:0] constoffset; + + // Destinations for constant-offset assignments + reg [2:0] dst_cdata; + reg [11:0] dst_sdata; + reg [29:0] dst_idata; + reg [59:0] dst_qdata; + reg [69:0] dst_wdata1; // assign idata within word + reg [69:0] dst_wdata2; // assign idata crossing word boundary + reg [69:0] dst_wdata3; // assign idata corresponding to whole word + reg [69:0] dst_wdata4; // assign qdata + reg [69:0] dst_wdata5; // assign wdata corresponding to several whole words + reg [69:0] dst_wdata6; // assign wdata starting at word-offset + reg [69:0] dst_wdata7; // assign wdata unaligned + + always @(*) begin + // Non-constant select offset, destination narrow + dstnarrow = 2'd0; + dstnarrow[varoffset1 +: 2'd2] = 2'd2; + + // Non-constant select offset, destination wide, width == 1 + dstwide1 = 70'd0; + dstwide1[varoffset2 +: 1'd1] = 1'd1; + + // Non-constant select offset, destination wide, width != 1 + dstwide2 = 70'd0; + dstwide2[varoffset3 +: 2'd2] = 2'd2; + + // Constant offset, IData into CData + constoffset = 7'd2; + dst_cdata = 3'd0; + dst_cdata[constoffset[0 +: 2] +: 3'd3] = 3'd6; + + // Constant offset, IData into SData + constoffset = 7'd11; + dst_sdata = 12'd0; + dst_sdata[constoffset[0 +: 4] +: 2'd2] = 2'd2; + + // Constant offset, IData into IData + constoffset = 7'd29; + dst_idata = 30'd0; + dst_idata[constoffset[0 +: 5] +: 2'd2] = 2'd2; + + // Constant offset, QData into QData + constoffset = 7'd59; + dst_qdata = 60'd0; + dst_qdata[constoffset[0 +: 6] +: 2'd2] = 2'd2; + + // Constant offset, IData into WData within word + constoffset = 7'd69; + dst_wdata1 = 70'd0; + dst_wdata1[constoffset +: 2'd2] = 2'd2; + + // Constant offset, IData into WData crossing word boundary + constoffset = 7'd61; + dst_wdata2 = 70'd0; + dst_wdata2[constoffset +: 4'd10] = 10'd1 << 4'd9; + + // Constant offset, IData into WData replacing a whole word + constoffset = 7'd64; + dst_wdata3 = 70'd0; + dst_wdata3[constoffset +: 6'd32] = 32'd1 << 3'd6; + + // Constant offset, QData into WData + constoffset = 7'd31; + dst_wdata4 = 70'd0; + dst_wdata4[constoffset +: 7'd40] = 40'd1 << 7'd39; + + // Constant offset, WData into WData replacing whole words + constoffset = 7'd32; + dst_wdata5 = 70'd0; + dst_wdata5[constoffset +: 7'd64] = 64'd1 << 7'd38; + + // Constant offset, WData into WData offset word aligned + constoffset = 7'd32; + dst_wdata6 = 70'd0; + dst_wdata6[constoffset +: 7'd40] = 40'd1 << 7'd38; + + // Constant offset, WData into WData unaligned + constoffset = 7'd1; + dst_wdata7 = 70'd0; + dst_wdata7[constoffset +: 7'd70] = 70'd1 << 7'd69; + end + + // Test loop + always @ (posedge clk) begin + // State machine to avoid verilator constant-folding offset + if (cyc == 0) begin + // Initialisation + varoffset1 <= 1'd0; + varoffset2 <= 7'd0; + varoffset3 <= 7'd0; + end else if (cyc == 1) begin + // Variable offsets set here to avoid verilator constant folding + varoffset1 <= 1'd1; + varoffset2 <= 7'd70; + varoffset3 <= 7'd69; + end else if (cyc == 2) begin + // Check all destinations are 0 + $write("dstwide1 = %23d, downshifted = %23d\n", dstwide1, dstwide1 >> 1); + $write("dstwide2 = %23d, downshifted = %23d\n", dstwide2, dstwide2 >> 1); + $write("dstnarrow = %23d, downshifted = %23d\n", dstnarrow, dstnarrow >> 1); + $write("dst_cdata = %23d, downshifted = %23d\n", dst_cdata, dst_cdata >> 1); + $write("dst_sdata = %23d, downshifted = %23d\n", dst_sdata, dst_sdata >> 1); + $write("dst_idata = %23d, downshifted = %23d\n", dst_idata, dst_idata >> 1); + $write("dst_qdata = %23d, downshifted = %23d\n", dst_qdata, dst_qdata >> 1); + $write("dst_wdata1 = %23d, downshifted = %23d\n", dst_wdata1, dst_wdata1 >> 1); + $write("dst_wdata2 = %23d, downshifted = %23d\n", dst_wdata2, dst_wdata2 >> 1); + $write("dst_wdata3 = %23d, downshifted = %23d\n", dst_wdata3, dst_wdata3 >> 1); + $write("dst_wdata4 = %23d, downshifted = %23d\n", dst_wdata4, dst_wdata4 >> 1); + $write("dst_wdata5 = %23d, downshifted = %23d\n", dst_wdata5, dst_wdata5 >> 1); + $write("dst_wdata6 = %23d, downshifted = %23d\n", dst_wdata6, dst_wdata6 >> 1); + $write("dst_wdata7 = %23d, downshifted = %23d\n", dst_wdata7, dst_wdata7 >> 1); + + if (dstwide1 !== 70'd0 || (dstwide1 >> 1) !== 70'd0) $stop; + if (dstwide2 !== 70'd0 || (dstwide2 >> 1) !== 70'd0) $stop; + if (dstnarrow !== 2'd0 || (dstnarrow >> 1) !== 2'd0) $stop; + if (dst_cdata !== 3'd0 || (dst_cdata >> 1) !== 3'd0) $stop; + if (dst_sdata !== 12'd0 || (dst_sdata >> 1) !== 12'd0) $stop; + if (dst_idata !== 30'd0 || (dst_idata >> 1) !== 30'd0) $stop; + if (dst_qdata !== 60'd0 || (dst_qdata >> 1) !== 60'd0) $stop; + if (dst_wdata1 !== 70'd0 || (dst_wdata1 >> 1) !== 70'd0) $stop; + if (dst_wdata2 !== 70'd0 || (dst_wdata2 >> 1) !== 70'd0) $stop; + if (dst_wdata3 !== 70'd0 || (dst_wdata3 >> 1) !== 70'd0) $stop; + if (dst_wdata4 !== 70'd0 || (dst_wdata4 >> 1) !== 70'd0) $stop; + if (dst_wdata5 !== 70'd0 || (dst_wdata5 >> 1) !== 70'd0) $stop; + if (dst_wdata6 !== 70'd0 || (dst_wdata6 >> 1) !== 70'd0) $stop; + if (dst_wdata7 !== 70'd0 || (dst_wdata7 >> 1) !== 70'd0) $stop; + end else begin + $write("*-* All Finished *-*\n"); + $finish; + end + + cyc <= cyc + 1; + end +endmodule diff --git a/test_regress/t/t_assign_slice_overflow_ox.pl b/test_regress/t/t_assign_slice_overflow_ox.pl new file mode 100755 index 000000000..5251be495 --- /dev/null +++ b/test_regress/t/t_assign_slice_overflow_ox.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 2021 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_assign_slice_overflow.v"); + +compile( + verilator_flags2 => ["-Ox"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_clk_inp_init.cpp b/test_regress/t/t_clk_inp_init.cpp index 79c85635a..4d2e17463 100644 --- a/test_regress/t/t_clk_inp_init.cpp +++ b/test_regress/t/t_clk_inp_init.cpp @@ -8,24 +8,23 @@ // General headers #include "verilated.h" -Vt_clk_inp_init* topp; - -vluint64_t main_time; -double sc_time_stamp() { return main_time; } - -void oneTest(int seed) { +void oneTest(int argc, char** argv, int seed) { vluint64_t sim_time = 1000; #ifdef TEST_VERBOSE VL_PRINTF("== Seed=%d\n", seed); #endif + const std::unique_ptr contextp{new VerilatedContext}; + contextp->commandArgs(argc, argv); + // Randomise initial state srand48(seed); srand48(5); - Verilated::randReset(123); + contextp->randReset(123); - topp = new Vt_clk_inp_init("top"); + // Construct the Verilated model, from Vtop.h generated from Verilating + const std::unique_ptr topp{new Vt_clk_inp_init{contextp.get()}}; // Start not in reset topp->rst_n = 1; @@ -33,33 +32,31 @@ void oneTest(int seed) { topp->eval(); // Tick for a little bit - while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { + while (contextp->time() < sim_time && !contextp->gotFinish()) { topp->clk = 0; topp->eval(); - main_time += 5; + contextp->timeInc(5); topp->clk = 1; topp->eval(); - main_time += 5; + contextp->timeInc(5); } - if (!Verilated::gotFinish()) { + if (!contextp->gotFinish()) { vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish"); } topp->final(); - VL_DO_DANGLING(delete topp, topp); } int main(int argc, char** argv, char** env) { - Verilated::commandArgs(argc, argv); #if VL_DEBUG // Verilated::debug(1); #endif - for (int seed = 123; seed < 133; ++seed) oneTest(seed); + for (int seed = 123; seed < 133; ++seed) oneTest(argc, argv, seed); return 0; } diff --git a/test_regress/t/t_const_op_red_scope.pl b/test_regress/t/t_const_op_red_scope.pl new file mode 100755 index 000000000..2cb5eeaff --- /dev/null +++ b/test_regress/t/t_const_op_red_scope.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 2021 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_const_op_red_scope.v b/test_regress/t/t_const_op_red_scope.v new file mode 100644 index 000000000..e36cd4d8a --- /dev/null +++ b/test_regress/t/t_const_op_red_scope.v @@ -0,0 +1,129 @@ +// 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, 2021 by ____YOUR_NAME_HERE____. +// SPDX-License-Identifier: CC0-1.0 + +module t(/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + integer cyc=0; + reg [63:0] crc; + reg [63:0] sum; + + // Take CRC data and apply to testblock inputs + wire [7:0] in = crc[7:0]; + + /*AUTOWIRE*/ + + wire out0; + wire out1; + wire out2; + wire out3; + wire out4; + wire out5; + wire out6; + wire out7; + + /*SelFlop AUTO_TEMPLATE(.n(@), + .out(out@)); */ + + SelFlop selflop0(/*AUTOINST*/ + // Outputs + .out (out0), // Templated + // Inputs + .clk (clk), + .in (in[7:0]), + .n (0)); // Templated + SelFlop selflop1(/*AUTOINST*/ + // Outputs + .out (out1), // Templated + // Inputs + .clk (clk), + .in (in[7:0]), + .n (1)); // Templated + SelFlop selflop2(/*AUTOINST*/ + // Outputs + .out (out2), // Templated + // Inputs + .clk (clk), + .in (in[7:0]), + .n (2)); // Templated + SelFlop selflop3(/*AUTOINST*/ + // Outputs + .out (out3), // Templated + // Inputs + .clk (clk), + .in (in[7:0]), + .n (3)); // Templated + + // Aggregate outputs into a single result vector + wire outo = out0|out1|out2|out3; + wire outa = out0&out1&out2&out3; + wire outx = out0^out1^out2^out3; + wire [63:0] result = {61'h0, outo, outa, outx}; + + // Test loop + always @ (posedge clk) begin +`ifdef TEST_VERBOSE + $write("[%0t] cyc==%0d crc=%x result=%x\n",$time, cyc, crc, result); +`endif + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63]^crc[2]^crc[0]}; + sum <= result ^ {sum[62:0],sum[63]^sum[2]^sum[0]}; + if (cyc == 0) begin + // Setup + crc <= 64'h5aef0c8d_d70a4497; + sum <= '0; + end + else if (cyc < 10) begin + sum <= '0; + end + else if (cyc < 90) begin + end + else if (cyc == 99) begin + $write("[%0t] cyc==%0d crc=%x sum=%x\n",$time, cyc, crc, sum); + if (crc !== 64'hc77bb9b3784ea091) $stop; + // What checksum will we end up with (above print should match) +`define EXPECTED_SUM 64'h118c5809c7856d78 + if (sum !== `EXPECTED_SUM) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module SelFlop(/*AUTOARG*/ + // Outputs + out, + // Inputs + clk, in, n + ); + + input clk; + input [7:0] in; + input [2:0] n; + output reg out; + + // verilator no_inline_module + + always @(posedge clk) begin + out <= in[n]; + end +endmodule diff --git a/test_regress/t/t_cover_lib_1.out b/test_regress/t/t_cover_lib_1.out index 71fdbee4b..30cc8a765 100644 --- a/test_regress/t/t_cover_lib_1.out +++ b/test_regress/t/t_cover_lib_1.out @@ -1,4 +1,4 @@ # SystemC::Coverage-3 -C 'f../../t/t_cover_lib_c.cppl35pagesp_user/t_cover_lib_cokept_onehmain' 100 -C 'f../../t/t_cover_lib_c.cppl36pagesp_user/t_cover_lib_cokept_twohmain' 210 -C 'f../../t/t_cover_lib_c.cppl37pagesp_user/t_cover_lib_colost_threehmain' 220 +C 'f../../t/t_cover_lib_c.cppl39pagesp_user/t_cover_lib_cokept_onehmain' 100 +C 'f../../t/t_cover_lib_c.cppl40pagesp_user/t_cover_lib_cokept_twohmain' 210 +C 'f../../t/t_cover_lib_c.cppl41pagesp_user/t_cover_lib_colost_threehmain' 220 diff --git a/test_regress/t/t_cover_lib_2.out b/test_regress/t/t_cover_lib_2.out index 445668d78..a015f43e9 100644 --- a/test_regress/t/t_cover_lib_2.out +++ b/test_regress/t/t_cover_lib_2.out @@ -1,3 +1,3 @@ # SystemC::Coverage-3 -C 'f../../t/t_cover_lib_c.cppl35pagesp_user/t_cover_lib_cokept_onehmain' 100 -C 'f../../t/t_cover_lib_c.cppl36pagesp_user/t_cover_lib_cokept_twohmain' 210 +C 'f../../t/t_cover_lib_c.cppl39pagesp_user/t_cover_lib_cokept_onehmain' 100 +C 'f../../t/t_cover_lib_c.cppl40pagesp_user/t_cover_lib_cokept_twohmain' 210 diff --git a/test_regress/t/t_cover_lib_3.out b/test_regress/t/t_cover_lib_3.out index e04cf5d1c..26778b3b9 100644 --- a/test_regress/t/t_cover_lib_3.out +++ b/test_regress/t/t_cover_lib_3.out @@ -1,3 +1,3 @@ # SystemC::Coverage-3 -C 'f../../t/t_cover_lib_c.cppl35pagesp_user/t_cover_lib_cokept_onehmain' 0 -C 'f../../t/t_cover_lib_c.cppl36pagesp_user/t_cover_lib_cokept_twohmain' 0 +C 'f../../t/t_cover_lib_c.cppl39pagesp_user/t_cover_lib_cokept_onehmain' 0 +C 'f../../t/t_cover_lib_c.cppl40pagesp_user/t_cover_lib_cokept_twohmain' 0 diff --git a/test_regress/t/t_cover_lib_c.cpp b/test_regress/t/t_cover_lib_c.cpp index cce1893b6..863ce000b 100644 --- a/test_regress/t/t_cover_lib_c.cpp +++ b/test_regress/t/t_cover_lib_c.cpp @@ -14,15 +14,17 @@ #include #include "svdpi.h" +#include "TestCheck.h" + #include "verilated_cov.h" #include VM_PREFIX_INCLUDE -double sc_time_stamp() { return 0; } - //====================================================================== -int failure = 0; +double sc_time_stamp() { return 0; } + +int errors = 0; //====================================================================== @@ -32,14 +34,27 @@ int main() { vluint32_t covers[1]; vluint64_t coverw[2]; - VL_COVER_INSERT(&covers[0], "comment", "kept_one"); - VL_COVER_INSERT(&coverw[0], "comment", "kept_two"); - VL_COVER_INSERT(&coverw[1], "comment", "lost_three"); + VerilatedCovContext* covContextp = Verilated::defaultContextp()->coveragep(); + + VL_COVER_INSERT(covContextp, &covers[0], "comment", "kept_one"); + VL_COVER_INSERT(covContextp, &coverw[0], "comment", "kept_two"); + VL_COVER_INSERT(covContextp, &coverw[1], "comment", "lost_three"); covers[0] = 100; coverw[0] = 210; coverw[1] = 220; +#ifdef T_COVER_LIB + TEST_CHECK_CSTR(covContextp->defaultFilename(), "coverage.dat"); + covContextp->write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage1.dat"); + covContextp->clearNonMatch("kept_"); + covContextp->write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage2.dat"); + covContextp->zero(); + covContextp->write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage3.dat"); + covContextp->clear(); + covContextp->write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage4.dat"); +#elif defined(T_COVER_LIB_LEGACY) + TEST_CHECK_CSTR(VerilatedCov::defaultFilename(), "coverage.dat"); VerilatedCov::write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage1.dat"); VerilatedCov::clearNonMatch("kept_"); VerilatedCov::write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage2.dat"); @@ -47,7 +62,10 @@ int main() { VerilatedCov::write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage3.dat"); VerilatedCov::clear(); VerilatedCov::write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage4.dat"); +#else +#error +#endif printf("*-* All Finished *-*\n"); - return (failure ? 10 : 0); + return (errors ? 10 : 0); } diff --git a/test_regress/t/t_cover_lib_legacy.pl b/test_regress/t/t_cover_lib_legacy.pl new file mode 100755 index 000000000..9d53f269e --- /dev/null +++ b/test_regress/t/t_cover_lib_legacy.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 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); + +top_filename("t/t_cover_lib.v"); + +compile( + v_flags2 => ["--coverage t/t_cover_lib_c.cpp"], + verilator_flags2 => ["--exe -Wall -Wno-DECLFILENAME"], + make_flags => 'CPPFLAGS_ADD=-DTEST_OBJ_DIR="'.$Self->{obj_dir}.'"', + make_top_shell => 0, + make_main => 0, + ); + +execute( + check_finished => 1, + ); + +files_identical_sorted("$Self->{obj_dir}/coverage1.dat", "t/t_cover_lib_1.out"); +files_identical_sorted("$Self->{obj_dir}/coverage2.dat", "t/t_cover_lib_2.out"); +files_identical_sorted("$Self->{obj_dir}/coverage3.dat", "t/t_cover_lib_3.out"); +files_identical_sorted("$Self->{obj_dir}/coverage4.dat", "t/t_cover_lib_4.out"); + +ok(1); +1; diff --git a/test_regress/t/t_dist_error_format.pl b/test_regress/t/t_dist_error_format.pl index 329ab3096..8a30f1f7b 100755 --- a/test_regress/t/t_dist_error_format.pl +++ b/test_regress/t/t_dist_error_format.pl @@ -39,7 +39,8 @@ sub formats { foreach my $line (split /\n/, $wholefile) { ++$lineno; $line =~ s/(\$display|\$write).*\".*%(Error|Warning)//; - if ($line =~ /(Error|Warning)/) { + if ($line =~ /(Error|Warning)/ + && $line !~ /Error-internal-contents-bad/) { # These formats are documented in bin/verilator # Error with fileline # For testing only: we assume no : in filename diff --git a/test_regress/t/t_dpi_lib_c.cpp b/test_regress/t/t_dpi_lib_c.cpp index 7113909ce..29a4d36b4 100644 --- a/test_regress/t/t_dpi_lib_c.cpp +++ b/test_regress/t/t_dpi_lib_c.cpp @@ -14,6 +14,8 @@ #include #include "svdpi.h" +#include "TestCheck.h" + //====================================================================== // clang-format off @@ -40,17 +42,8 @@ extern void dpii_check(); //====================================================================== -int failure = 0; -int dpii_failure() { return failure; } - -#define CHECK_RESULT_HEX(got, exp) \ - do { \ - if ((got) != (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << std::hex \ - << ": GOT=" << (got) << " EXP=" << (exp) << std::endl; \ - failure = __LINE__; \ - } \ - } while (0) +int errors = 0; +int dpii_failure() { return errors; } //====================================================================== @@ -59,49 +52,49 @@ void dpii_lib_bit_check() { bv[0] = 0xa3a2a1a0; // 31..0 bv[1] = 0xa7a6a5a4; // 63..32 bv[2] = 0xabaaa9a8; // 95..64 - CHECK_RESULT_HEX((int)svGetBitselBit(bv, 32), 0); - CHECK_RESULT_HEX((int)svGetBitselBit(bv, 33), 0); - CHECK_RESULT_HEX((int)svGetBitselBit(bv, 34), 1); - CHECK_RESULT_HEX((int)svGetBitselBit(bv, 35), 0); - CHECK_RESULT_HEX((int)svGetBitselBit(bv, 36), 0); - CHECK_RESULT_HEX((int)svGetBitselBit(bv, 37), 1); - CHECK_RESULT_HEX((int)svGetBitselBit(bv, 38), 0); - CHECK_RESULT_HEX((int)svGetBitselBit(bv, 39), 1); + TEST_CHECK_HEX_EQ((int)svGetBitselBit(bv, 32), 0); + TEST_CHECK_HEX_EQ((int)svGetBitselBit(bv, 33), 0); + TEST_CHECK_HEX_EQ((int)svGetBitselBit(bv, 34), 1); + TEST_CHECK_HEX_EQ((int)svGetBitselBit(bv, 35), 0); + TEST_CHECK_HEX_EQ((int)svGetBitselBit(bv, 36), 0); + TEST_CHECK_HEX_EQ((int)svGetBitselBit(bv, 37), 1); + TEST_CHECK_HEX_EQ((int)svGetBitselBit(bv, 38), 0); + TEST_CHECK_HEX_EQ((int)svGetBitselBit(bv, 39), 1); svPutBitselBit(bv, 32, 1); svPutBitselBit(bv, 33, 0); svPutBitselBit(bv, 34, 1); svPutBitselBit(bv, 35, 1); - CHECK_RESULT_HEX(bv[0], 0xa3a2a1a0); - CHECK_RESULT_HEX(bv[1], 0xa7a6a5ad); - CHECK_RESULT_HEX(bv[2], 0xabaaa9a8); + TEST_CHECK_HEX_EQ(bv[0], 0xa3a2a1a0); + TEST_CHECK_HEX_EQ(bv[1], 0xa7a6a5ad); + TEST_CHECK_HEX_EQ(bv[2], 0xabaaa9a8); svBitVecVal btmp[2]; svGetPartselBit(btmp, bv, 40, 8); - CHECK_RESULT_HEX(btmp[0], 0xa5); + TEST_CHECK_HEX_EQ(btmp[0], 0xa5); svGetPartselBit(btmp, bv, 32, 32); - CHECK_RESULT_HEX(btmp[0], 0xa7a6a5ad); + TEST_CHECK_HEX_EQ(btmp[0], 0xa7a6a5ad); svGetPartselBit(btmp, bv, 48, 40); - CHECK_RESULT_HEX(btmp[0], 0xa9a8a7a6); - CHECK_RESULT_HEX(btmp[1], 0xaa); + TEST_CHECK_HEX_EQ(btmp[0], 0xa9a8a7a6); + TEST_CHECK_HEX_EQ(btmp[1], 0xaa); btmp[0] = 0xa5; svPutPartselBit(bv, btmp[0], 48, 8); - CHECK_RESULT_HEX(bv[0], 0xa3a2a1a0); - CHECK_RESULT_HEX(bv[1], 0xa7a5a5ad); - CHECK_RESULT_HEX(bv[2], 0xabaaa9a8); + TEST_CHECK_HEX_EQ(bv[0], 0xa3a2a1a0); + TEST_CHECK_HEX_EQ(bv[1], 0xa7a5a5ad); + TEST_CHECK_HEX_EQ(bv[2], 0xabaaa9a8); btmp[0] = 0x11223344; svPutPartselBit(bv, btmp[0], 32, 32); - CHECK_RESULT_HEX(bv[0], 0xa3a2a1a0); - CHECK_RESULT_HEX(bv[1], 0x11223344); - CHECK_RESULT_HEX(bv[2], 0xabaaa9a8); + TEST_CHECK_HEX_EQ(bv[0], 0xa3a2a1a0); + TEST_CHECK_HEX_EQ(bv[1], 0x11223344); + TEST_CHECK_HEX_EQ(bv[2], 0xabaaa9a8); btmp[0] = 0x99887766; svPutPartselBit(bv, btmp[0], 24, 24); - CHECK_RESULT_HEX(bv[0], 0x66a2a1a0); - CHECK_RESULT_HEX(bv[1], 0x11228877); - CHECK_RESULT_HEX(bv[2], 0xabaaa9a8); + TEST_CHECK_HEX_EQ(bv[0], 0x66a2a1a0); + TEST_CHECK_HEX_EQ(bv[1], 0x11228877); + TEST_CHECK_HEX_EQ(bv[2], 0xabaaa9a8); } void dpii_lib_logic_check() { @@ -112,68 +105,68 @@ void dpii_lib_logic_check() { lv[0].bval = 0xc3c2c1c0; // 31..0 lv[1].bval = 0xc7c6c5c4; // 63..32 lv[2].bval = 0xcbcac9c8; // 95..64 - CHECK_RESULT_HEX((int)svGetBitselLogic(lv, 32), 0); - CHECK_RESULT_HEX((int)svGetBitselLogic(lv, 33), 0); - CHECK_RESULT_HEX((int)svGetBitselLogic(lv, 34), 3); - CHECK_RESULT_HEX((int)svGetBitselLogic(lv, 35), 0); - CHECK_RESULT_HEX((int)svGetBitselLogic(lv, 36), 1); - CHECK_RESULT_HEX((int)svGetBitselLogic(lv, 37), 1); - CHECK_RESULT_HEX((int)svGetBitselLogic(lv, 38), 2); - CHECK_RESULT_HEX((int)svGetBitselLogic(lv, 39), 3); + TEST_CHECK_HEX_EQ((int)svGetBitselLogic(lv, 32), 0); + TEST_CHECK_HEX_EQ((int)svGetBitselLogic(lv, 33), 0); + TEST_CHECK_HEX_EQ((int)svGetBitselLogic(lv, 34), 3); + TEST_CHECK_HEX_EQ((int)svGetBitselLogic(lv, 35), 0); + TEST_CHECK_HEX_EQ((int)svGetBitselLogic(lv, 36), 1); + TEST_CHECK_HEX_EQ((int)svGetBitselLogic(lv, 37), 1); + TEST_CHECK_HEX_EQ((int)svGetBitselLogic(lv, 38), 2); + TEST_CHECK_HEX_EQ((int)svGetBitselLogic(lv, 39), 3); svPutBitselLogic(lv, 32, 1); svPutBitselLogic(lv, 33, 0); svPutBitselLogic(lv, 34, 1); svPutBitselLogic(lv, 35, 3); - CHECK_RESULT_HEX(lv[0].aval, 0xb3b2b1b0); - CHECK_RESULT_HEX(lv[1].aval, 0xb7b6b5bd); - CHECK_RESULT_HEX(lv[2].aval, 0xbbbab9b8); - CHECK_RESULT_HEX(lv[0].bval, 0xc3c2c1c0); - CHECK_RESULT_HEX(lv[1].bval, 0xc7c6c5c8); - CHECK_RESULT_HEX(lv[2].bval, 0xcbcac9c8); + TEST_CHECK_HEX_EQ(lv[0].aval, 0xb3b2b1b0); + TEST_CHECK_HEX_EQ(lv[1].aval, 0xb7b6b5bd); + TEST_CHECK_HEX_EQ(lv[2].aval, 0xbbbab9b8); + TEST_CHECK_HEX_EQ(lv[0].bval, 0xc3c2c1c0); + TEST_CHECK_HEX_EQ(lv[1].bval, 0xc7c6c5c8); + TEST_CHECK_HEX_EQ(lv[2].bval, 0xcbcac9c8); svLogicVecVal ltmp[2]; svGetPartselLogic(ltmp, lv, 40, 8); - CHECK_RESULT_HEX(ltmp[0].aval, 0xb5); - CHECK_RESULT_HEX(ltmp[0].bval, 0xc5); + TEST_CHECK_HEX_EQ(ltmp[0].aval, 0xb5); + TEST_CHECK_HEX_EQ(ltmp[0].bval, 0xc5); svGetPartselLogic(ltmp, lv, 32, 32); - CHECK_RESULT_HEX(ltmp[0].aval, 0xb7b6b5bd); - CHECK_RESULT_HEX(ltmp[0].bval, 0xc7c6c5c8); + TEST_CHECK_HEX_EQ(ltmp[0].aval, 0xb7b6b5bd); + TEST_CHECK_HEX_EQ(ltmp[0].bval, 0xc7c6c5c8); svGetPartselLogic(ltmp, lv, 48, 40); - CHECK_RESULT_HEX(ltmp[0].aval, 0xb9b8b7b6); - CHECK_RESULT_HEX(ltmp[0].bval, 0xc9c8c7c6); - CHECK_RESULT_HEX(ltmp[1].aval, 0xba); - CHECK_RESULT_HEX(ltmp[1].bval, 0xca); + TEST_CHECK_HEX_EQ(ltmp[0].aval, 0xb9b8b7b6); + TEST_CHECK_HEX_EQ(ltmp[0].bval, 0xc9c8c7c6); + TEST_CHECK_HEX_EQ(ltmp[1].aval, 0xba); + TEST_CHECK_HEX_EQ(ltmp[1].bval, 0xca); ltmp[0].aval = 0xb5; ltmp[0].bval = 0xc5; svPutPartselLogic(lv, ltmp[0], 48, 8); - CHECK_RESULT_HEX(lv[0].aval, 0xb3b2b1b0); - CHECK_RESULT_HEX(lv[1].aval, 0xb7b5b5bd); - CHECK_RESULT_HEX(lv[2].aval, 0xbbbab9b8); - CHECK_RESULT_HEX(lv[0].bval, 0xc3c2c1c0); - CHECK_RESULT_HEX(lv[1].bval, 0xc7c5c5c8); - CHECK_RESULT_HEX(lv[2].bval, 0xcbcac9c8); + TEST_CHECK_HEX_EQ(lv[0].aval, 0xb3b2b1b0); + TEST_CHECK_HEX_EQ(lv[1].aval, 0xb7b5b5bd); + TEST_CHECK_HEX_EQ(lv[2].aval, 0xbbbab9b8); + TEST_CHECK_HEX_EQ(lv[0].bval, 0xc3c2c1c0); + TEST_CHECK_HEX_EQ(lv[1].bval, 0xc7c5c5c8); + TEST_CHECK_HEX_EQ(lv[2].bval, 0xcbcac9c8); ltmp[0].aval = 0x11223344; ltmp[0].bval = 0x81828384; svPutPartselLogic(lv, ltmp[0], 32, 32); - CHECK_RESULT_HEX(lv[0].aval, 0xb3b2b1b0); - CHECK_RESULT_HEX(lv[1].aval, 0x11223344); - CHECK_RESULT_HEX(lv[2].aval, 0xbbbab9b8); - CHECK_RESULT_HEX(lv[0].bval, 0xc3c2c1c0); - CHECK_RESULT_HEX(lv[1].bval, 0x81828384); - CHECK_RESULT_HEX(lv[2].bval, 0xcbcac9c8); + TEST_CHECK_HEX_EQ(lv[0].aval, 0xb3b2b1b0); + TEST_CHECK_HEX_EQ(lv[1].aval, 0x11223344); + TEST_CHECK_HEX_EQ(lv[2].aval, 0xbbbab9b8); + TEST_CHECK_HEX_EQ(lv[0].bval, 0xc3c2c1c0); + TEST_CHECK_HEX_EQ(lv[1].bval, 0x81828384); + TEST_CHECK_HEX_EQ(lv[2].bval, 0xcbcac9c8); ltmp[0].aval = 0x99887766; ltmp[0].bval = 0x89888786; svPutPartselLogic(lv, ltmp[0], 24, 24); - CHECK_RESULT_HEX(lv[0].aval, 0x66b2b1b0); - CHECK_RESULT_HEX(lv[1].aval, 0x11228877); - CHECK_RESULT_HEX(lv[2].aval, 0xbbbab9b8); - CHECK_RESULT_HEX(lv[0].bval, 0x86c2c1c0); - CHECK_RESULT_HEX(lv[1].bval, 0x81828887); - CHECK_RESULT_HEX(lv[2].bval, 0xcbcac9c8); + TEST_CHECK_HEX_EQ(lv[0].aval, 0x66b2b1b0); + TEST_CHECK_HEX_EQ(lv[1].aval, 0x11228877); + TEST_CHECK_HEX_EQ(lv[2].aval, 0xbbbab9b8); + TEST_CHECK_HEX_EQ(lv[0].bval, 0x86c2c1c0); + TEST_CHECK_HEX_EQ(lv[1].bval, 0x81828887); + TEST_CHECK_HEX_EQ(lv[2].bval, 0xcbcac9c8); } //====================================================================== diff --git a/test_regress/t/t_dpi_open_c.cpp b/test_regress/t/t_dpi_open_c.cpp index d75c822a1..60b6b1d33 100644 --- a/test_regress/t/t_dpi_open_c.cpp +++ b/test_regress/t/t_dpi_open_c.cpp @@ -14,6 +14,8 @@ #include #include "svdpi.h" +#include "TestCheck.h" + //====================================================================== // clang-format off @@ -70,26 +72,8 @@ extern int dpii_failure(); } #endif -int failure = 0; -int dpii_failure() { return failure; } - -#define CHECK_RESULT_HEX(got, exp) \ - do { \ - if ((got) != (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << std::hex \ - << ": GOT=" << (got) << " EXP=" << (exp) << std::endl; \ - failure = __LINE__; \ - } \ - } while (0) - -#define CHECK_RESULT_HEX_NE(got, exp) \ - do { \ - if ((got) == (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << std::hex \ - << ": GOT=" << (got) << " EXP!=" << (exp) << std::endl; \ - failure = __LINE__; \ - } \ - } while (0) +int errors = 0; +int dpii_failure() { return errors; } void dpii_unused(const svOpenArrayHandle u) {} @@ -107,58 +91,58 @@ void _dpii_all(int c, int p, int u, const svOpenArrayHandle i, const svOpenArray if (p) { int d = 0; if (c == 0 || c == 1) { - CHECK_RESULT_HEX(svLeft(i, d), 1); - CHECK_RESULT_HEX(svRight(i, d), -1); - CHECK_RESULT_HEX(svLow(i, d), -1); - CHECK_RESULT_HEX(svHigh(i, d), 1); - // CHECK_RESULT_HEX(svIncrement(i, d), 0); - CHECK_RESULT_HEX(svSize(i, d), 3); + TEST_CHECK_HEX_EQ(svLeft(i, d), 1); + TEST_CHECK_HEX_EQ(svRight(i, d), -1); + TEST_CHECK_HEX_EQ(svLow(i, d), -1); + TEST_CHECK_HEX_EQ(svHigh(i, d), 1); + // TEST_CHECK_HEX_EQ(svIncrement(i, d), 0); + TEST_CHECK_HEX_EQ(svSize(i, d), 3); } else if (c == 2) { - CHECK_RESULT_HEX(svLeft(i, d), 95); - CHECK_RESULT_HEX(svRight(i, d), 1); - CHECK_RESULT_HEX(svLow(i, d), 1); - CHECK_RESULT_HEX(svHigh(i, d), 95); - // CHECK_RESULT_HEX(svIncrement(i, d), 0); - CHECK_RESULT_HEX(svSize(i, d), 95); + TEST_CHECK_HEX_EQ(svLeft(i, d), 95); + TEST_CHECK_HEX_EQ(svRight(i, d), 1); + TEST_CHECK_HEX_EQ(svLow(i, d), 1); + TEST_CHECK_HEX_EQ(svHigh(i, d), 95); + // TEST_CHECK_HEX_EQ(svIncrement(i, d), 0); + TEST_CHECK_HEX_EQ(svSize(i, d), 95); } else { - CHECK_RESULT_HEX(0, 1); + TEST_CHECK_HEX_EQ(0, 1); } } #endif if (u >= 1) { int d = 1; if (c == 0) { - CHECK_RESULT_HEX(svLeft(i, d), -2); - CHECK_RESULT_HEX(svRight(i, d), 2); - CHECK_RESULT_HEX(svLow(i, d), -2); - CHECK_RESULT_HEX(svHigh(i, d), 2); - // CHECK_RESULT_HEX(svIncrement(i, d), 0); - CHECK_RESULT_HEX(svSize(i, d), 5); + TEST_CHECK_HEX_EQ(svLeft(i, d), -2); + TEST_CHECK_HEX_EQ(svRight(i, d), 2); + TEST_CHECK_HEX_EQ(svLow(i, d), -2); + TEST_CHECK_HEX_EQ(svHigh(i, d), 2); + // TEST_CHECK_HEX_EQ(svIncrement(i, d), 0); + TEST_CHECK_HEX_EQ(svSize(i, d), 5); } else if (c == 1) { - CHECK_RESULT_HEX(svLeft(i, d), 2); - CHECK_RESULT_HEX(svRight(i, d), -2); - CHECK_RESULT_HEX(svLow(i, d), -2); - CHECK_RESULT_HEX(svHigh(i, d), 2); - // CHECK_RESULT_HEX(svIncrement(i, d), 0); - CHECK_RESULT_HEX(svSize(i, d), 5); + TEST_CHECK_HEX_EQ(svLeft(i, d), 2); + TEST_CHECK_HEX_EQ(svRight(i, d), -2); + TEST_CHECK_HEX_EQ(svLow(i, d), -2); + TEST_CHECK_HEX_EQ(svHigh(i, d), 2); + // TEST_CHECK_HEX_EQ(svIncrement(i, d), 0); + TEST_CHECK_HEX_EQ(svSize(i, d), 5); } } if (u >= 2) { int d = 2; if (c == 0) { - CHECK_RESULT_HEX(svLeft(i, d), -3); - CHECK_RESULT_HEX(svRight(i, d), 3); - CHECK_RESULT_HEX(svLow(i, d), -3); - CHECK_RESULT_HEX(svHigh(i, d), 3); - // CHECK_RESULT_HEX(svIncrement(i, d), 0); - CHECK_RESULT_HEX(svSize(i, d), 7); + TEST_CHECK_HEX_EQ(svLeft(i, d), -3); + TEST_CHECK_HEX_EQ(svRight(i, d), 3); + TEST_CHECK_HEX_EQ(svLow(i, d), -3); + TEST_CHECK_HEX_EQ(svHigh(i, d), 3); + // TEST_CHECK_HEX_EQ(svIncrement(i, d), 0); + TEST_CHECK_HEX_EQ(svSize(i, d), 7); } else if (c == 1) { - CHECK_RESULT_HEX(svLeft(i, d), 3); - CHECK_RESULT_HEX(svRight(i, d), -3); - CHECK_RESULT_HEX(svLow(i, d), -3); - CHECK_RESULT_HEX(svHigh(i, d), 3); - // CHECK_RESULT_HEX(svIncrement(i, d), 0); - CHECK_RESULT_HEX(svSize(i, d), 7); + TEST_CHECK_HEX_EQ(svLeft(i, d), 3); + TEST_CHECK_HEX_EQ(svRight(i, d), -3); + TEST_CHECK_HEX_EQ(svLow(i, d), -3); + TEST_CHECK_HEX_EQ(svHigh(i, d), 3); + // TEST_CHECK_HEX_EQ(svIncrement(i, d), 0); + TEST_CHECK_HEX_EQ(svSize(i, d), 7); } } #ifdef VERILATOR @@ -240,12 +224,12 @@ void dpii_open_bit(const svOpenArrayHandle i, const svOpenArrayHandle o) {} void dpii_open_byte(const svOpenArrayHandle i, const svOpenArrayHandle o) { intptr_t arrPtr = (intptr_t)svGetArrayPtr(i); - CHECK_RESULT_HEX_NE(arrPtr, 0); // All the arrays should actually exist + TEST_CHECK_HEX_NE(arrPtr, 0); // All the arrays should actually exist #ifndef NC // NC always returns zero and warns int sizeInputOfArray = svSizeOfArray(i); - CHECK_RESULT_HEX_NE(sizeInputOfArray, 0); // None of the test cases have zero size - CHECK_RESULT_HEX_NE(svDimensions(i), 0); // All the test cases are unpacked arrays + TEST_CHECK_HEX_NE(sizeInputOfArray, 0); // None of the test cases have zero size + TEST_CHECK_HEX_NE(svDimensions(i), 0); // All the test cases are unpacked arrays #endif } @@ -254,12 +238,12 @@ void dpii_open_logic(const svOpenArrayHandle i, const svOpenArrayHandle o) {} static void _dpii_open_int_ux(int u, const svOpenArrayHandle i, const svOpenArrayHandle o) { intptr_t arrPtr = (intptr_t)svGetArrayPtr(i); - CHECK_RESULT_HEX_NE(arrPtr, 0); // All the arrays should actually exist + TEST_CHECK_HEX_NE(arrPtr, 0); // All the arrays should actually exist #ifndef NC // NC always returns zero and warns int sizeInputOfArray = svSizeOfArray(i); - CHECK_RESULT_HEX_NE(sizeInputOfArray, 0); // None of the test cases have zero size - CHECK_RESULT_HEX(svDimensions(i), u); + TEST_CHECK_HEX_NE(sizeInputOfArray, 0); // None of the test cases have zero size + TEST_CHECK_HEX_EQ(svDimensions(i), u); #endif int dim = svDimensions(i); @@ -268,30 +252,30 @@ static void _dpii_open_int_ux(int u, const svOpenArrayHandle i, const svOpenArra if (dim == 1) { intptr_t ip = (intptr_t)svGetArrElemPtr(i, a); intptr_t i2p = (intptr_t)svGetArrElemPtr1(i, a); - CHECK_RESULT_HEX(ip, i2p); - CHECK_RESULT_HEX_NE(ip, 0); + TEST_CHECK_HEX_EQ(ip, i2p); + TEST_CHECK_HEX_NE(ip, 0); intptr_t op = (intptr_t)svGetArrElemPtr(o, a); - CHECK_RESULT_HEX_NE(op, 0); + TEST_CHECK_HEX_NE(op, 0); *reinterpret_cast(op) = ~*reinterpret_cast(ip); } else { for (int b = svLow(i, 2); b <= svHigh(i, 2); ++b) { if (dim == 2) { intptr_t ip = (intptr_t)svGetArrElemPtr(i, a, b); intptr_t i2p = (intptr_t)svGetArrElemPtr2(i, a, b); - CHECK_RESULT_HEX(ip, i2p); - CHECK_RESULT_HEX_NE(ip, 0); + TEST_CHECK_HEX_EQ(ip, i2p); + TEST_CHECK_HEX_NE(ip, 0); intptr_t op = (intptr_t)svGetArrElemPtr(o, a, b); - CHECK_RESULT_HEX_NE(op, 0); + TEST_CHECK_HEX_NE(op, 0); *reinterpret_cast(op) = ~*reinterpret_cast(ip); } else { for (int c = svLow(i, 3); c <= svHigh(i, 3); ++c) { if (dim == 3) { intptr_t ip = (intptr_t)svGetArrElemPtr(i, a, b, c); intptr_t i2p = (intptr_t)svGetArrElemPtr3(i, a, b, c); - CHECK_RESULT_HEX(ip, i2p); - CHECK_RESULT_HEX_NE(ip, 0); + TEST_CHECK_HEX_EQ(ip, i2p); + TEST_CHECK_HEX_NE(ip, 0); intptr_t op = (intptr_t)svGetArrElemPtr(o, a, b, c); - CHECK_RESULT_HEX_NE(op, 0); + TEST_CHECK_HEX_NE(op, 0); *reinterpret_cast(op) = ~*reinterpret_cast(ip); } } diff --git a/test_regress/t/t_dpi_open_elem_c.cpp b/test_regress/t/t_dpi_open_elem_c.cpp index 72a8793a4..6897375e6 100644 --- a/test_regress/t/t_dpi_open_elem_c.cpp +++ b/test_regress/t/t_dpi_open_elem_c.cpp @@ -14,6 +14,8 @@ #include #include "svdpi.h" +#include "TestCheck.h" + //====================================================================== // clang-format off @@ -52,26 +54,8 @@ extern int dpii_failure(); } #endif -int failure = 0; -int dpii_failure() { return failure; } - -#define CHECK_RESULT_HEX(got, exp) \ - do { \ - if ((got) != (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << std::hex \ - << ": GOT=" << (got) << " EXP=" << (exp) << std::endl; \ - failure = __LINE__; \ - } \ - } while (0) - -#define CHECK_RESULT_HEX_NE(got, exp) \ - do { \ - if ((got) == (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << std::hex \ - << ": GOT=" << (got) << " EXP!=" << (exp) << std::endl; \ - failure = __LINE__; \ - } \ - } while (0) +int errors = 0; +int dpii_failure() { return errors; } void dpii_unused(const svOpenArrayHandle u) {} @@ -82,8 +66,7 @@ static void _dpii_bit_elem_ux(int p, int u, const svOpenArrayHandle i, const svO int dim = svDimensions(i); #ifndef NC // NC always returns zero and warns - CHECK_RESULT_HEX(dim, u); - // svSizeOfArray(i) undeterministic as not in C representation + TEST_CHECK_HEX_EQ(dim, u); #endif for (int a = svLow(i, 1); a <= svHigh(i, 1); ++a) { @@ -91,7 +74,7 @@ static void _dpii_bit_elem_ux(int p, int u, const svOpenArrayHandle i, const svO if (dim == 1) { svBit v = svGetBitArrElem(i, a); svBit v2 = svGetBitArrElem1(i, a); - CHECK_RESULT_HEX(v, v2); + TEST_CHECK_HEX_EQ(v, v2); svPutBitArrElem(o, v ? 0 : 1, a); svPutBitArrElem1(q, v ? 0 : 1, a); } else { @@ -99,7 +82,7 @@ static void _dpii_bit_elem_ux(int p, int u, const svOpenArrayHandle i, const svO if (dim == 2) { svBit v = svGetBitArrElem(i, a, b); svBit v2 = svGetBitArrElem2(i, a, b); - CHECK_RESULT_HEX(v, v2); + TEST_CHECK_HEX_EQ(v, v2); svPutBitArrElem(o, v ? 0 : 1, a, b); svPutBitArrElem2(q, v ? 0 : 1, a, b); } else { @@ -107,7 +90,7 @@ static void _dpii_bit_elem_ux(int p, int u, const svOpenArrayHandle i, const svO if (dim == 3) { svBit v = svGetBitArrElem(i, a, b, c); svBit v2 = svGetBitArrElem3(i, a, b, c); - CHECK_RESULT_HEX(v, v2); + TEST_CHECK_HEX_EQ(v, v2); svPutBitArrElem(o, v ? 0 : 1, a, b, c); svPutBitArrElem3(q, v ? 0 : 1, a, b, c); } @@ -135,19 +118,20 @@ void dpii_bit_elem_p0_u3(int p, int u, const svOpenArrayHandle i, const svOpenAr static void _dpii_logic_elem_ux(int p, int u, const svOpenArrayHandle i, const svOpenArrayHandle o, const svOpenArrayHandle q) { - int sizeInputOfArray = svSizeOfArray(i); int dim = svDimensions(i); #ifndef NC // NC always returns zero and warns - CHECK_RESULT_HEX(dim, u); - // svSizeOfArray(i) undeterministic as not in C representation + TEST_CHECK_HEX_EQ(dim, u); #endif + int sizeInputOfArray = svSizeOfArray(i); + // svSizeOfArray(i) undeterministic as not in C representation + if (sizeInputOfArray) {} for (int a = svLow(i, 1); a <= svHigh(i, 1); ++a) { if (dim == 1) { svLogic v = svGetLogicArrElem(i, a); svLogic v2 = svGetLogicArrElem1(i, a); - CHECK_RESULT_HEX(v, v2); + TEST_CHECK_HEX_EQ(v, v2); svPutLogicArrElem(o, v ? 0 : 1, a); svPutLogicArrElem1(q, v ? 0 : 1, a); } else { @@ -155,7 +139,7 @@ static void _dpii_logic_elem_ux(int p, int u, const svOpenArrayHandle i, const s if (dim == 2) { svLogic v = svGetLogicArrElem(i, a, b); svLogic v2 = svGetLogicArrElem2(i, a, b); - CHECK_RESULT_HEX(v, v2); + TEST_CHECK_HEX_EQ(v, v2); svPutLogicArrElem(o, v ? 0 : 1, a, b); svPutLogicArrElem2(q, v ? 0 : 1, a, b); } else { @@ -163,7 +147,7 @@ static void _dpii_logic_elem_ux(int p, int u, const svOpenArrayHandle i, const s if (dim == 3) { svLogic v = svGetLogicArrElem(i, a, b, c); svLogic v2 = svGetLogicArrElem3(i, a, b, c); - CHECK_RESULT_HEX(v, v2); + TEST_CHECK_HEX_EQ(v, v2); svPutLogicArrElem(o, v ? 0 : 1, a, b, c); svPutLogicArrElem3(q, v ? 0 : 1, a, b, c); } diff --git a/test_regress/t/t_dpi_open_oob_bad_c.cpp b/test_regress/t/t_dpi_open_oob_bad_c.cpp index 5ed9853c0..2dd22cb86 100644 --- a/test_regress/t/t_dpi_open_oob_bad_c.cpp +++ b/test_regress/t/t_dpi_open_oob_bad_c.cpp @@ -14,6 +14,8 @@ #include #include "svdpi.h" +#include "TestCheck.h" + //====================================================================== // clang-format off @@ -24,27 +26,9 @@ #endif // clang-format on -#define CHECK_RESULT_HEX(got, exp) \ - do { \ - if ((got) != (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << std::hex \ - << ": GOT=" << (got) << " EXP=" << (exp) << std::endl; \ - failure = __LINE__; \ - } \ - } while (0) - -#define CHECK_RESULT_HEX_NE(got, exp) \ - do { \ - if ((got) == (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << std::hex \ - << ": GOT=" << (got) << " EXP!=" << (exp) << std::endl; \ - failure = __LINE__; \ - } \ - } while (0) - //====================================================================== -int failure = 0; +int errors = 0; void dpii_nullptr() { printf("%s:\n", __func__); @@ -104,12 +88,16 @@ void dpii_int_u3(const svOpenArrayHandle i) { printf("%s:\n", __func__); // Correct usage intptr_t ip = (intptr_t)svGetArrElemPtr3(i, 1, 2, 3); - CHECK_RESULT_HEX_NE(ip, 0); + TEST_CHECK_HEX_NE(ip, 0); // Out of bounds ip = (intptr_t)svGetArrElemPtr3(i, 1, 2, 30); + TEST_CHECK_HEX_EQ(ip, 0); ip = (intptr_t)svGetArrElemPtr3(i, 1, 20, 3); + TEST_CHECK_HEX_EQ(ip, 0); ip = (intptr_t)svGetArrElemPtr3(i, 10, 2, 3); + TEST_CHECK_HEX_EQ(ip, 0); ip = (intptr_t)svGetArrElemPtr1(i, 30); + TEST_CHECK_HEX_EQ(ip, 0); } void dpii_real_u1(const svOpenArrayHandle i) { diff --git a/test_regress/t/t_dpi_open_vecval_c.cpp b/test_regress/t/t_dpi_open_vecval_c.cpp index e8b49163c..b95fb6615 100644 --- a/test_regress/t/t_dpi_open_vecval_c.cpp +++ b/test_regress/t/t_dpi_open_vecval_c.cpp @@ -14,6 +14,8 @@ #include #include "svdpi.h" +#include "TestCheck.h" + //====================================================================== // clang-format off @@ -39,26 +41,8 @@ extern int dpii_failure(); } #endif -int failure = 0; -int dpii_failure() { return failure; } - -#define CHECK_RESULT_HEX(got, exp) \ - do { \ - if ((got) != (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << std::hex \ - << ": GOT=" << (got) << " EXP=" << (exp) << std::endl; \ - failure = __LINE__; \ - } \ - } while (0) - -#define CHECK_RESULT_HEX_NE(got, exp) \ - do { \ - if ((got) == (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << std::hex \ - << ": GOT=" << (got) << " EXP!=" << (exp) << std::endl; \ - failure = __LINE__; \ - } \ - } while (0) +int errors = 0; +int dpii_failure() { return errors; } static void _invert(int bits, svBitVecVal o[], const svBitVecVal i[]) { for (int w = 0; w < SV_PACKED_DATA_NELEMS(bits); ++w) o[w] = ~i[w]; @@ -102,7 +86,7 @@ static void _dpii_bit_vecval_ux(int bits, int p, int u, const svOpenArrayHandle int dim = svDimensions(i); #ifndef NC // NC always returns zero and warns - CHECK_RESULT_HEX(dim, u); + TEST_CHECK_HEX_EQ(dim, u); #endif svBitVecVal vv[SV_PACKED_DATA_NELEMS(bits)]; @@ -113,7 +97,7 @@ static void _dpii_bit_vecval_ux(int bits, int p, int u, const svOpenArrayHandle if (dim == 1) { svGetBitArrElemVecVal(vv, i, a); svGetBitArrElem1VecVal(vv2, i, a); - CHECK_RESULT_HEX(_same(bits, vv, vv2), true); + TEST_CHECK_HEX_EQ(_same(bits, vv, vv2), true); _invert(bits, vo, vv); svPutBitArrElemVecVal(o, vo, a); svPutBitArrElem1VecVal(q, vo, a); @@ -122,7 +106,7 @@ static void _dpii_bit_vecval_ux(int bits, int p, int u, const svOpenArrayHandle if (dim == 2) { svGetBitArrElemVecVal(vv, i, a, b); svGetBitArrElem2VecVal(vv2, i, a, b); - CHECK_RESULT_HEX(_same(bits, vv, vv2), true); + TEST_CHECK_HEX_EQ(_same(bits, vv, vv2), true); _invert(bits, vo, vv); svPutBitArrElemVecVal(o, vo, a, b); svPutBitArrElem2VecVal(q, vo, a, b); @@ -131,7 +115,7 @@ static void _dpii_bit_vecval_ux(int bits, int p, int u, const svOpenArrayHandle if (dim == 3) { svGetBitArrElemVecVal(vv, i, a, b, c); svGetBitArrElem3VecVal(vv2, i, a, b, c); - CHECK_RESULT_HEX(_same(bits, vv, vv2), true); + TEST_CHECK_HEX_EQ(_same(bits, vv, vv2), true); _invert(bits, vo, vv); svPutBitArrElemVecVal(o, vo, a, b, c); svPutBitArrElem3VecVal(q, vo, a, b, c); @@ -173,7 +157,7 @@ static void _dpii_logic_vecval_ux(int bits, int p, int u, const svOpenArrayHandl int dim = svDimensions(i); #ifndef NC // NC always returns zero and warns - CHECK_RESULT_HEX(dim, u); + TEST_CHECK_HEX_EQ(dim, u); #endif svLogicVecVal vv[SV_PACKED_DATA_NELEMS(bits)]; @@ -184,7 +168,7 @@ static void _dpii_logic_vecval_ux(int bits, int p, int u, const svOpenArrayHandl if (dim == 1) { svGetLogicArrElemVecVal(vv, i, a); svGetLogicArrElem1VecVal(vv2, i, a); - CHECK_RESULT_HEX(_same(bits, vv, vv2), true); + TEST_CHECK_HEX_EQ(_same(bits, vv, vv2), true); _invert(bits, vo, vv); svPutLogicArrElemVecVal(o, vo, a); svPutLogicArrElem1VecVal(q, vo, a); @@ -193,7 +177,7 @@ static void _dpii_logic_vecval_ux(int bits, int p, int u, const svOpenArrayHandl if (dim == 2) { svGetLogicArrElemVecVal(vv, i, a, b); svGetLogicArrElem2VecVal(vv2, i, a, b); - CHECK_RESULT_HEX(_same(bits, vv, vv2), true); + TEST_CHECK_HEX_EQ(_same(bits, vv, vv2), true); _invert(bits, vo, vv); svPutLogicArrElemVecVal(o, vo, a, b); svPutLogicArrElem2VecVal(q, vo, a, b); @@ -202,7 +186,7 @@ static void _dpii_logic_vecval_ux(int bits, int p, int u, const svOpenArrayHandl if (dim == 3) { svGetLogicArrElemVecVal(vv, i, a, b, c); svGetLogicArrElem3VecVal(vv2, i, a, b, c); - CHECK_RESULT_HEX(_same(bits, vv, vv2), true); + TEST_CHECK_HEX_EQ(_same(bits, vv, vv2), true); _invert(bits, vo, vv); svPutLogicArrElemVecVal(o, vo, a, b, c); svPutLogicArrElem3VecVal(q, vo, a, b, c); diff --git a/test_regress/t/t_dpi_openfirst_c.cpp b/test_regress/t/t_dpi_openfirst_c.cpp index 3b60a4129..2d07a60c5 100644 --- a/test_regress/t/t_dpi_openfirst_c.cpp +++ b/test_regress/t/t_dpi_openfirst_c.cpp @@ -14,6 +14,8 @@ #include #include "svdpi.h" +#include "TestCheck.h" + //====================================================================== // clang-format off @@ -40,34 +42,25 @@ extern void dpii_open_i(const svOpenArrayHandle i, const svOpenArrayHandle o); //====================================================================== -int failure = 0; -int dpii_failure() { return failure; } - -#define CHECK_RESULT_HEX(got, exp) \ - do { \ - if ((got) != (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << std::hex \ - << ": GOT=" << (got) << " EXP=" << (exp) << std::endl; \ - failure = __LINE__; \ - } \ - } while (0) +int errors = 0; +int dpii_failure() { return errors; } //====================================================================== void dpii_open_i(const svOpenArrayHandle i, const svOpenArrayHandle o) { // Illegal in VCS: - // CHECK_RESULT_HEX(svLeft(i, 0), 2); - // CHECK_RESULT_HEX(svRight(i, 0), 0); - // CHECK_RESULT_HEX(svLow(i, 0), 0); - // CHECK_RESULT_HEX(svHigh(i, 0), 2); + // TEST_CHECK_HEX_EQ(svLeft(i, 0), 2); + // TEST_CHECK_HEX_EQ(svRight(i, 0), 0); + // TEST_CHECK_HEX_EQ(svLow(i, 0), 0); + // TEST_CHECK_HEX_EQ(svHigh(i, 0), 2); // - CHECK_RESULT_HEX(svDimensions(i), 1); - CHECK_RESULT_HEX(svLeft(i, 1), 2); - CHECK_RESULT_HEX(svRight(i, 1), 0); - CHECK_RESULT_HEX(svLow(i, 1), 0); - CHECK_RESULT_HEX(svHigh(i, 1), 2); - // CHECK_RESULT_HEX(svIncrement(i, 1), 0); - CHECK_RESULT_HEX(svSize(i, 1), 3); + TEST_CHECK_HEX_EQ(svDimensions(i), 1); + TEST_CHECK_HEX_EQ(svLeft(i, 1), 2); + TEST_CHECK_HEX_EQ(svRight(i, 1), 0); + TEST_CHECK_HEX_EQ(svLow(i, 1), 0); + TEST_CHECK_HEX_EQ(svHigh(i, 1), 2); + // TEST_CHECK_HEX_EQ(svIncrement(i, 1), 0); + TEST_CHECK_HEX_EQ(svSize(i, 1), 3); for (int a = 0; a < 3; ++a) { svBitVecVal vec[1]; svGetBitArrElemVecVal(vec, i, a); diff --git a/test_regress/t/t_func_crc.pl b/test_regress/t/t_func_crc.pl index 6549284aa..179c4c77d 100755 --- a/test_regress/t/t_func_crc.pl +++ b/test_regress/t/t_func_crc.pl @@ -19,7 +19,7 @@ execute( ); if ($Self->{vlt}) { - file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 3888); + file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 3870); } ok(1); diff --git a/test_regress/t/t_gate_ormux.pl b/test_regress/t/t_gate_ormux.pl index 996e05170..b4bc92497 100755 --- a/test_regress/t/t_gate_ormux.pl +++ b/test_regress/t/t_gate_ormux.pl @@ -19,7 +19,7 @@ compile( ); if ($Self->{vlt}) { - file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 994); + file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 898); } execute( diff --git a/test_regress/t/t_leak.cpp b/test_regress/t/t_leak.cpp index 8553bfccd..f9bdf3bda 100644 --- a/test_regress/t/t_leak.cpp +++ b/test_regress/t/t_leak.cpp @@ -11,7 +11,7 @@ #include #include #include -#include "Vt_leak.h" +#include VM_PREFIX_INCLUDE unsigned int main_time = 0; double sc_time_stamp() { return main_time; } @@ -45,19 +45,36 @@ long long get_memory_usage() { } void make_and_destroy() { - Vt_leak* topp = new Vt_leak; +#ifdef VL_NO_LEGACY + VerilatedContext* contextp = new VerilatedContext; + VM_PREFIX* topp = new VM_PREFIX{contextp}; +#else + VM_PREFIX* topp = new VM_PREFIX; +#endif Verilated::debug(0); - Verilated::gotFinish(0); topp->eval(); topp->clk = true; - while (!Verilated::gotFinish()) { + while (! +#ifdef VL_NO_LEGACY + contextp->gotFinish() +#else + Verilated::gotFinish() +#endif + ) { +#ifdef VL_NO_LEGACY + contextp->timeInc(5); +#else main_time += 5; +#endif topp->clk = !topp->clk; topp->eval(); } VL_DO_DANGLING(delete topp, topp); +#ifdef VL_NO_LEGACY + VL_DO_DANGLING(delete contextp, contextp); +#endif } int main(int argc, char* argv[]) { diff --git a/test_regress/t/t_leak.pl b/test_regress/t/t_leak.pl index 4ab37524e..39a931540 100755 --- a/test_regress/t/t_leak.pl +++ b/test_regress/t/t_leak.pl @@ -20,6 +20,7 @@ compile( make_top_shell => 0, make_main => 0, verilator_flags2 => ["--exe $Self->{t_dir}/$Self->{name}.cpp"], + make_flags => 'CPPFLAGS_ADD=-DVL_NO_LEGACY', ); execute( diff --git a/test_regress/t/t_leak.v b/test_regress/t/t_leak.v index b85a8099d..25ef75492 100644 --- a/test_regress/t/t_leak.v +++ b/test_regress/t/t_leak.v @@ -15,7 +15,7 @@ module t (clk); cyc <= cyc + 1; if (cyc==2) begin // Not $finish; as we don't want a message to scroll by - $c("Verilated::gotFinish(true);"); + $c("Verilated::threadContextp()->gotFinish(true);"); end end endmodule diff --git a/test_regress/t/t_leak_legacy.pl b/test_regress/t/t_leak_legacy.pl new file mode 100755 index 000000000..03166108f --- /dev/null +++ b/test_regress/t/t_leak_legacy.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 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 + +if ($Self->{vltmt} && exists $ENV{TRAVIS_DIST} && + $ENV{TRAVIS_DIST} eq "trusty") +{ + skip("Multithreaded test does not work under CI w/ Ubuntu Trusty"); +} + +scenarios(vlt_all => 1); + +top_filename("t/t_leak.v"); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe $Self->{t_dir}/t_leak.cpp"], + make_flags => 'CPPFLAGS_ADD=-UVL_NO_LEGACY', + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_pow2.v b/test_regress/t/t_math_pow2.v index 114f04f56..f181f6d21 100644 --- a/test_regress/t/t_math_pow2.v +++ b/test_regress/t/t_math_pow2.v @@ -16,8 +16,9 @@ module t (/*AUTOARG*/ // Aggregate outputs into a single result vector //wire [31:0] pow32b = {24'h0,crc[15:8]}**crc[7:0]; // Overflows - wire [3:0] pow4b = crc[7:4]**crc[3:0]; - wire [63:0] result = {60'h0, pow4b}; + wire [3:0] pow4b = crc[7:4] ** crc[3:0]; + wire [31:0] pow2 = 2 ** crc[3:0]; // Optimizes to shift + wire [63:0] result = {pow2, 28'h0, pow4b}; // Test loop always @ (posedge clk) begin @@ -40,8 +41,7 @@ module t (/*AUTOARG*/ else if (cyc==99) begin $write("[%0t] cyc==%0d crc=%x sum=%x\n",$time, cyc, crc, sum); if (crc !== 64'hc77bb9b3784ea091) $stop; - // What checksum will we end up with (above print should match) -`define EXPECTED_SUM 64'h1fec4b2b71cf8024 +`define EXPECTED_SUM 64'h056ea1c5a63aff6a if (sum !== `EXPECTED_SUM) $stop; $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_mod_interface_array4.v b/test_regress/t/t_mod_interface_array4.v index 62ab2eebd..1e8781f82 100644 --- a/test_regress/t/t_mod_interface_array4.v +++ b/test_regress/t/t_mod_interface_array4.v @@ -11,33 +11,6 @@ interface intf (); integer index; endinterface -module sub - ( - input logic clk, - input int cyc, - intf alh[1:2], - intf ahl[2:1], - intf blh[1:2], - intf bhl[2:1] - ); - - always @(posedge clk) begin - if (cyc == 5) begin - `checkh(alh[1].index, 2); - `checkh(alh[2].index, 1); - `checkh(ahl[1].index, 1); - `checkh(ahl[2].index, 2); - `checkh(blh[1].index, 1); - `checkh(blh[2].index, 2); - `checkh(bhl[1].index, 2); - `checkh(bhl[2].index, 1); - $write("*-* All Finished *-*\n"); - $finish; - end - end - -endmodule - module t ( clk @@ -64,14 +37,41 @@ module t always @(posedge clk) begin cyc <= cyc + 1; if (cyc == 1) begin - ifa1_intf[1].index = 1; - ifa1_intf[2].index = 2; - ifa2_intf[1].index = 1; - ifa2_intf[2].index = 2; - ifb1_intf[1].index = 1; - ifb1_intf[2].index = 2; - ifb2_intf[1].index = 1; - ifb2_intf[2].index = 2; + ifa1_intf[1].index = 'h101; + ifa1_intf[2].index = 'h102; + ifa2_intf[1].index = 'h201; + ifa2_intf[2].index = 'h202; + ifb1_intf[1].index = 'h301; + ifb1_intf[2].index = 'h302; + ifb2_intf[1].index = 'h401; + ifb2_intf[2].index = 'h402; + end + end + +endmodule + +module sub + ( + input logic clk, + input int cyc, + intf alh[1:2], + intf ahl[2:1], + intf blh[1:2], + intf bhl[2:1] + ); + + always @(posedge clk) begin + if (cyc == 5) begin + `checkh(alh[1].index, 'h102); + `checkh(alh[2].index, 'h101); + `checkh(ahl[1].index, 'h201); + `checkh(ahl[2].index, 'h202); + `checkh(blh[1].index, 'h301); + `checkh(blh[2].index, 'h302); + `checkh(bhl[1].index, 'h402); + `checkh(bhl[2].index, 'h401); + $write("*-* All Finished *-*\n"); + $finish; end end diff --git a/test_regress/t/t_mod_interface_array4_noinl.pl b/test_regress/t/t_mod_interface_array4_noinl.pl new file mode 100755 index 000000000..6797c1016 --- /dev/null +++ b/test_regress/t/t_mod_interface_array4_noinl.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 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); + +top_filename("t/t_mod_interface_array4.v"); + +compile( + v_flags2 => ["-Oi"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_mod_interface_array5.pl b/test_regress/t/t_mod_interface_array5.pl new file mode 100755 index 000000000..2cb5eeaff --- /dev/null +++ b/test_regress/t/t_mod_interface_array5.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 2021 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_mod_interface_array5.v b/test_regress/t/t_mod_interface_array5.v new file mode 100644 index 000000000..ff5d55625 --- /dev/null +++ b/test_regress/t/t_mod_interface_array5.v @@ -0,0 +1,112 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2021 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) + +interface intf (); + integer value; +endinterface + +module fanout + #(parameter int N = 1) + ( + intf upstream, + intf downstream[N-1:0] + ); + + genvar i; + for (i = 0; i < N; i = i + 1) + assign downstream[i].value = upstream.value; + +endmodule + +module xbar + ( + input logic clk, + input int cyc, + intf Masters[1:0] + ); + + localparam NUM_DEMUX_OUT = 2 * 4; + localparam NUM_MUX_IN = 2 * 4; + intf demuxOut[NUM_DEMUX_OUT-1:0](); + intf muxIn[NUM_MUX_IN-1:0](); + + //fan out master connections to the crossbar matrix + fanout #(.N(4)) fanout_inst0 + (.upstream(Masters[0]), + .downstream(demuxOut[3:0])); + + fanout #(.N(4)) fanout_inst1 + (.upstream(Masters[1]), + .downstream(demuxOut[7:4])); + + //the crossbar matrix assignments, done as 1D arrays because verilator doesn't currently support >1D arrays of interfaces + genvar slv, mst; + for (slv = 0; slv < 4; slv = slv + 1) begin + for (mst = 0; mst < 2; mst = mst + 1) begin + localparam int muxIdx = (slv*2)+mst; + localparam int demuxIdx = slv+(mst*4); + assign muxIn[muxIdx].value = demuxOut[demuxIdx].value; + end + end + + always @(posedge clk) begin + if (cyc == 5) begin + `checkh(Masters[0].value, 2); + `checkh(Masters[1].value, 1); + // The first 4 demuxOut values should have the value of the first Master + `checkh(demuxOut[0].value, Masters[0].value); + `checkh(demuxOut[1].value, Masters[0].value); + `checkh(demuxOut[2].value, Masters[0].value); + `checkh(demuxOut[3].value, Masters[0].value); + // The next 4 demuxOut values should have the value of the second Master + `checkh(demuxOut[4].value, Masters[1].value); + `checkh(demuxOut[5].value, Masters[1].value); + `checkh(demuxOut[6].value, Masters[1].value); + `checkh(demuxOut[7].value, Masters[1].value); + // Each 2 mux inputs should have one input from each master, in order from low to high + `checkh(muxIn[0].value, Masters[0].value); + `checkh(muxIn[1].value, Masters[1].value); + `checkh(muxIn[2].value, Masters[0].value); + `checkh(muxIn[3].value, Masters[1].value); + `checkh(muxIn[4].value, Masters[0].value); + `checkh(muxIn[5].value, Masters[1].value); + `checkh(muxIn[6].value, Masters[0].value); + `checkh(muxIn[7].value, Masters[1].value); + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + + +module t + ( + clk + ); + input clk; + + intf masters[1:0](); + + int cyc; + + xbar sub + (.clk, + .cyc, + .Masters(masters)); + + always @(posedge clk) begin + cyc <= cyc + 1; + if (cyc == 1) begin + masters[0].value <= 2; + masters[1].value <= 1; + end + end + +endmodule diff --git a/test_regress/t/t_mod_interface_array6.pl b/test_regress/t/t_mod_interface_array6.pl new file mode 100755 index 000000000..2cb5eeaff --- /dev/null +++ b/test_regress/t/t_mod_interface_array6.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 2021 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_mod_interface_array6.v b/test_regress/t/t_mod_interface_array6.v new file mode 100644 index 000000000..bb5fb50a5 --- /dev/null +++ b/test_regress/t/t_mod_interface_array6.v @@ -0,0 +1,158 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2021 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) + +interface intf (); + integer index; +endinterface + +module t + ( + clk + ); + input clk; + + intf ifa1_intf[4:1](); + intf ifa2_intf[4:1](); + intf ifb1_intf[1:4](); + intf ifb2_intf[1:4](); + + int cyc; + + sub sub0 + ( + .n(0), + .clk, + .cyc, + .alh(ifa1_intf[2:1]), + .ahl(ifa2_intf[2:1]), + .blh(ifb1_intf[1:2]), + .bhl(ifb2_intf[1:2]) + ); + + sub sub1 + ( + .n(1), + .clk, + .cyc, + .alh(ifa1_intf[4:3]), + .ahl(ifa2_intf[4:3]), + .blh(ifb1_intf[3:4]), + .bhl(ifb2_intf[3:4]) + ); + +`ifndef verilator // Backwards slicing not supported + sub sub2 + ( + .n(2), + .clk, + .cyc, + .alh(ifa1_intf[1:2]), // backwards vs decl + .ahl(ifa2_intf[1:2]), // backwards vs decl + .blh(ifb1_intf[2:1]), // backwards vs decl + .bhl(ifb2_intf[2:1]) // backwards vs decl + ); + + sub sub3 + ( + .n(3), + .clk, + .cyc, + .alh(ifa1_intf[3:4]), // backwards vs decl + .ahl(ifa2_intf[3:4]), // backwards vs decl + .blh(ifb1_intf[4:3]), // backwards vs decl + .bhl(ifb2_intf[4:3]) // backwards vs decl + ); +`endif + + always @(posedge clk) begin + cyc <= cyc + 1; + if (cyc == 1) begin + ifa1_intf[1].index = 'h101; + ifa1_intf[2].index = 'h102; + ifa1_intf[3].index = 'h103; + ifa1_intf[4].index = 'h104; + ifa2_intf[1].index = 'h201; + ifa2_intf[2].index = 'h202; + ifa2_intf[3].index = 'h203; + ifa2_intf[4].index = 'h204; + ifb1_intf[1].index = 'h301; + ifb1_intf[2].index = 'h302; + ifb1_intf[3].index = 'h303; + ifb1_intf[4].index = 'h304; + ifb2_intf[1].index = 'h401; + ifb2_intf[2].index = 'h402; + ifb2_intf[3].index = 'h403; + ifb2_intf[4].index = 'h404; + end + end + +endmodule + +module sub + ( + input logic clk, + input int cyc, + input int n, + intf alh[1:2], + intf ahl[2:1], + intf blh[1:2], + intf bhl[2:1] + ); + + always @(posedge clk) begin + + if (cyc == 5) begin + if (n == 0) begin + `checkh(alh[1].index, 'h102); + `checkh(alh[2].index, 'h101); + `checkh(ahl[1].index, 'h201); + `checkh(ahl[2].index, 'h202); + `checkh(blh[1].index, 'h301); + `checkh(blh[2].index, 'h302); + `checkh(bhl[1].index, 'h402); + `checkh(bhl[2].index, 'h401); + end + else if (n == 1) begin + `checkh(alh[1].index, 'h104); + `checkh(alh[2].index, 'h103); + `checkh(ahl[1].index, 'h203); + `checkh(ahl[2].index, 'h204); + `checkh(blh[1].index, 'h303); + `checkh(blh[2].index, 'h304); + `checkh(bhl[1].index, 'h404); + `checkh(bhl[2].index, 'h403); + end + else if (n == 2) begin + `checkh(alh[1].index, 'h101); + `checkh(alh[2].index, 'h102); + `checkh(ahl[1].index, 'h202); + `checkh(ahl[2].index, 'h201); + `checkh(blh[1].index, 'h302); + `checkh(blh[2].index, 'h301); + `checkh(bhl[1].index, 'h401); + `checkh(bhl[2].index, 'h402); + end + else if (n == 3) begin + `checkh(alh[1].index, 'h103); + `checkh(alh[2].index, 'h104); + `checkh(ahl[1].index, 'h204); + `checkh(ahl[2].index, 'h203); + `checkh(blh[1].index, 'h304); + `checkh(blh[2].index, 'h303); + `checkh(bhl[1].index, 'h403); + `checkh(bhl[2].index, 'h404); + end + end + if (cyc == 9 && n == 0) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_mod_interface_array6_noinl.pl b/test_regress/t/t_mod_interface_array6_noinl.pl new file mode 100755 index 000000000..5244ac42c --- /dev/null +++ b/test_regress/t/t_mod_interface_array6_noinl.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 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); + +top_filename("t/t_mod_interface_array6.v"); + +compile( + v_flags2 => ["-Oi"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_multitop_sig.cpp b/test_regress/t/t_multitop_sig.cpp index 4cdca1235..aa3f7459f 100644 --- a/test_regress/t/t_multitop_sig.cpp +++ b/test_regress/t/t_multitop_sig.cpp @@ -10,15 +10,11 @@ #include #include "Vt_multitop_sig.h" +#include "TestCheck.h" + double sc_time_stamp() { return 0; } -// Use cout to avoid issues with %d/%lx etc -#define CHECK_RESULT(got, exp) \ - if ((got) != (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << ": GOT = " << (got) \ - << " EXP = " << (exp) << std::endl; \ - return __LINE__; \ - } +int errors = 0; int main(int argc, char* argv[]) { Vt_multitop_sig* topp = new Vt_multitop_sig(""); @@ -30,20 +26,21 @@ int main(int argc, char* argv[]) { topp->b__02Ein = 0; topp->uniq_in = 0; topp->eval(); - CHECK_RESULT(topp->a__02Eout, 1); - CHECK_RESULT(topp->b__02Eout, 0); - CHECK_RESULT(topp->uniq_out, 1); + TEST_CHECK_EQ(topp->a__02Eout, 1); + TEST_CHECK_EQ(topp->b__02Eout, 0); + TEST_CHECK_EQ(topp->uniq_out, 1); topp->a__02Ein = 1; topp->b__02Ein = 1; topp->uniq_in = 1; topp->eval(); - CHECK_RESULT(topp->a__02Eout, 0); - CHECK_RESULT(topp->b__02Eout, 1); - CHECK_RESULT(topp->uniq_out, 0); + TEST_CHECK_EQ(topp->a__02Eout, 0); + TEST_CHECK_EQ(topp->b__02Eout, 1); + TEST_CHECK_EQ(topp->uniq_out, 0); } topp->final(); VL_DO_DANGLING(delete topp, topp); printf("*-* All Finished *-*\n"); + return errors ? 10 : 0; } diff --git a/test_regress/t/t_order_multidriven.cpp b/test_regress/t/t_order_multidriven.cpp index 9a5993967..1b9f6ef9a 100644 --- a/test_regress/t/t_order_multidriven.cpp +++ b/test_regress/t/t_order_multidriven.cpp @@ -41,10 +41,10 @@ static void cycle() { } int main() { + const std::unique_ptr contextp{new VerilatedContext}; + contextp->traceEverOn(true); - Verilated::traceEverOn(true); - - vcore = new Vt_order_multidriven; + vcore = new VM_PREFIX{contextp.get()}; vcd = new VerilatedVcdC; vcore->trace(vcd, 99); diff --git a/test_regress/t/t_preproc_debugi.pl b/test_regress/t/t_preproc_debugi.pl new file mode 100755 index 000000000..775712eb7 --- /dev/null +++ b/test_regress/t/t_preproc_debugi.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 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); + +# Hit the debug statements in the preprocessor for internal coverage + +run(cmd => ["../bin/verilator", + "-E", + "t/t_preproc_debugi.v", + "--debug", + "--debugi-V3PreShell 10", + ], + tee => $Self->{verbose}, + logfile => "$Self->{obj_dir}/sim.log", + verilator_run => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_preproc_debugi.v b/test_regress/t/t_preproc_debugi.v new file mode 100644 index 000000000..d044a69d7 --- /dev/null +++ b/test_regress/t/t_preproc_debugi.v @@ -0,0 +1,10 @@ +// 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 + +`define FOO +`define BAR(aa,bb) aa bb +`FOO +`BAR(aa,bb) diff --git a/test_regress/t/t_preproc_eof1_bad.out b/test_regress/t/t_preproc_eof1_bad.out new file mode 100644 index 000000000..23f5c48f7 --- /dev/null +++ b/test_regress/t/t_preproc_eof1_bad.out @@ -0,0 +1,2 @@ +%Error: t/t_preproc_eof1_bad.v:9:1: EOF in '/* ... */' block comment +%Error: Exiting due to diff --git a/test_regress/t/t_preproc_eof1_bad.pl b/test_regress/t/t_preproc_eof1_bad.pl new file mode 100755 index 000000000..a60503a1f --- /dev/null +++ b/test_regress/t/t_preproc_eof1_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_preproc_eof1_bad.v b/test_regress/t/t_preproc_eof1_bad.v new file mode 100644 index 000000000..84de21a44 --- /dev/null +++ b/test_regress/t/t_preproc_eof1_bad.v @@ -0,0 +1,7 @@ +// 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 + +/* diff --git a/test_regress/t/t_preproc_eof2_bad.out b/test_regress/t/t_preproc_eof2_bad.out new file mode 100644 index 000000000..ec4540052 --- /dev/null +++ b/test_regress/t/t_preproc_eof2_bad.out @@ -0,0 +1,2 @@ +%Error: t/t_preproc_eof2_bad.v:10:1: Unterminated ( in define formal arguments. +%Error: Exiting due to diff --git a/test_regress/t/t_preproc_eof2_bad.pl b/test_regress/t/t_preproc_eof2_bad.pl new file mode 100755 index 000000000..a60503a1f --- /dev/null +++ b/test_regress/t/t_preproc_eof2_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_preproc_eof2_bad.v b/test_regress/t/t_preproc_eof2_bad.v new file mode 100644 index 000000000..4009eed35 --- /dev/null +++ b/test_regress/t/t_preproc_eof2_bad.v @@ -0,0 +1,7 @@ +// 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 + +`define FOO(a, diff --git a/test_regress/t/t_preproc_eof3_bad.out b/test_regress/t/t_preproc_eof3_bad.out new file mode 100644 index 000000000..8306501ba --- /dev/null +++ b/test_regress/t/t_preproc_eof3_bad.out @@ -0,0 +1,7 @@ +%Error: t/t_preproc_eof3_bad.v:10:1: EOF in define argument list + 10 | %Error-internal-contents-bad-ct2-ln10 + | ^ +%Error: t/t_preproc_eof3_bad.v:10:1: Expecting ) or , to end argument list for define reference. Found: EOF + 10 | %Error-internal-contents-bad-ct2-ln10 + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_preproc_eof3_bad.pl b/test_regress/t/t_preproc_eof3_bad.pl new file mode 100755 index 000000000..a60503a1f --- /dev/null +++ b/test_regress/t/t_preproc_eof3_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_preproc_eof3_bad.v b/test_regress/t/t_preproc_eof3_bad.v new file mode 100644 index 000000000..4bfdf63c8 --- /dev/null +++ b/test_regress/t/t_preproc_eof3_bad.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, 2019 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define FOO(a,b) +`FOO(1, diff --git a/test_regress/t/t_preproc_eof4_bad.out b/test_regress/t/t_preproc_eof4_bad.out new file mode 100644 index 000000000..b41e72159 --- /dev/null +++ b/test_regress/t/t_preproc_eof4_bad.out @@ -0,0 +1,2 @@ +%Error: t/t_preproc_eof4_bad.v:8:1: Unterminated string +%Error: Exiting due to diff --git a/test_regress/t/t_preproc_eof4_bad.pl b/test_regress/t/t_preproc_eof4_bad.pl new file mode 100755 index 000000000..a60503a1f --- /dev/null +++ b/test_regress/t/t_preproc_eof4_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_preproc_eof4_bad.v b/test_regress/t/t_preproc_eof4_bad.v new file mode 100644 index 000000000..521c65dfa --- /dev/null +++ b/test_regress/t/t_preproc_eof4_bad.v @@ -0,0 +1,7 @@ +// 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 + +"blah diff --git a/test_regress/t/t_savable.v b/test_regress/t/t_savable.v index c5e010a4e..1fc854566 100644 --- a/test_regress/t/t_savable.v +++ b/test_regress/t/t_savable.v @@ -15,7 +15,7 @@ module t (/*AUTOARG*/ parameter MODEL_WIDTH = 10; input [MODEL_WIDTH-1:0] model; - initial $write("Model width = %d\n", MODEL_WIDTH); + initial $write("Model width = %0d\n", MODEL_WIDTH); sub sub (/*AUTOINST*/ // Inputs diff --git a/test_regress/t/t_savable_format1_bad.out b/test_regress/t/t_savable_format1_bad.out index 72f5bde4d..9f68e5cfd 100644 --- a/test_regress/t/t_savable_format1_bad.out +++ b/test_regress/t/t_savable_format1_bad.out @@ -1,4 +1,4 @@ -Model width = 40 +Model width = 40 Restoring model from 'obj_vlt/t_savable_format1_bad/saved.vltsv' %Error: obj_vlt/t_savable_format1_bad/saved.vltsv:0: Can't deserialize save-restore file as was made from different model: obj_vlt/t_savable_format1_bad/saved.vltsv Aborting... diff --git a/test_regress/t/t_savable_format2_bad.out b/test_regress/t/t_savable_format2_bad.out index 69680ca5b..5cf0ec859 100644 --- a/test_regress/t/t_savable_format2_bad.out +++ b/test_regress/t/t_savable_format2_bad.out @@ -1,4 +1,4 @@ -Model width = 10 +Model width = 10 Restoring model from 'obj_vlt/t_savable_format2_bad/saved.vltsv' %Error: obj_vlt/t_savable_format2_bad/saved.vltsv:0: Can't deserialize; file has wrong header signature, or file not found: obj_vlt/t_savable_format2_bad/saved.vltsv Aborting... diff --git a/test_regress/t/t_savable_format2_bad.pl b/test_regress/t/t_savable_format2_bad.pl index 8a6e75248..ee0a4d518 100755 --- a/test_regress/t/t_savable_format2_bad.pl +++ b/test_regress/t/t_savable_format2_bad.pl @@ -27,7 +27,7 @@ execute( # Break the header file_sed("$Self->{obj_dir}/saved.vltsv", "$Self->{obj_dir}/saved.vltsv", - sub { s/verilatorsave01/verilatorsavBAD/g; }); + sub { s/verilatorsave/verilatorsavBAD/g; }); execute( all_run_flags => ['+save_restore=1'], diff --git a/test_regress/t/t_savable_format3_bad.out b/test_regress/t/t_savable_format3_bad.out index 3389dee4b..7999da1d3 100644 --- a/test_regress/t/t_savable_format3_bad.out +++ b/test_regress/t/t_savable_format3_bad.out @@ -1,4 +1,4 @@ -Model width = 10 +Model width = 10 Restoring model from 'obj_vlt/t_savable_format3_bad/saved.vltsv' %Error: obj_vlt/t_savable_format3_bad/saved.vltsv:0: Can't deserialize; file has wrong end-of-file signature: obj_vlt/t_savable_format3_bad/saved.vltsv Aborting... diff --git a/test_regress/t/t_savable_open_bad.out b/test_regress/t/t_savable_open_bad.out index ca2151f02..5079f8e58 100644 --- a/test_regress/t/t_savable_open_bad.out +++ b/test_regress/t/t_savable_open_bad.out @@ -1,4 +1,4 @@ -Model width = 10 +Model width = 10 Restoring model from 'obj_vlt/t_savable_open_bad/saved.vltsv' %Error: obj_vlt/t_savable_open_bad/saved.vltsv:0: Can't deserialize; file has wrong header signature, or file not found: obj_vlt/t_savable_open_bad/saved.vltsv Aborting... diff --git a/test_regress/t/t_savable_open_bad2.cpp b/test_regress/t/t_savable_open_bad2.cpp index 1fb06a851..61f04db42 100644 --- a/test_regress/t/t_savable_open_bad2.cpp +++ b/test_regress/t/t_savable_open_bad2.cpp @@ -12,20 +12,12 @@ #include #include VM_PREFIX_INCLUDE -//====================================================================== - -#define CHECK_RESULT_HEX(got, exp) \ - do { \ - if ((got) != (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << std::hex \ - << ": GOT=" << (got) << " EXP=" << (exp) << std::endl; \ - exit(10); \ - } \ - } while (0) +#include "TestCheck.h" //====================================================================== unsigned int main_time = 0; +int errors = 0; double sc_time_stamp() { return main_time; } @@ -36,13 +28,13 @@ int main(int argc, char* argv[]) { { VerilatedSave os; os.open("/No_such_file_as_this"); - CHECK_RESULT_HEX(os.isOpen(), false); + TEST_CHECK_EQ(os.isOpen(), false); } { VerilatedRestore os; os.open("/No_such_file_as_this"); - CHECK_RESULT_HEX(os.isOpen(), false); + TEST_CHECK_EQ(os.isOpen(), false); } - return 0; + return errors ? 10 : 0; } diff --git a/test_regress/t/t_scope_map.cpp b/test_regress/t/t_scope_map.cpp index 912150699..a978627a7 100644 --- a/test_regress/t/t_scope_map.cpp +++ b/test_regress/t/t_scope_map.cpp @@ -14,16 +14,15 @@ #include "Vt_scope_map.h" -unsigned long long main_time = 0; -double sc_time_stamp() { return (double)main_time; } - const unsigned long long dt_2 = 3; int main(int argc, char** argv, char** env) { - Vt_scope_map* top = new Vt_scope_map("top"); + const std::unique_ptr contextp{new VerilatedContext}; - Verilated::debug(0); - Verilated::traceEverOn(true); + Vt_scope_map* top = new Vt_scope_map{contextp.get(), "top"}; + + contextp->debug(0); + contextp->traceEverOn(true); VerilatedVcdC* tfp = new VerilatedVcdC; top->trace(tfp, 99); @@ -31,10 +30,10 @@ int main(int argc, char** argv, char** env) { top->CLK = 0; top->eval(); - tfp->dump((unsigned int)(main_time)); - ++main_time; + tfp->dump(contextp->time()); + contextp->timeInc(1); - const VerilatedScopeNameMap* scopeMapp = Verilated::scopeNameMap(); + const VerilatedScopeNameMap* scopeMapp = contextp->scopeNameMap(); for (VerilatedScopeNameMap::const_iterator it = scopeMapp->begin(); it != scopeMapp->end(); ++it) { #ifdef TEST_VERBOSE @@ -106,14 +105,14 @@ int main(int argc, char** argv, char** env) { top->CLK = 0; top->eval(); - tfp->dump((unsigned int)(main_time)); - ++main_time; + tfp->dump(contextp->time()); + contextp->timeInc(1); // Posedge on clock, expect all the public bits to flip top->CLK = 1; top->eval(); - tfp->dump((unsigned int)(main_time)); - ++main_time; + tfp->dump(contextp->time()); + contextp->timeInc(1); for (VerilatedScopeNameMap::const_iterator it = scopeMapp->begin(); it != scopeMapp->end(); ++it) { @@ -153,8 +152,8 @@ int main(int argc, char** argv, char** env) { top->CLK = 0; top->eval(); - tfp->dump((unsigned int)(main_time)); - ++main_time; + tfp->dump(contextp->time()); + contextp->timeInc(1); tfp->close(); top->final(); diff --git a/test_regress/t/t_sys_system.v b/test_regress/t/t_sys_system.v index 2ea7a2c0d..54f4468af 100644 --- a/test_regress/t/t_sys_system.v +++ b/test_regress/t/t_sys_system.v @@ -16,8 +16,9 @@ module t; `endif `endif `endif - $system("exit 0"); - $system("echo hello"); + $system("ls"); // IData + $system("exit 0"); // QData + $system("echo hello"); // WDATA `ifndef VCS i = $system("exit 0"); if (i!==0) $stop; diff --git a/test_regress/t/t_timescale.cpp b/test_regress/t/t_timescale.cpp index f4a673912..84c53c583 100644 --- a/test_regress/t/t_timescale.cpp +++ b/test_regress/t/t_timescale.cpp @@ -5,6 +5,8 @@ #include +#include "TestCheck.h" + #include VM_PREFIX_INCLUDE unsigned long long main_time = 0; @@ -14,43 +16,39 @@ double sc_time_stamp() { return (double)main_time; } #define FILENM "t_timescale.cpp" -#define CHECK_RESULT(got, exp) \ - if ((got) != (exp)) { \ - std::cout << std::dec << "%Error: " << FILENM << ":" << __LINE__ << ": GOT = " << (got) \ - << " EXP = " << (exp) << std::endl; \ - return __LINE__; \ - } +int errors = 0; int main(int argc, char** argv, char** env) { VM_PREFIX* top = new VM_PREFIX("top"); - CHECK_RESULT(VL_TIME_STR_CONVERT("100s"), 2); - CHECK_RESULT(VL_TIME_STR_CONVERT("10s"), 1); - CHECK_RESULT(VL_TIME_STR_CONVERT("1s"), 0); - CHECK_RESULT(VL_TIME_STR_CONVERT("100ms"), -1); - CHECK_RESULT(VL_TIME_STR_CONVERT("10ms"), -2); - CHECK_RESULT(VL_TIME_STR_CONVERT("1ms"), -3); - CHECK_RESULT(VL_TIME_STR_CONVERT("100us"), -4); - CHECK_RESULT(VL_TIME_STR_CONVERT("10us"), -5); - CHECK_RESULT(VL_TIME_STR_CONVERT("1us"), -6); - CHECK_RESULT(VL_TIME_STR_CONVERT("100ns"), -7); - CHECK_RESULT(VL_TIME_STR_CONVERT("10ns"), -8); - CHECK_RESULT(VL_TIME_STR_CONVERT("1ns"), -9); - CHECK_RESULT(VL_TIME_STR_CONVERT("100ps"), -10); - CHECK_RESULT(VL_TIME_STR_CONVERT("10ps"), -11); - CHECK_RESULT(VL_TIME_STR_CONVERT("1ps"), -12); - CHECK_RESULT(VL_TIME_STR_CONVERT("100fs"), -13); - CHECK_RESULT(VL_TIME_STR_CONVERT("10fs"), -14); - CHECK_RESULT(VL_TIME_STR_CONVERT("1fs"), -15); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("100s"), 2); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("10s"), 1); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1s"), 0); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("100ms"), -1); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("10ms"), -2); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1ms"), -3); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("100us"), -4); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("10us"), -5); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1us"), -6); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("100ns"), -7); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("10ns"), -8); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1ns"), -9); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("100ps"), -10); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("10ps"), -11); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1ps"), -12); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("100fs"), -13); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("10fs"), -14); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1fs"), -15); - CHECK_RESULT(VL_TIME_STR_CONVERT("1.5s"), 0); - CHECK_RESULT(VL_TIME_STR_CONVERT("1s "), 0); - CHECK_RESULT(VL_TIME_STR_CONVERT("1ss"), 0); - CHECK_RESULT(VL_TIME_STR_CONVERT("s"), 0); - CHECK_RESULT(VL_TIME_STR_CONVERT(0), 0); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1.5s"), 0); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1s "), 0); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("1ss"), 0); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT("s"), 0); + TEST_CHECK_EQ(VL_TIME_STR_CONVERT(0), 0); top->final(); VL_DO_DANGLING(delete top, top); printf("*-* All Finished *-*\n"); - return 0; + + return errors ? 10 : 0; } diff --git a/test_regress/t/t_verilated_debug.out b/test_regress/t/t_verilated_debug.out index f4bc23936..4dd67d22f 100644 --- a/test_regress/t/t_verilated_debug.out +++ b/test_regress/t/t_verilated_debug.out @@ -8,6 +8,8 @@ internalsDump: -V{t#,#}+++++TOP Evaluate Vt_verilated_debug::eval -V{t#,#}+ Vt_verilated_debug::_eval_debug_assertions -V{t#,#}+ Vt_verilated_debug::_eval_initial +-V{t#,#}+ Vt_verilated_debug::_initial__TOP__1 + Data: w96: 000000aa 000000bb 000000cc -V{t#,#}+ Vt_verilated_debug::_eval_settle -V{t#,#}+ Vt_verilated_debug::_eval -V{t#,#}+ Vt_verilated_debug::_change_request @@ -20,7 +22,7 @@ internalsDump: -V{t#,#}+ Vt_verilated_debug::_eval_debug_assertions -V{t#,#}+ Clock loop -V{t#,#}+ Vt_verilated_debug::_eval --V{t#,#}+ Vt_verilated_debug::_sequent__TOP__1 +-V{t#,#}+ Vt_verilated_debug::_sequent__TOP__2 *-* All Finished *-* -V{t#,#}+ Vt_verilated_debug::_change_request -V{t#,#}+ Vt_verilated_debug::_change_request_1 diff --git a/test_regress/t/t_verilated_debug.v b/test_regress/t/t_verilated_debug.v index e541f39d9..9cd229c4f 100644 --- a/test_regress/t/t_verilated_debug.v +++ b/test_regress/t/t_verilated_debug.v @@ -11,6 +11,14 @@ module t (/*AUTOARG*/ input clk; + reg [95:0] wide; + + initial begin + // internal code coverage for _vl_debug_print_w + wide = {32'haa, 32'hbb, 32'hcc}; + $c("_vl_debug_print_w(",$bits(wide),",",wide,");"); + end + // Test loop always @ (posedge clk) begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_vpi_cb_iter.cpp b/test_regress/t/t_vpi_cb_iter.cpp index b71f33ff6..4fc10ec06 100644 --- a/test_regress/t/t_vpi_cb_iter.cpp +++ b/test_regress/t/t_vpi_cb_iter.cpp @@ -19,12 +19,13 @@ #include #include +#include "TestCheck.h" #include "TestSimulator.h" #include "TestVpi.h" #include "vpi_user.h" -bool got_error = false; +int errors = 0; TestVpiHandle vh_value_cb; TestVpiHandle vh_rw_cb; @@ -34,33 +35,6 @@ unsigned int last_rw_cb_time = 0; unsigned int main_time = 0; -#ifdef TEST_VERBOSE -bool verbose = true; -#else -bool verbose = false; -#endif - -#define CHECK_RESULT_NZ(got) \ - if (!(got)) { \ - printf("%%Error: %s:%d: GOT = NULL EXP = !NULL\n", __FILE__, __LINE__); \ - got_error = true; \ - } - -// Use cout to avoid issues with %d/%lx etc -#define CHECK_RESULT_NE(got, exp) \ - if ((got) == (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << ": GOT = " << (got) \ - << " EXP = !" << (exp) << std::endl; \ - got_error = true; \ - } - -// Use cout to avoid issues with %d/%lx etc -#define CHECK_RESULT(got, exp) \ - if ((got) != (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << ": GOT = " << (got) \ - << " EXP = " << (exp) << std::endl; \ - got_error = true; \ - } static void reregister_value_cb(); static void reregister_rw_cb(); @@ -79,13 +53,13 @@ static void reregister_value_cb() { if (verbose) vpi_printf(const_cast("- Removing cbValueChange callback\n")); int ret = vpi_remove_cb(vh_value_cb); vh_value_cb.freed(); - CHECK_RESULT(ret, 1); + TEST_CHECK_EQ(ret, 1); if (verbose) { vpi_printf(const_cast("- last_value_cb_time %d , main_time %d\n"), last_value_cb_time, main_time); } - CHECK_RESULT_NE(main_time, last_value_cb_time); + TEST_CHECK_NE(main_time, last_value_cb_time); last_value_cb_time = main_time; } if (verbose) vpi_printf(const_cast("- Registering cbValueChange callback\n")); @@ -95,7 +69,7 @@ static void reregister_value_cb() { cb_data_testcase.reason = cbValueChange; TestVpiHandle vh1 = VPI_HANDLE("count"); - CHECK_RESULT_NZ(vh1); + TEST_CHECK_NZ(vh1); s_vpi_value v; v.format = vpiSuppressVal; @@ -104,7 +78,7 @@ static void reregister_value_cb() { cb_data_testcase.value = &v; vh_value_cb = vpi_register_cb(&cb_data_testcase); - CHECK_RESULT_NZ(vh_value_cb); + TEST_CHECK_NZ(vh_value_cb); } static void reregister_rw_cb() { @@ -112,13 +86,13 @@ static void reregister_rw_cb() { if (verbose) vpi_printf(const_cast("- Removing cbReadWriteSynch callback\n")); int ret = vpi_remove_cb(vh_rw_cb); vh_rw_cb.freed(); - CHECK_RESULT(ret, 1); + TEST_CHECK_EQ(ret, 1); if (verbose) { vpi_printf(const_cast("- last_rw_cb_time %d , main_time %d\n"), last_rw_cb_time, main_time); } - CHECK_RESULT_NE(main_time, last_rw_cb_time); + TEST_CHECK_NE(main_time, last_rw_cb_time); last_rw_cb_time = main_time; } if (verbose) vpi_printf(const_cast("- Registering cbReadWriteSynch callback\n")); @@ -128,7 +102,7 @@ static void reregister_rw_cb() { cb_data_testcase.reason = cbReadWriteSynch; vh_rw_cb = vpi_register_cb(&cb_data_testcase); - CHECK_RESULT_NZ(vh_rw_cb); + TEST_CHECK_NZ(vh_rw_cb); } static int the_filler_callback(p_cb_data cb_data) { return 0; } @@ -143,7 +117,7 @@ static void register_filler_cb() { cb_data_1.reason = cbReadWriteSynch; TestVpiHandle cb_data_1_h = vpi_register_cb(&cb_data_1); - CHECK_RESULT_NZ(cb_data_1_h); + TEST_CHECK_NZ(cb_data_1_h); if (verbose) { vpi_printf(const_cast("- Registering filler cbValueChange callback\n")); @@ -154,7 +128,7 @@ static void register_filler_cb() { cb_data_2.reason = cbValueChange; TestVpiHandle vh2 = VPI_HANDLE("count"); - CHECK_RESULT_NZ(vh2); + TEST_CHECK_NZ(vh2); s_vpi_value v; v.format = vpiSuppressVal; @@ -163,7 +137,7 @@ static void register_filler_cb() { cb_data_2.value = &v; TestVpiHandle cb_data_2_h = vpi_register_cb(&cb_data_2); - CHECK_RESULT_NZ(cb_data_2_h); + TEST_CHECK_NZ(cb_data_2_h); } double sc_time_stamp() { return main_time; } @@ -176,9 +150,9 @@ int main(int argc, char** argv, char** env) { VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out reregister_value_cb(); - CHECK_RESULT_NZ(vh_value_cb); + TEST_CHECK_NZ(vh_value_cb); reregister_rw_cb(); - CHECK_RESULT_NZ(vh_rw_cb); + TEST_CHECK_NZ(vh_rw_cb); register_filler_cb(); topp->eval(); @@ -186,12 +160,12 @@ int main(int argc, char** argv, char** env) { while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { main_time += 1; - if (verbose) VL_PRINTF("Sim Time %d got_error %d\n", main_time, got_error); + if (verbose) VL_PRINTF("Sim Time %d got_error %d\n", main_time, errors); topp->clk = !topp->clk; topp->eval(); VerilatedVpi::callValueCbs(); VerilatedVpi::callCbs(cbReadWriteSynch); - if (got_error) vl_stop(__FILE__, __LINE__, "TOP-cpp"); + if (errors) vl_stop(__FILE__, __LINE__, "TOP-cpp"); } if (!Verilated::gotFinish()) { @@ -200,5 +174,5 @@ int main(int argc, char** argv, char** env) { topp->final(); VL_DO_DANGLING(delete topp, topp); - return 0; + return errors ? 10 : 0; } diff --git a/test_regress/t/t_vpi_time_cb.cpp b/test_regress/t/t_vpi_time_cb.cpp index e3f9748a5..c6a83d793 100644 --- a/test_regress/t/t_vpi_time_cb.cpp +++ b/test_regress/t/t_vpi_time_cb.cpp @@ -9,187 +9,23 @@ // //************************************************************************* -#ifdef IS_VPI - -#include "vpi_user.h" - -#else - #include "Vt_vpi_time_cb.h" #include "verilated.h" #include "svdpi.h" -#include #include "Vt_vpi_time_cb__Dpi.h" #include "verilated_vpi.h" #include "verilated_vcd_c.h" -#endif +#include "TestCheck.h" -#include -#include -#include #include -#include "TestSimulator.h" -#include "TestVpi.h" - unsigned int main_time = 0; -unsigned int callback_count_time1 = 3; -unsigned int callback_count_time2 = 4; -unsigned int callback_count_start_of_sim = 0; //====================================================================== -// Use cout to avoid issues with %d/%lx etc -#define CHECK_RESULT(got, exp) \ - if ((got) != (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << ": GOT = " << (got) \ - << " EXP = " << (exp) << std::endl; \ - return __LINE__; \ - } - -//====================================================================== - -#ifdef IS_VPI - -static int _never_cb(p_cb_data cb_data) { - CHECK_RESULT(0, 1); // Should never get called -} - -static int _time_cb1(p_cb_data cb_data) { - s_vpi_time t; - t.type = vpiSimTime; - vpi_get_time(0, &t); - // fprintf(stdout, "time_cb1: %d\n", t.low); - CHECK_RESULT(callback_count_time1, t.low); - callback_count_time1++; - - t_cb_data cb_data_n; - bzero(&cb_data_n, sizeof(cb_data_n)); - { - cb_data_n.reason = cbAfterDelay; - t.type = vpiSimTime; - t.high = 0; - t.low = 1; - cb_data_n.time = &t; - cb_data_n.cb_rtn = _time_cb1; - TestVpiHandle cb_data_n1_h = vpi_register_cb(&cb_data_n); - CHECK_RESULT(vpi_get(vpiType, cb_data_n1_h), vpiCallback); - } - { - // Test cancelling a callback - cb_data_n.reason = cbAfterDelay; - t.type = vpiSimTime; - t.high = 0; - t.low = 1; - cb_data_n.time = &t; - cb_data_n.cb_rtn = _never_cb; - TestVpiHandle cb_h = vpi_register_cb(&cb_data_n); - vpi_remove_cb(cb_h); - cb_h.freed(); - } - return 0; -} - -static int _time_cb2(p_cb_data cb_data) { - s_vpi_time t; - t.type = vpiSimTime; - vpi_get_time(0, &t); - // fprintf(stdout, "time_cb2: %d\n", t.low); - CHECK_RESULT(callback_count_time2, t.low); - callback_count_time2++; - - t_cb_data cb_data_n; - bzero(&cb_data_n, sizeof(cb_data_n)); - - cb_data_n.reason = cbAfterDelay; - t.type = vpiSimTime; - t.high = 0; - t.low = 1; - cb_data_n.time = &t; - cb_data_n.cb_rtn = _time_cb2; - TestVpiHandle cb_data_n2_h = vpi_register_cb(&cb_data_n); - CHECK_RESULT(vpi_get(vpiType, cb_data_n2_h), vpiCallback); - return 0; -} - -static int _start_of_sim_cb(p_cb_data cb_data) { -#ifdef TEST_VERBOSE - printf("-_start_of_sim_cb\n"); -#endif - - t_cb_data cb_data_n1, cb_data_n2; - bzero(&cb_data_n1, sizeof(cb_data_n1)); - bzero(&cb_data_n2, sizeof(cb_data_n2)); - s_vpi_time t1, t2; - - cb_data_n1.reason = cbAfterDelay; - t1.type = vpiSimTime; - t1.high = 0; - t1.low = 3; - cb_data_n1.time = &t1; - cb_data_n1.cb_rtn = _time_cb1; - TestVpiHandle cb_data_n1_h = vpi_register_cb(&cb_data_n1); - CHECK_RESULT(vpi_get(vpiType, cb_data_n1_h), vpiCallback); - - cb_data_n2.reason = cbAfterDelay; - t2.type = vpiSimTime; - t2.high = 0; - t2.low = 4; - cb_data_n2.time = &t2; - cb_data_n2.cb_rtn = _time_cb2; - TestVpiHandle cb_data_n2_h = vpi_register_cb(&cb_data_n2); - callback_count_start_of_sim++; - return 0; -} - -static int _end_of_sim_cb(p_cb_data cb_data) { - CHECK_RESULT(callback_count_start_of_sim, 1); - fprintf(stdout, "*-* All Finished *-*\n"); - return 0; -} - -// cver entry -#ifdef __cplusplus -extern "C" -#endif - - // clang-format off -void vpi_compat_bootstrap(void) { - // clang-format on - t_cb_data cb_data; - bzero(&cb_data, sizeof(cb_data)); - { - // VL_PRINTF("register start-of-sim callback\n"); - cb_data.reason = cbStartOfSimulation; - cb_data.time = 0; - cb_data.cb_rtn = _start_of_sim_cb; - TestVpiHandle _start_of_sim_cb_h = vpi_register_cb(&cb_data); - } - { - cb_data.reason = cbEndOfSimulation; - cb_data.time = 0; - cb_data.cb_rtn = _end_of_sim_cb; - TestVpiHandle _end_of_sim_cb_h = vpi_register_cb(&cb_data); - } - { - // Test cancelling a callback - cb_data.reason = cbStartOfSimulation; - cb_data.time = 0; - cb_data.cb_rtn = _never_cb; - TestVpiHandle cb_h = vpi_register_cb(&cb_data); - vpi_remove_cb(cb_h); - cb_h.freed(); - } -} - -// icarus entry -void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; - -#else - double sc_time_stamp() { return main_time; } int main(int argc, char** argv, char** env) { @@ -200,10 +36,8 @@ int main(int argc, char** argv, char** env) { VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out // clang-format off -#ifdef VERILATOR -# ifdef TEST_VERBOSE +#ifdef TEST_VERBOSE Verilated::scopesDump(); -# endif #endif // clang-format on @@ -215,32 +49,21 @@ int main(int argc, char** argv, char** env) { tfp->open(VL_STRINGIFY(TEST_OBJ_DIR) "/simx.vcd"); #endif - // Load and initialize the PLI application - { - const char* filenamep = VL_STRINGIFY(TEST_OBJ_DIR) "/libvpi.so"; - void* lib = dlopen(filenamep, RTLD_LAZY); - void* bootstrap = dlsym(lib, "vpi_compat_bootstrap"); - if (!bootstrap) { - std::string msg = std::string("%Error: Could not dlopen ") + filenamep; - vl_fatal(__FILE__, __LINE__, "main", msg.c_str()); - } - ((void (*)(void))bootstrap)(); - } - VerilatedVpi::callCbs(cbStartOfSimulation); topp->eval(); topp->clk = 0; - main_time += 1; while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { main_time += 1; topp->eval(); VerilatedVpi::callValueCbs(); VerilatedVpi::callTimedCbs(); - CHECK_RESULT(VerilatedVpi::cbNextDeadline(), main_time + 1); - topp->clk = !topp->clk; - // mon_do(); + if (main_time > 20) { // Else haven't registered callbacks + TEST_CHECK_EQ(VerilatedVpi::cbNextDeadline(), main_time + 1); + } + if ((main_time % 5) == 0) topp->clk = !topp->clk; + // mon_do(); #if VM_TRACE if (tfp) tfp->dump(main_time); #endif @@ -258,7 +81,5 @@ int main(int argc, char** argv, char** env) { #endif VL_DO_DANGLING(delete topp, topp); - return 0; + return errors ? 10 : 0; } - -#endif diff --git a/test_regress/t/t_vpi_time_cb.pl b/test_regress/t/t_vpi_time_cb.pl index 98dd60d00..ec265a4e2 100755 --- a/test_regress/t/t_vpi_time_cb.pl +++ b/test_regress/t/t_vpi_time_cb.pl @@ -13,17 +13,14 @@ scenarios(simulator => 1); compile( make_top_shell => 0, make_main => 0, - make_pli => 1, sim_time => 2100, - iv_flags2 => ["-g2005-sv -D USE_VPI_NOT_DPI -DWAVES -DIVERILOG"], - v_flags2 => ["+define+USE_VPI_NOT_DPI"], - verilator_flags2 => ["-CFLAGS '-DVL_DEBUG -ggdb' --exe --vpi --no-l2name $Self->{t_dir}/t_vpi_time_cb.cpp -LDFLAGS '-ldl -rdynamic'"], + v_flags2 => ["t/t_vpi_time_cb_c.cpp"], + iv_flags2 => ["-g2005-sv -DWAVES -DIVERILOG"], + verilator_flags2 => ["-CFLAGS '-DVL_DEBUG -ggdb' --exe --vpi --no-l2name $Self->{t_dir}/t_vpi_time_cb.cpp"], ); execute( - use_libvpi => 1, check_finished => 1, - all_run_flags => ['+PLUS +INT=1234 +STRSTR'] ); ok(1); diff --git a/test_regress/t/t_vpi_time_cb.v b/test_regress/t/t_vpi_time_cb.v index 1ac2d001f..5b1249718 100644 --- a/test_regress/t/t_vpi_time_cb.v +++ b/test_regress/t/t_vpi_time_cb.v @@ -6,6 +6,9 @@ // Version 2.0. // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +import "DPI-C" function void dpii_init(); +import "DPI-C" function void dpii_final(); + module t (/*AUTOARG*/ // Inputs clk @@ -19,15 +22,18 @@ module t (/*AUTOARG*/ // Test loop initial begin count = 0; + dpii_init(); end always @(posedge clk) begin `ifdef TEST_VERBOSE - $display("[%0t] clk", $time); + $display("[%0t] clk @ count %0d", $time, count); `endif count <= count + 2; - if (count == 1000) begin + if (count == 200) begin + $display("Final section"); // See C++ code: $write("*-* All Finished *-*\n"); + dpii_final(); $finish; end end diff --git a/test_regress/t/t_vpi_time_cb_c.cpp b/test_regress/t/t_vpi_time_cb_c.cpp new file mode 100644 index 000000000..c6a82aae3 --- /dev/null +++ b/test_regress/t/t_vpi_time_cb_c.cpp @@ -0,0 +1,134 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2010-2011 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 +// +//************************************************************************* + +#include "vpi_user.h" + +#include +#include +#include +#include + +#include "TestCheck.h" +#include "TestSimulator.h" +#include "TestVpi.h" + +int errors = 0; + +unsigned int callback_count1 = 0; +unsigned int callback_count2 = 0; +unsigned int callback_time1 = 0; +unsigned int callback_time2 = 0; + +//====================================================================== + +static int _never_cb(p_cb_data cb_data) { + TEST_CHECK_EQ(0, 1); // Should never get called + return 0; +} + +static int _time_cb1(p_cb_data cb_data) { + s_vpi_time t; + t.type = vpiSimTime; + vpi_get_time(0, &t); + TEST_VERBOSE_PRINTF("time_cb1: %d\n", t.low); + ++callback_count1; + if (callback_time1) TEST_CHECK_EQ(callback_time1, t.low); + callback_time1 = t.low + 1; // Next call + + t_cb_data cb_data_n; + bzero(&cb_data_n, sizeof(cb_data_n)); + { + cb_data_n.reason = cbAfterDelay; + t.type = vpiSimTime; + t.high = 0; + t.low = 1; + cb_data_n.time = &t; + cb_data_n.cb_rtn = _time_cb1; + TestVpiHandle cb_data_n1_h = vpi_register_cb(&cb_data_n); + TEST_CHECK_EQ(vpi_get(vpiType, cb_data_n1_h), vpiCallback); + } + { + // Test cancelling a callback + cb_data_n.reason = cbAfterDelay; + t.type = vpiSimTime; + t.high = 0; + t.low = 1; + cb_data_n.time = &t; + cb_data_n.cb_rtn = _never_cb; + TestVpiHandle cb_h = vpi_register_cb(&cb_data_n); + vpi_remove_cb(cb_h); + cb_h.freed(); + } + return 0; +} + +static int _time_cb2(p_cb_data cb_data) { + // One-shot + s_vpi_time t; + t.type = vpiSimTime; + vpi_get_time(0, &t); + TEST_VERBOSE_PRINTF("time_cb2: %d\n", t.low); + if (callback_time2) TEST_CHECK_EQ(callback_time2, t.low); + ++callback_count2; + + t_cb_data cb_data_n; + bzero(&cb_data_n, sizeof(cb_data_n)); + + cb_data_n.reason = cbAfterDelay; + t.type = vpiSimTime; + t.high = 0; + t.low = 1; + cb_data_n.time = &t; + cb_data_n.cb_rtn = _time_cb2; + TestVpiHandle cb_data_n2_h = vpi_register_cb(&cb_data_n); + TEST_CHECK_EQ(vpi_get(vpiType, cb_data_n2_h), vpiCallback); + return 0; +} + +extern "C" void dpii_init() { + TEST_VERBOSE_PRINTF("-dpii_init()\n"); + + t_cb_data cb_data_n1, cb_data_n2; + bzero(&cb_data_n1, sizeof(cb_data_n1)); + bzero(&cb_data_n2, sizeof(cb_data_n2)); + s_vpi_time t1, t2; + + cb_data_n1.reason = cbAfterDelay; + t1.type = vpiSimTime; + t1.high = 0; + t1.low = 3; + cb_data_n1.time = &t1; + cb_data_n1.cb_rtn = _time_cb1; + TestVpiHandle cb_data_n1_h = vpi_register_cb(&cb_data_n1); + TEST_CHECK_EQ(vpi_get(vpiType, cb_data_n1_h), vpiCallback); + + cb_data_n2.reason = cbAfterDelay; + t2.type = vpiSimTime; + t2.high = 0; + t2.low = 4; + cb_data_n2.time = &t2; + cb_data_n2.cb_rtn = _time_cb2; + TestVpiHandle cb_data_n2_h = vpi_register_cb(&cb_data_n2); +} + +extern "C" void dpii_final() { + TEST_VERBOSE_PRINTF("-dpii_final()\n"); + + // Allow some slop as cb might be before/after this call + TEST_CHECK(callback_count1, 1010, (callback_count1 >= 1000 && callback_count1 <= 1020)); + TEST_CHECK(callback_count2, 1010, (callback_count2 >= 1000 && callback_count2 <= 1020)); + + if (errors) { + vpi_control(vpiStop); + } else { + printf("*-* All Finished *-*\n"); + } +} diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index d9ec774b2..9ddd720e2 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -498,7 +498,7 @@ int _mon_check_putget_str(p_cb_data cb_data) { static TestVpiHandle cb; static struct { TestVpiHandle scope, sig, rfr, check, verbose; - char str[128 + 1]; // char per bit plus null terminator + std::string str; int type; // value type in .str union { PLI_INT32 integer; @@ -532,10 +532,10 @@ int _mon_check_putget_str(p_cb_data cb_data) { vpi_get_value(data[i].sig, &v); TEST_MSG("%s\n", v.value.str); if (data[i].type) { - CHECK_RESULT_CSTR(v.value.str, data[i].str); + CHECK_RESULT_CSTR(v.value.str, data[i].str.c_str()); } else { data[i].type = v.format; - strcpy(data[i].str, v.value.str); + data[i].str = std::string(v.value.str); } } @@ -556,7 +556,7 @@ int _mon_check_putget_str(p_cb_data cb_data) { if (callback_count_strs & 7) { // put same value back - checking encoding/decoding equivalent v.format = data[i].type; - v.value.str = data[i].str; + v.value.str = (PLI_BYTE8*)(data[i].str.c_str()); // Can't reinterpret_cast vpi_put_value(data[i].sig, &v, &t, vpiNoDelay); v.format = vpiIntVal; v.value.integer = 1; diff --git a/test_regress/t/t_vpi_zero_time_cb.cpp b/test_regress/t/t_vpi_zero_time_cb.cpp index 1c1f15e5d..865472719 100644 --- a/test_regress/t/t_vpi_zero_time_cb.cpp +++ b/test_regress/t/t_vpi_zero_time_cb.cpp @@ -32,25 +32,17 @@ #include #include +#include "TestCheck.h" #include "TestSimulator.h" #include "TestVpi.h" +int errors = 0; unsigned int main_time = 0; unsigned int callback_count_zero_time = 0; unsigned int callback_count_start_of_sim = 0; //====================================================================== -// Use cout to avoid issues with %d/%lx etc -#define CHECK_RESULT(got, exp) \ - if ((got) != (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << ": GOT = " << (got) \ - << " EXP = " << (exp) << std::endl; \ - return __LINE__; \ - } - -//====================================================================== - #ifdef IS_VPI static int _zero_time_cb(p_cb_data cb_data) { @@ -79,9 +71,9 @@ static int _start_of_sim_cb(p_cb_data cb_data) { } static int _end_of_sim_cb(p_cb_data cb_data) { - CHECK_RESULT(callback_count_start_of_sim, 1); - CHECK_RESULT(callback_count_zero_time, 1); - fprintf(stdout, "*-* All Finished *-*\n"); + TEST_CHECK_EQ(callback_count_start_of_sim, 1); + TEST_CHECK_EQ(callback_count_zero_time, 1); + if (!errors) fprintf(stdout, "*-* All Finished *-*\n"); return 0; } diff --git a/test_regress/t/t_wire_self_bad.out b/test_regress/t/t_wire_self_bad.out new file mode 100644 index 000000000..cff5ee7a9 --- /dev/null +++ b/test_regress/t/t_wire_self_bad.out @@ -0,0 +1,4 @@ +%Error: t/t_wire_self_bad.v:11:16: Wire inputs its own output, creating circular logic (wire x=x) + 11 | wire myself = myself; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_wire_self_bad.pl b/test_regress/t/t_wire_self_bad.pl new file mode 100755 index 000000000..85114ac5d --- /dev/null +++ b/test_regress/t/t_wire_self_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 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( + verilator_flags2 => ["--lint-only --language 1800-2017"], + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_wire_self_bad.v b/test_regress/t/t_wire_self_bad.v new file mode 100644 index 000000000..be5c1b5ee --- /dev/null +++ b/test_regress/t/t_wire_self_bad.v @@ -0,0 +1,13 @@ +// DESCRIPTION: Verilator: Verilog Test module for SystemVerilog 'alias' +// +// Simple bi-directional alias test. +// +// 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 (/*AUTOARG*/); + + wire myself = myself; + +endmodule diff --git a/test_regress/t/t_wrapper_context.cpp b/test_regress/t/t_wrapper_context.cpp new file mode 100644 index 000000000..d898c212d --- /dev/null +++ b/test_regress/t/t_wrapper_context.cpp @@ -0,0 +1,124 @@ +// +// DESCRIPTION: Verilator: Verilog Multiple Model Test Module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020-2021 by Andreas Kuster. +// SPDX-License-Identifier: CC0-1.0 +// + +#include +#include + +#include +#include + +#include VM_PREFIX_INCLUDE + +double sc_time_stamp() { return 0; } + +VerilatedMutex outputMutex; + +#ifdef T_WRAPPER_CONTEXT +#elif defined(T_WRAPPER_CONTEXT_SEQ) +VerilatedMutex sequentialMutex; +#elif defined(T_WRAPPER_CONTEXT_FST) +#else +#error "Unexpected test name" +#endif + +void sim(VM_PREFIX* topp) { +#ifdef T_WRAPPER_CONTEXT_SEQ + // Run each sim sequentially + const VerilatedLockGuard seqLock(sequentialMutex); +#endif + + VerilatedContext* contextp = topp->contextp(); + // This test created a thread, so need to associate VerilatedContext with it + Verilated::threadContextp(contextp); + + // reset + topp->clk = 0; + topp->rst = 1; + topp->stop = (topp->trace_number == 0); + topp->eval(); + + contextp->timeInc(1); + topp->clk = 1; + topp->eval(); + + contextp->timeInc(1); + topp->rst = 0; + topp->clk = 0; + topp->eval(); + + // simulate until done + while (!contextp->gotFinish()) { + // increment time + contextp->timeInc(1); + + { + const VerilatedLockGuard lock(outputMutex); +#ifdef TEST_VERBOSE + // std::endl needed to flush output before mutex release + std::cout << "{top" << topp->trace_number + << ", ctx=" << reinterpret_cast(contextp) << "} [" << contextp->time() + << "]" << std::endl; +#endif + } + + // toggle clk + topp->clk = !topp->clk; + + // evaluate model + topp->eval(); + } + + std::string filename + = std::string(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage_") + topp->name() + ".dat"; + contextp->coveragep()->write(filename.c_str()); +} + +int main(int argc, char** argv, char** env) { + // Create contexts + std::unique_ptr context0p{new VerilatedContext}; + std::unique_ptr context1p{new VerilatedContext}; + + // configuration + context0p->fatalOnError(false); + context1p->fatalOnError(false); + context0p->traceEverOn(true); + context1p->traceEverOn(true); + + // instantiate verilated design + std::unique_ptr top0p{new VM_PREFIX{context0p.get(), "top0"}}; + std::unique_ptr top1p{new VM_PREFIX{context1p.get(), "top1"}}; + + top0p->trace_number = 0; + top0p->trace_number = 1; + + std::cout << "Below '%Error: ... Verilog $stop' is expected as part of the test\n"; + + // create threads + std::thread t0(sim, top0p.get()); + std::thread t1(sim, top1p.get()); + + // wait to finish + t0.join(); + t1.join(); + + // check if both finished + bool pass = true; + if (top0p->done_o && top1p->done_o) { + std::cout << "*-* All Finished *-*" << std::endl; + } else { + std::cout << "Error: Early termination!" << std::endl; + pass = false; + } + + // final model cleanup + top0p->final(); + top1p->final(); + + // exit successful + return pass ? 0 : 10; +} diff --git a/test_regress/t/t_wrapper_context.pl b/test_regress/t/t_wrapper_context.pl new file mode 100755 index 000000000..e95e77250 --- /dev/null +++ b/test_regress/t/t_wrapper_context.pl @@ -0,0 +1,33 @@ +#!/usr/bin/env perl +if (!$::Driver) { use strict; use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Multiple Model Test Module +# +# Copyright 2020-2021 by Andreas Kuster. 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_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + # link threads library, add custom .cpp code, add tracing & coverage support + verilator_flags2 => ["-threads 1 --exe $Self->{t_dir}/$Self->{name}.cpp", + "--trace --coverage -cc"], + make_flags => 'CPPFLAGS_ADD=-DVL_NO_LEGACY', + ); + +execute( + check_finished => 1, + ); + +files_identical_sorted("$Self->{obj_dir}/coverage_top0.dat", "t/t_wrapper_context_top0.out"); +files_identical_sorted("$Self->{obj_dir}/coverage_top1.dat", "t/t_wrapper_context_top1.out"); + +vcd_identical("$Self->{obj_dir}/trace0.vcd", "t/t_wrapper_context_trace0.out"); +vcd_identical("$Self->{obj_dir}/trace1.vcd", "t/t_wrapper_context_trace1.out"); + +ok(1); +1; diff --git a/test_regress/t/t_wrapper_context.v b/test_regress/t/t_wrapper_context.v new file mode 100644 index 000000000..e892d6138 --- /dev/null +++ b/test_regress/t/t_wrapper_context.v @@ -0,0 +1,54 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This model counts from 0 to 10. It is instantiated twice in concurrent +// threads to check for race conditions/signal interference. +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020-2021 by Andreas Kuster. +// SPDX-License-Identifier: CC0-1.0 + +`define STRINGIFY(x) `"x`" + +module top + ( + input clk, + input rst, + input [31:0] trace_number, + input stop, + output bit [31:0] counter, + output bit done_o + ); + + initial begin + string number; + string filename; + number.itoa(trace_number); + filename = {`STRINGIFY(`TEST_OBJ_DIR), "/trace", number, ".vcd"}; + $display("Writing dumpfile '%s'", filename); + $dumpfile(filename); + $dumpvars(); + end + + always@(posedge clk) begin + if (rst) + counter <= 0; + else + counter <= counter + 1; + end + always_comb begin + done_o = '0; + if (stop) begin + if (counter >= 5 && stop) begin + done_o = '1; + $stop; + end + end + else begin + if (counter >= 10) begin + done_o = '1; + $finish; + end + end + end + +endmodule diff --git a/test_regress/t/t_wrapper_context_fst.pl b/test_regress/t/t_wrapper_context_fst.pl new file mode 100755 index 000000000..8b40a7b2d --- /dev/null +++ b/test_regress/t/t_wrapper_context_fst.pl @@ -0,0 +1,29 @@ +#!/usr/bin/env perl +if (!$::Driver) { use strict; use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Multiple Model Test Module +# +# Copyright 2020-2021 by Andreas Kuster. 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_all => 1); + +top_filename("t/t_wrapper_context.v"); + +compile( + make_top_shell => 0, + make_main => 0, + # link threads library, add custom .cpp code, add tracing & coverage support + verilator_flags2 => ["-threads 1 --exe $Self->{t_dir}/t_wrapper_context.cpp", + "--trace-fst --coverage -cc"], + make_flags => 'CPPFLAGS_ADD=-DVL_NO_LEGACY', + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_wrapper_context_seq.pl b/test_regress/t/t_wrapper_context_seq.pl new file mode 100755 index 000000000..8ddf958f8 --- /dev/null +++ b/test_regress/t/t_wrapper_context_seq.pl @@ -0,0 +1,29 @@ +#!/usr/bin/env perl +if (!$::Driver) { use strict; use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Multiple Model Test Module +# +# Copyright 2020-2021 by Andreas Kuster. 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_all => 1); + +top_filename("t/t_wrapper_context.v"); + +compile( + make_top_shell => 0, + make_main => 0, + # link threads library, add custom .cpp code, add tracing & coverage support + verilator_flags2 => ["-threads 1 --exe $Self->{t_dir}/t_wrapper_context.cpp", + "--trace --coverage -cc"], + make_flags => 'CPPFLAGS_ADD=-DVL_NO_LEGACY', + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_wrapper_context_top0.out b/test_regress/t/t_wrapper_context_top0.out new file mode 100644 index 000000000..4affa5cef --- /dev/null +++ b/test_regress/t/t_wrapper_context_top0.out @@ -0,0 +1,79 @@ +# SystemC::Coverage-3 +C 'ft/t_wrapper_context.vl14n22pagev_toggle/topoclkhtop0.top' 21 +C 'ft/t_wrapper_context.vl15n22pagev_toggle/toporsthtop0.top' 2 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[0]htop0.top' 1 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[10]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[11]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[12]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[13]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[14]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[15]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[16]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[17]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[18]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[19]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[1]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[20]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[21]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[22]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[23]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[24]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[25]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[26]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[27]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[28]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[29]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[2]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[30]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[31]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[3]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[4]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[5]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[6]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[7]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[8]htop0.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[9]htop0.top' 0 +C 'ft/t_wrapper_context.vl17n22pagev_toggle/topostophtop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[0]htop0.top' 10 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[10]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[11]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[12]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[13]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[14]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[15]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[16]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[17]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[18]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[19]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[1]htop0.top' 5 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[20]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[21]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[22]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[23]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[24]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[25]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[26]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[27]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[28]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[29]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[2]htop0.top' 2 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[30]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[31]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[3]htop0.top' 1 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[4]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[5]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[6]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[7]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[8]htop0.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[9]htop0.top' 0 +C 'ft/t_wrapper_context.vl19n22pagev_toggle/topodone_ohtop0.top' 1 +C 'ft/t_wrapper_context.vl22n4pagev_line/topoblockS22,25-29htop0.top' 1 +C 'ft/t_wrapper_context.vl32n4pagev_line/topoblockS32htop0.top' 11 +C 'ft/t_wrapper_context.vl33n7pagev_branch/topoifS33-34htop0.top' 1 +C 'ft/t_wrapper_context.vl33n8pagev_branch/topoelseS36htop0.top' 10 +C 'ft/t_wrapper_context.vl38n4pagev_line/topoblockS38-39htop0.top' 1 +C 'ft/t_wrapper_context.vl40n7pagev_branch/topoifS40htop0.top' 0 +C 'ft/t_wrapper_context.vl40n8pagev_branch/topoelseS46htop0.top' 24 +C 'ft/t_wrapper_context.vl41n11pagev_line/topoelsehtop0.top' 0 +C 'ft/t_wrapper_context.vl47n10pagev_branch/topoifS47-49htop0.top' 1 +C 'ft/t_wrapper_context.vl47n11pagev_branch/topoelsehtop0.top' 23 diff --git a/test_regress/t/t_wrapper_context_top1.out b/test_regress/t/t_wrapper_context_top1.out new file mode 100644 index 000000000..84013beb4 --- /dev/null +++ b/test_regress/t/t_wrapper_context_top1.out @@ -0,0 +1,79 @@ +# SystemC::Coverage-3 +C 'ft/t_wrapper_context.vl14n22pagev_toggle/topoclkhtop1.top' 11 +C 'ft/t_wrapper_context.vl15n22pagev_toggle/toporsthtop1.top' 2 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[0]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[10]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[11]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[12]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[13]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[14]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[15]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[16]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[17]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[18]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[19]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[1]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[20]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[21]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[22]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[23]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[24]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[25]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[26]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[27]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[28]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[29]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[2]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[30]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[31]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[3]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[4]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[5]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[6]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[7]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[8]htop1.top' 0 +C 'ft/t_wrapper_context.vl16n22pagev_toggle/topotrace_number[9]htop1.top' 0 +C 'ft/t_wrapper_context.vl17n22pagev_toggle/topostophtop1.top' 1 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[0]htop1.top' 5 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[10]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[11]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[12]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[13]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[14]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[15]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[16]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[17]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[18]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[19]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[1]htop1.top' 2 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[20]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[21]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[22]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[23]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[24]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[25]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[26]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[27]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[28]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[29]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[2]htop1.top' 1 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[30]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[31]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[3]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[4]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[5]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[6]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[7]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[8]htop1.top' 0 +C 'ft/t_wrapper_context.vl18n22pagev_toggle/topocounter[9]htop1.top' 0 +C 'ft/t_wrapper_context.vl19n22pagev_toggle/topodone_ohtop1.top' 1 +C 'ft/t_wrapper_context.vl22n4pagev_line/topoblockS22,25-29htop1.top' 1 +C 'ft/t_wrapper_context.vl32n4pagev_line/topoblockS32htop1.top' 6 +C 'ft/t_wrapper_context.vl33n7pagev_branch/topoifS33-34htop1.top' 1 +C 'ft/t_wrapper_context.vl33n8pagev_branch/topoelseS36htop1.top' 5 +C 'ft/t_wrapper_context.vl38n4pagev_line/topoblockS38-39htop1.top' 1 +C 'ft/t_wrapper_context.vl40n7pagev_branch/topoifS40htop1.top' 14 +C 'ft/t_wrapper_context.vl40n8pagev_branch/topoelseS46htop1.top' 0 +C 'ft/t_wrapper_context.vl41n11pagev_line/topoelsehtop1.top' 13 +C 'ft/t_wrapper_context.vl47n10pagev_branch/topoifS47-49htop1.top' 0 +C 'ft/t_wrapper_context.vl47n11pagev_branch/topoelsehtop1.top' 0 diff --git a/test_regress/t/t_wrapper_context_trace0.out b/test_regress/t/t_wrapper_context_trace0.out new file mode 100644 index 000000000..cc90048e5 --- /dev/null +++ b/test_regress/t/t_wrapper_context_trace0.out @@ -0,0 +1,59 @@ +$version Generated by VerilatedVcd $end +$date Sat Mar 6 21:09:45 2021 $end +$timescale 1ps $end + + $scope module top1 $end + $var wire 1 # clk $end + $var wire 32 ' counter [31:0] $end + $var wire 1 ( done_o $end + $var wire 1 $ rst $end + $var wire 1 & stop $end + $var wire 32 % trace_number [31:0] $end + $scope module top $end + $var wire 1 # clk $end + $var wire 32 ' counter [31:0] $end + $var wire 1 ( done_o $end + $var wire 1 $ rst $end + $var wire 1 & stop $end + $var wire 32 % trace_number [31:0] $end + $upscope $end + $upscope $end +$enddefinitions $end + + +#0 +0# +1$ +b00000000000000000000000000000000 % +1& +b00000000000000000000000000000000 ' +0( +#1 +1# +#2 +0# +0$ +#3 +1# +b00000000000000000000000000000001 ' +#4 +0# +#5 +1# +b00000000000000000000000000000010 ' +#6 +0# +#7 +1# +b00000000000000000000000000000011 ' +#8 +0# +#9 +1# +b00000000000000000000000000000100 ' +#10 +0# +#11 +1# +b00000000000000000000000000000101 ' +1( diff --git a/test_regress/t/t_wrapper_context_trace1.out b/test_regress/t/t_wrapper_context_trace1.out new file mode 100644 index 000000000..3fa5258a3 --- /dev/null +++ b/test_regress/t/t_wrapper_context_trace1.out @@ -0,0 +1,84 @@ +$version Generated by VerilatedVcd $end +$date Sat Mar 6 21:09:47 2021 $end +$timescale 1ps $end + + $scope module top0 $end + $var wire 1 # clk $end + $var wire 32 ' counter [31:0] $end + $var wire 1 ( done_o $end + $var wire 1 $ rst $end + $var wire 1 & stop $end + $var wire 32 % trace_number [31:0] $end + $scope module top $end + $var wire 1 # clk $end + $var wire 32 ' counter [31:0] $end + $var wire 1 ( done_o $end + $var wire 1 $ rst $end + $var wire 1 & stop $end + $var wire 32 % trace_number [31:0] $end + $upscope $end + $upscope $end +$enddefinitions $end + + +#0 +0# +1$ +b00000000000000000000000000000001 % +0& +b00000000000000000000000000000000 ' +0( +#1 +1# +#2 +0# +0$ +#3 +1# +b00000000000000000000000000000001 ' +#4 +0# +#5 +1# +b00000000000000000000000000000010 ' +#6 +0# +#7 +1# +b00000000000000000000000000000011 ' +#8 +0# +#9 +1# +b00000000000000000000000000000100 ' +#10 +0# +#11 +1# +b00000000000000000000000000000101 ' +#12 +0# +#13 +1# +b00000000000000000000000000000110 ' +#14 +0# +#15 +1# +b00000000000000000000000000000111 ' +#16 +0# +#17 +1# +b00000000000000000000000000001000 ' +#18 +0# +#19 +1# +b00000000000000000000000000001001 ' +#20 +0# +#21 +1# +b00000000000000000000000000001010 ' +1( diff --git a/test_regress/t/t_wrapper_legacy.cpp b/test_regress/t/t_wrapper_legacy.cpp index d93356a6a..c0dc195d2 100644 --- a/test_regress/t/t_wrapper_legacy.cpp +++ b/test_regress/t/t_wrapper_legacy.cpp @@ -16,28 +16,15 @@ #include #include -bool got_error = false; +#include "TestCheck.h" -// Use cout to avoid issues with %d/%lx etc -#define CHECK_RESULT(got, exp) \ - if ((got) != (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << ": GOT = " << (got) \ - << " EXP = " << (exp) << std::endl; \ - got_error = true; \ - } -#define CHECK_RESULT_CSTR(got, exp) \ - if (strcmp((got), (exp))) { \ - printf("%%Error: %s:%d: GOT = '%s' EXP = '%s'\n", __FILE__, __LINE__, \ - (got) ? (got) : "", (exp) ? (exp) : ""); \ - return __LINE__; \ - } +int errors = 0; vluint64_t main_time = 0; - -#ifdef T_WRAPPER_LEGACY_TIME64 -vluint64_t vl_time_stamp64() { return main_time; } -#endif #ifdef T_WRAPPER_LEGACY +#elif defined(T_WRAPPER_LEGACY_TIME64) +vluint64_t vl_time_stamp64() { return main_time; } +#elif defined(T_WRAPPER_LEGACY_TIMED) double sc_time_stamp() { return main_time; } #endif @@ -48,36 +35,52 @@ int main(int argc, char** argv, char** env) { // Many used only by git@github.com:djg/verilated-rs.git Verilated::commandArgs(argc, argv); // Commonly used - CHECK_RESULT_CSTR(Verilated::commandArgsPlusMatch("not-matching"), ""); + TEST_CHECK_CSTR(Verilated::commandArgsPlusMatch("not-matching"), ""); + + const char* argadd[] = {"+testingPlusAdd+2", nullptr}; + Verilated::commandArgsAdd(1, argadd); + TEST_CHECK_CSTR(Verilated::commandArgsPlusMatch("testingPlusAdd"), "+testingPlusAdd+2"); Verilated::assertOn(true); - CHECK_RESULT(Verilated::assertOn(), true); + TEST_CHECK_EQ(Verilated::assertOn(), true); Verilated::calcUnusedSigs(true); - CHECK_RESULT(Verilated::calcUnusedSigs(), true); + TEST_CHECK_EQ(Verilated::calcUnusedSigs(), true); Verilated::debug(9); // Commonly used - CHECK_RESULT(Verilated::debug(), 9); + TEST_CHECK_EQ(Verilated::debug(), 9); Verilated::debug(0); + Verilated::errorLimit(2); + TEST_CHECK_EQ(Verilated::errorLimit(), 2); + + Verilated::fatalOnError(true); + TEST_CHECK_EQ(Verilated::fatalOnError(), true); + Verilated::fatalOnVpiError(true); - CHECK_RESULT(Verilated::fatalOnVpiError(), true); + TEST_CHECK_EQ(Verilated::fatalOnVpiError(), true); + + Verilated::gotError(false); + TEST_CHECK_EQ(Verilated::gotError(), false); Verilated::gotFinish(false); - CHECK_RESULT(Verilated::gotFinish(), false); // Commonly used + TEST_CHECK_EQ(Verilated::gotFinish(), false); // Commonly used Verilated::mkdir(VL_STRINGIFY(TEST_OBJ_DIR) "/mkdired"); Verilated::randReset(0); - CHECK_RESULT(Verilated::randReset(), 0); + TEST_CHECK_EQ(Verilated::randReset(), 0); + + Verilated::randSeed(1234); + TEST_CHECK_EQ(Verilated::randSeed(), 1234); Verilated::traceEverOn(true); // Commonly used - CHECK_RESULT_CSTR(Verilated::productName(), Verilated::productName()); - CHECK_RESULT_CSTR(Verilated::productVersion(), Verilated::productVersion()); + TEST_CHECK_CSTR(Verilated::productName(), Verilated::productName()); + TEST_CHECK_CSTR(Verilated::productVersion(), Verilated::productVersion()); - if (Verilated::timeunit()) {} - if (Verilated::timeprecision()) {} + TEST_CHECK_EQ(Verilated::timeunit(), 12); + TEST_CHECK_EQ(Verilated::timeprecision(), 12); VM_PREFIX* topp = new VM_PREFIX(); @@ -87,10 +90,24 @@ int main(int argc, char** argv, char** env) { VL_PRINTF("Starting\n"); vluint64_t sim_time = 100; - while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { - CHECK_RESULT(VL_TIME_Q(), main_time); - CHECK_RESULT(VL_TIME_D(), main_time); + while ( +#ifdef T_WRAPPER_LEGACY + Verilated::time() +#else + vl_time_stamp64() +#endif + < sim_time + && !Verilated::gotFinish()) { + TEST_CHECK_EQ(VL_TIME_Q(), main_time); + TEST_CHECK_EQ(VL_TIME_D(), main_time); + main_time += 1; +#ifdef T_WRAPPER_LEGACY + Verilated::timeInc(1); + // Check reading and writing of time + Verilated::time(Verilated::time()); +#endif + topp->clk = !topp->clk; topp->eval(); } @@ -104,5 +121,5 @@ int main(int argc, char** argv, char** env) { VL_DO_DANGLING(delete topp, topp); Verilated::runExitCallbacks(); - return got_error ? 10 : 0; + return errors ? 10 : 0; } diff --git a/test_regress/t/t_wrapper_legacy.pl b/test_regress/t/t_wrapper_legacy.pl index 293973bb4..bdcde3470 100755 --- a/test_regress/t/t_wrapper_legacy.pl +++ b/test_regress/t/t_wrapper_legacy.pl @@ -14,6 +14,7 @@ compile( make_top_shell => 0, make_main => 0, verilator_flags2 => ["--exe $Self->{t_dir}/$Self->{name}.cpp"], + make_flags => 'CPPFLAGS_ADD=-DVL_TIME_CONTEXT', ); execute( diff --git a/test_regress/t/t_wrapper_legacy_timed.pl b/test_regress/t/t_wrapper_legacy_timed.pl new file mode 100755 index 000000000..edde6839d --- /dev/null +++ b/test_regress/t/t_wrapper_legacy_timed.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 2020 by Wilson Snyder and Marlon James. 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_all => 1); + +top_filename("t/t_wrapper_legacy.v"); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe $Self->{t_dir}/t_wrapper_legacy.cpp"], + make_flags => 'CPPFLAGS_ADD=-UVL_TIME_CONTEXT', + ); + +execute( + check_finished => 1, + ); + +ok(1); +1;