diff --git a/.clang-format b/.clang-format index ad282b495..251eebe52 100644 --- a/.clang-format +++ b/.clang-format @@ -108,4 +108,3 @@ Standard: Cpp11 TabWidth: 8 UseTab: Never ... - diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2575fb44f..4cced10dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,7 +29,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04] + os: [ubuntu-20.04, ubuntu-18.04] compiler: - { cc: clang, cxx: clang++ } - { cc: gcc, cxx: g++ } @@ -37,11 +37,9 @@ jobs: exclude: # Build pull requests only with ubuntu-20.04 and without m32 - os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }} - - os: ${{ github.event_name == 'pull_request' && 'ubuntu-16.04' || 'do-not-exclude' }} - m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }} # Build -m32 only on ubuntu-20.04 - {os: ubuntu-18.04, m32: 1} - - {os: ubuntu-16.04, m32: 1} runs-on: ${{ matrix.os }} name: Build | ${{ matrix.os }} | ${{ matrix.compiler.cc }} ${{ matrix.m32 && '| -m32' || '' }} env: @@ -92,7 +90,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04] + os: [ubuntu-20.04, ubuntu-18.04] compiler: - { cc: clang, cxx: clang++ } - { cc: gcc, cxx: g++ } @@ -101,11 +99,9 @@ jobs: exclude: # Build pull requests only with ubuntu-20.04 and without m32 - os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }} - - os: ${{ github.event_name == 'pull_request' && 'ubuntu-16.04' || 'do-not-exclude' }} - m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }} # Build -m32 only on ubuntu-20.04 - {os: ubuntu-18.04, m32: 1} - - {os: ubuntu-16.04, m32: 1} runs-on: ${{ matrix.os }} name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.suite }} ${{ matrix.m32 && '| -m32' || '' }} env: diff --git a/Changes b/Changes index ca5a3183b..82fe61b54 100644 --- a/Changes +++ b/Changes @@ -8,6 +8,32 @@ The changes in each Verilator version are described below. The contributors that suggested a given feature are shown in []. Thanks! +Verilator 4.212 2021-09-01 +========================== + +**Minor:** + +* Fix re-evaluation of logic dependent on state set in DPI exports (#3091). [Geza Lore] +* Support unpacked array localparams in tasks/functions (#3078). [Geza Lore] +* Support timeunit/timeprecision in $unit. +* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski] +* Add --instr-count-dpi to tune assumed DPI import cost for multithreaded + model scheduling. Default value changed to 200 (#3068). [Yinan Xu] +* Output files are split based on the set of headers required + in order to aid incremental compilation via ccache (#3071). [Geza Lore] +* Parameter values are now emitted as 'static constexpr' instead of enum. + C++ direct references to parameters might require updating (#3077). [Geza Lore] +* Refactored Verilated include files; include verilated.h not verilated_heavy.h. +* Add header guards on Dpi.h generated files (#2979). [Tood Strader] +* Add XML ccall, constpool, initarray, and if/while begins (#3080). [Steven Hugg] +* Add error when constant function under a generate (#3103). [Don Owen] +* Fix -G to treat simple integer literals as signed (#3060). [Anikin1610] +* Fix emitted string array initializers (#2895). [Iztok Jeras] +* Fix bitop tree optimization dropping necessary & operator (#3096). [Flavien Solt] +* Fix internal error on wide -x-initial unique (#3106). [Alexandre Joannou] +* Fix traces to show array instances with brackets (#3092) (#3095). [Pieter Kapsenberg] + + Verilator 4.210 2021-07-07 ========================== diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP deleted file mode 100644 index 4a964bc40..000000000 --- a/MANIFEST.SKIP +++ /dev/null @@ -1,76 +0,0 @@ -\.ccache/ -\.clang-format -\.clang-tidy -\.git/ -\.git$ -\.github/ -\.svn/ -\.(bak|old)/ -\.(bak|old)$ -\.tar\. -.*\.tgz -.*\.log -\..*\.swp -.*\.tmp -.*\.tidy -.*\.tex -.*\.key -.*\.vcd -.*\.1 -\.codacy\.yml -\.travis\.yml -/_build/ -/build/ -/obj_dbg/ -/obj_dir/ -/obj_dist/ -/obj_iv/ -/obj_ms/ -/obj_nc/ -/obj_opt/ -/obj_vcs/ -/obj_vlt/ -/obj_vltmt/ -INCA_libs/ -/cov_work/ -/logs/ -^Makefile$ -README.html -bin/verilator_bin.* -bin/verilator_coverage_bin.* -docs/.*\.html$ -docs/_build/ -docs/clang-format.txt$ -docs/doxygen-doc/.* -docs/spelling.txt -examples/xml_py/copied/ -examples/xml_py/graph.* -sonar-project.properties -src/Makefile$ -src/Makefile_obj$ -include/verilated.mk$ -test_regress/.gdbinit$ -test_regress/transcript -codecov.yml -config.cache$ -config.status$ -verilator\.log -verilator\.tex -verilator-config.cmake$ -verilator-config-version.cmake$ -verilator.pc$ -verilator_bin.* -verilator_coverage_bin.* -.vcsmx_rebuild$ -ncverilog.history -autom4te\.cache/ -nodist/ -ci/ -/simv$ -/simv.daidir/ -/vc_hdrs.h$ -/csrc/ -obj_dir.* -TAGS -gmon.out -.*~ diff --git a/Makefile.in b/Makefile.in index 38c73b19d..7b09873c8 100644 --- a/Makefile.in +++ b/Makefile.in @@ -118,81 +118,6 @@ INFOS = verilator.html verilator.pdf INFOS_OLD = README README.html README.pdf -# Files that can be generated, but should be up to date for a distribution. -DISTDEP = info Makefile - -DISTFILES1 = $(INFOS) .gitignore \ - *.in *.ac \ - Artistic \ - Changes \ - LICENSE \ - MANIFEST.SKIP \ - README.rst \ - verilator-config.cmake.in \ - verilator-config-version.cmake.in \ - bin/verilator \ - bin/verilator_ccache_report \ - bin/verilator_coverage \ - bin/verilator_difftree \ - bin/verilator_gantt \ - bin/verilator_includer \ - bin/verilator_profcfunc \ - docs/.gitignore \ - docs/CONTRIBUTING.rst \ - docs/CONTRIBUTORS \ - docs/Makefile \ - docs/_static/*.png \ - docs/_static/css/* \ - docs/bin/* \ - docs/gen/* \ - docs/guide/*.py \ - docs/guide/*.rst \ - docs/guide/figures/* \ - docs/install.rst \ - docs/internals.rst \ - docs/internals.rst \ - docs/verilated.dox \ - docs/xml.rst \ - install-sh configure *.pod \ - include/*.[chv]* \ - include/*.in \ - include/.*ignore \ - include/gtkwave/*.[chv]* \ - include/vltstd/*.[chv]* \ - .*attributes */.*attributes */*/.*attributes \ - src/.*ignore src/*.in src/*.cpp src/*.[chly] \ - src/astgen src/bisonpre src/*fix src/cppcheck_filtered \ - src/config_rev \ - src/vlcovgen src/mkinstalldirs \ - src/.gdbinit \ - src/*.pl src/*.pod \ - examples/*/.*ignore examples/*/Makefile* \ - examples/*/*.[chv]* examples/*/vl_* \ - examples/*/CMakeLists.txt \ - -DISTFILES2 = \ - test_*/.*ignore test_*/Makefile* test_*/*.cpp \ - test_*/*.pl test_*/*.v test_*/*.vc test_*/*.vh \ - test_regress/*.pl \ - test_regress/Makefile \ - test_regress/Makefile_obj \ - test_regress/input.vc \ - test_regress/CMakeLists.txt \ - test_regress/t/t*/*.sv* \ - test_regress/t/t*/*.v* \ - test_regress/t/t*/*/*.sv* \ - test_regress/t/t*/*/*.v* \ - test_regress/t/t*/*.cpp \ - test_regress/t/t*/CMakeLists.txt \ - test_regress/t/*.cpp \ - test_regress/t/*.h \ - test_regress/t/*.dat \ - test_regress/t/*.mem \ - test_regress/t/*.out \ - test_regress/t/*.pl \ - test_regress/t/*.pf \ - test_regress/t/*.v* \ - INST_PROJ_FILES = \ bin/verilator \ bin/verilator_ccache_report \ @@ -386,44 +311,7 @@ uninstall: install: all_nomsg install-all install-all: installbin installman installdata install-msg -install-here: installman ftp - -ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users -DISTNAMEREV = $(shell sed -e '/DTVERSION/!d' -e 's/.*verilator_\([^"]*\).*/\1/' -e q ${srcdir}/src/config_rev.h) - -VERILATOR_CAD_DIR = $(CAD_DIR)/verilator/$(DISTNAMEREV)/$(DIRPROJECT_ARCH) -INST_PROJ_CVS = cp_if_cvs_diff - -install-cadtools: dist - @echo "Install-project to $(CAD_DIR)" - strip bin/verilator_bin* - strip bin/verilator_coverage_bin* - $(MAKE) install-cadtools-quick - $(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/man/man1 - for p in $(VL_INST_MAN_FILES) ; do \ - $(INSTALL_DATA) $$p $(VERILATOR_CAD_DIR)/man/man1/$$p; \ - done - $(INST_PROJ_CVS) $(DISTNAME).tgz $(VERILATOR_CAD_DIR)/verilator.tgz - rm $(DISTNAME).tgz - -install-cadtools-quick: -ifeq ($(CFG_WITH_DEFENV),yes) - @echo "%Error: Reconfigure with './configure --disable-defenv' to avoid hardcoded paths." - false -endif - @echo "Install-cadtools-quick (no strip) to $(VERILATOR_CAD_DIR)" - $(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/include/gtkwave - $(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/include/vltstd - $(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/bin - for p in $(INST_PROJ_FILES) ; do \ - $(INST_PROJ_CVS) $$p $(VERILATOR_CAD_DIR)/$$p; \ - done - for p in $(INST_PROJ_BIN_FILES) ; do \ - $(INST_PROJ_CVS) $$p $(VERILATOR_CAD_DIR)/$$p; \ - done - -# VERILATOR_AUTHOR_SITE -endif +install-here: installman info # Use --xml flag to see the cppcheck code to use for suppression CPPCHECK_CPP = $(wildcard \ @@ -524,9 +412,6 @@ lint-py: format-pl-exec: -chmod a+x test_regress/t/*.pl - -ftp: info - install-msg: @echo @echo "Installed binaries to $(DESTDIR)$(bindir)/verilator" @@ -597,15 +482,6 @@ TAGS: $(TAGFILES) doxygen: $(MAKE) -C docs doxygen -###################################################################### -# Test targets - -dist-file-list: - @echo "begin-dist-file-list:"; # Scripts look for this - @echo $(wildcard $(DISTFILES1)) - @echo $(wildcard $(DISTFILES2)) - @echo "end-dist-file-list:"; # Scripts look for this - ###################################################################### # Distributions @@ -613,36 +489,16 @@ DISTTITLE := Verilator $(word 1,$(PACKAGE_VERSION)) DISTNAME := verilator-$(word 1,$(PACKAGE_VERSION)) DISTDATEPRE := $(word 2,$(PACKAGE_VERSION)) DISTDATE := $(subst /,-,$(DISTDATEPRE)) - DISTTAGNAME := $(subst .,_,$(subst -,_,$(DISTNAME))) tag: svnorcvs tag $(DISTTAGNAME) -# Don't depend on DISTFILES because there's no rule for "standards.info*". -dist: $(DISTDEP) maintainer-copy - -rm -fr $(DISTNAME) - for file in $(DISTFILES1); do \ - mkdir -p `dirname $(DISTNAME)/$$file` >/dev/null ; \ - ln $$file $(DISTNAME)/$$file \ - || { echo copying $$file instead; cp -p $$file $(DISTNAME)/$$file;}; \ - done; true; - for file in $(DISTFILES2); do \ - mkdir -p `dirname $(DISTNAME)/$$file` >/dev/null ; \ - ln $$file $(DISTNAME)/$$file \ - || { echo copying $$file instead; cp -p $$file $(DISTNAME)/$$file;}; \ - done; true; - chmod -R a+r $(DISTNAME) - tar chf $(DISTNAME).tar $(DISTNAME) - gzip --force --best $(DISTNAME).tar - mv $(DISTNAME).tar.gz $(DISTNAME).tgz - rm -fr $(DISTNAME) - maintainer-diff: svnorcvs diff $(DISTTAGNAME) preexist: svnorcvs nexists $(DISTTAGNAME) -maintainer-dist: preexist dist tag - svnorcvs release $(DISTNAME).tgz +maintainer-dist: preexist tag + svnorcvs release $(DISTTAGNAME) diff --git a/README.rst b/README.rst index d13cd58ba..033567427 100644 --- a/README.rst +++ b/README.rst @@ -5,6 +5,8 @@ :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://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages + :target: https://repology.org/project/verilator/versions .. 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 @@ -12,6 +14,7 @@ .. image:: https://github.com/verilator/verilator/workflows/build/badge.svg :target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild + Welcome to Verilator ==================== diff --git a/bin/verilator b/bin/verilator index b8d272440..0a9ceaf71 100755 --- a/bin/verilator +++ b/bin/verilator @@ -333,6 +333,7 @@ detailed descriptions of these arguments. --if-depth Tune IFDEPTH warning +incdir+ Directory to search for includes --inline-mult Tune module inlining + --instr-count-dpi Assumed dynamic instruction count of DPI imports -LDFLAGS Linker pre-object arguments for makefile --l2-name Verilog scope name of the top module --language Default language standard to parse diff --git a/configure.ac b/configure.ac index 174afb803..720f03e4c 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.210 2021-07-07], +AC_INIT([Verilator],[4.212 2021-09-01], [https://verilator.org], [verilator],[https://verilator.org]) # When releasing, also update header of Changes file @@ -346,14 +346,9 @@ AC_SUBST(CFG_CXXFLAGS_PROFILE) # Flag to select newest language standard supported # Macros work such that first option that passes is the one we take -# Currently enabled gnu++14/c++14 due to packaged SystemC dependency -# gnu++17 is the newest that Verilator supports -# std++03 is the oldest that Verilator supports -#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20) -#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17) -_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++14) -_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++11) -_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++03) +# Currently enabled c++14 due to packaged SystemC dependency +# c++17 is the newest that Verilator supports +# c++03 is the oldest that Verilator supports #_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20) #_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17) _MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++14) @@ -367,11 +362,6 @@ _MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++11) _MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++14) #_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++17) #_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++20) -_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++03) -_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++11) -_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++14) -#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++17) -#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++20) AC_SUBST(CFG_CXXFLAGS_STD_OLDEST) # Flags for compiling Verilator internals including parser, and Verilated files diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 47c79e2f9..bead98916 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -30,6 +30,7 @@ Harald Heckmann Howard Su Huang Rui HyungKi Jeong +Ivan Vnučec Iztok Jeras James Hanlon James Hutchinson @@ -83,6 +84,7 @@ Sebastien Van Cauwenberghe Sergi Granell Stefan Wallentowitz Stephen Henry +Steven Hugg Tim Snyder Tobias Rosenkranz Tobias Wölfel diff --git a/docs/guide/contributors.rst b/docs/guide/contributors.rst index c38cb8515..849fa795b 100644 --- a/docs/guide/contributors.rst +++ b/docs/guide/contributors.rst @@ -13,7 +13,8 @@ When possible, please instead report bugs at `Verilator Issues Primary author is Wilson Snyder . -Major concepts by Paul Wasson, Duane Galbi, John Coiner and Jie Xu. +Major concepts by Paul Wasson, Duane Galbi, John Coiner, Geza Lore, Yutetsu +Takatsukasa, and Jie Xu. Contributors diff --git a/docs/guide/deprecations.rst b/docs/guide/deprecations.rst index aa9fcb5df..8d3124175 100644 --- a/docs/guide/deprecations.rst +++ b/docs/guide/deprecations.rst @@ -11,6 +11,11 @@ C++11 compiler support require C++14 or newer compilers for both compiling Verilator and compiling Verilated models no sooner than January 2022. +Verilated_heavy.h + The legacy "verilated_heavy.h" include was replaced with just including + "verilated.h". Verilated_heavy.h is planned for removal no sooner than + July 2022. + Configuration File -msg The :vlopt:`lint_off` "-msg" option has been replaced with the "-rule" option. "-msg" is planned for removal no sooner than January 2021. diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index f92024741..c12214f0f 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -536,6 +536,14 @@ Summary: times, but potentially faster simulation speed. This setting is ignored for very small modules; they will always be inlined, if allowed. +.. option:: --instr-count-dpi + + Assumed dynamic instruction count of the average DPI import. This is used + by the partitioning algorithm when creating a multithread model. The + default value is 200. Adjusting this to an appropriate value can yield + performance improvements in multithreaded models. Ignored when creating a + single threaded model. + .. option:: -j [] Specify the level of parallelism for :vlopt:`--build`. The must @@ -1044,6 +1052,8 @@ Summary: Verilator assumes DPI pure imports are threadsafe, but non-pure DPI imports are not. + See also :vlopt:`--instr-count-dpi` option. + .. option:: --threads-max-mtasks Rarely needed. When using :vlopt:`--threads`, specify the number of diff --git a/docs/guide/install.rst b/docs/guide/install.rst index 5f2c50b12..5b95f23e4 100644 --- a/docs/guide/install.rst +++ b/docs/guide/install.rst @@ -23,6 +23,8 @@ package: apt-get install verilator # On Ubuntu +For other distributions refer to `Repology Verilator Distro Packages +`__. .. _Git Install: @@ -44,7 +46,6 @@ In brief, to install from git: #sudo apt-get install zlibc zlib1g zlib1g-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 diff --git a/docs/guide/verilating.rst b/docs/guide/verilating.rst index d1231a314..b33dd4acd 100644 --- a/docs/guide/verilating.rst +++ b/docs/guide/verilating.rst @@ -216,6 +216,12 @@ be done by a "main thread". In most cases the eval thread and main thread are the same thread (i.e. the user's top C++ testbench runs on a single thread), but this is not required. +When making frequent use of DPI imported functions in a multi-threaded +model, it may be beneficial to performance to adjust the +:vlopt:`--instr-count-dpi` option based on some experimentation. This +influences the partitioning of the model by adjusting the assumed execution +time of DPI imports. + The :vlopt:`--trace-threads` options can be used to produce trace dumps using multiple threads. If :vlopt:`--trace-threads` is set without :vlopt:`--threads`, then :vlopt:`--trace-threads` will imply diff --git a/include/gtkwave/fstapi.c b/include/gtkwave/fstapi.c index b4c9823d2..2e28e64ac 100644 --- a/include/gtkwave/fstapi.c +++ b/include/gtkwave/fstapi.c @@ -4989,9 +4989,7 @@ unsigned int secnum = 0; int blocks_skipped = 0; fst_off_t blkpos = 0; uint64_t seclen, beg_tim; -#ifdef FST_DEBUG uint64_t end_tim; -#endif uint64_t frame_uclen, frame_clen, frame_maxhandle, vc_maxhandle; fst_off_t vc_start; fst_off_t indx_pntr, indx_pos; @@ -5058,14 +5056,11 @@ for(;;) if(!seclen) break; beg_tim = fstReaderUint64(xc->f); -#ifdef FST_DEBUG - end_tim = -#endif - fstReaderUint64(xc->f); + end_tim = fstReaderUint64(xc->f); if(xc->limit_range_valid) { - if(beg_tim < xc->limit_range_start) + if(end_tim < xc->limit_range_start) { blocks_skipped++; blkpos += seclen; diff --git a/include/verilated.cpp b/include/verilated.cpp index 01d31413f..f86f04852 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -173,9 +173,9 @@ void vl_stop_maybe(const char* filename, int linenum, const char* hier, bool may void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE { #ifdef VL_THREADED - VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { // + VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { // vl_finish(filename, linenum, hier); - })); + }}); #else vl_finish(filename, linenum, hier); #endif @@ -183,9 +183,9 @@ void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAF void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_SAFE { #ifdef VL_THREADED - VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { // + VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { // vl_stop_maybe(filename, linenum, hier, maybe); - })); + }}); #else vl_stop_maybe(filename, linenum, hier, maybe); #endif @@ -193,9 +193,9 @@ void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE { #ifdef VL_THREADED - VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { // + VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { // vl_fatal(filename, linenum, hier, msg); - })); + }}); #else vl_fatal(filename, linenum, hier, msg); #endif @@ -208,14 +208,14 @@ void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE { va_list aq; va_copy(aq, ap); - int len = VL_VSNPRINTF(nullptr, 0, formatp, aq); + size_t len = VL_VSNPRINTF(nullptr, 0, formatp, aq); va_end(aq); if (VL_UNLIKELY(len < 1)) return ""; - char* bufp = new char[len + 1]; + char* const bufp = new char[len + 1]; VL_VSNPRINTF(bufp, len + 1, formatp, ap); - const std::string out = std::string(bufp, len); + std::string out{bufp, len}; // Not const to allow move optimization delete[] bufp; return out; } @@ -263,9 +263,9 @@ void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE { va_start(ap, formatp); const std::string out = _vl_string_vprintf(formatp, ap); va_end(ap); - VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { // + VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { // VL_PRINTF("%s", out.c_str()); - })); + }}); } #endif @@ -276,7 +276,7 @@ static vluint32_t vl_sys_rand32() VL_MT_UNSAFE { // Return random 32-bits using system library. // Used only to construct seed for Verilator's PNRG. static VerilatedMutex s_mutex; - const VerilatedLockGuard lock(s_mutex); // Otherwise rand is unsafe + const VerilatedLockGuard lock{s_mutex}; // Otherwise rand is unsafe #if defined(_WIN32) && !defined(__CYGWIN__) // Windows doesn't have lrand48(), although Cygwin does. return (std::rand() << 16) ^ std::rand(); @@ -742,7 +742,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA } else if (!inPct) { // Normal text // Fast-forward to next escape and add to output const char* ep = pos; - while (ep[0] && ep[0] != '%') ep++; + while (ep[0] && ep[0] != '%') ++ep; if (ep != pos) { output.append(pos, ep - pos); pos += ep - pos - 1; @@ -776,7 +776,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA output += '%'; break; case 'N': { // "C" string with name of module, add . if needed - const char* cstrp = va_arg(ap, const char*); + const char* const cstrp = va_arg(ap, const char*); if (VL_LIKELY(*cstrp)) { output += cstrp; output += '.'; @@ -784,13 +784,13 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA break; } case 'S': { // "C" string - const char* cstrp = va_arg(ap, const char*); + const char* const cstrp = va_arg(ap, const char*); output += cstrp; break; } case '@': { // Verilog/C++ string va_arg(ap, int); // # bits is ignored - const std::string* cstrp = va_arg(ap, const std::string*); + const std::string* const cstrp = va_arg(ap, const std::string*); std::string padding; if (width > cstrp->size()) padding.append(width - cstrp->size(), ' '); output += left ? (*cstrp + padding) : (padding + *cstrp); @@ -808,7 +808,8 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA const int timeunit = va_arg(ap, int); output += _vl_vsformat_time(t_tmp, d, timeunit, left, width); } else { - std::string fmts(pctp, pos - pctp + 1); + const size_t len = pos - pctp + 1; + std::string fmts{pctp, len}; VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, fmts.c_str(), d); output += t_tmp; } @@ -862,7 +863,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA if (VL_SIGN_E(lbits, lwp[VL_WORDS_I(lbits) - 1])) { VlWide neg; VL_NEGATE_W(VL_WORDS_I(lbits), neg, lwp); - append = std::string("-") + VL_DECIMAL_NW(lbits, neg); + append = std::string{"-"} + VL_DECIMAL_NW(lbits, neg); } else { append = VL_DECIMAL_NW(lbits, lwp); } @@ -957,7 +958,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA } break; default: { // LCOV_EXCL_START - const std::string msg = std::string("Unknown _vl_vsformat code: ") + pos[0]; + const std::string msg = std::string{"Unknown _vl_vsformat code: "} + pos[0]; VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); break; } // LCOV_EXCL_STOP @@ -969,14 +970,14 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA } static inline bool _vl_vsss_eof(FILE* fp, int floc) VL_MT_SAFE { - if (fp) { + if (VL_LIKELY(fp)) { return std::feof(fp) ? true : false; // true : false to prevent MSVC++ warning } else { return floc < 0; } } static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE { - if (fp) { + if (VL_LIKELY(fp)) { std::fgetc(fp); } else { floc -= 8; @@ -985,7 +986,7 @@ static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE { static inline int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp, const std::string& fstr) VL_MT_SAFE { // Get a character without advancing - if (fp) { + if (VL_LIKELY(fp)) { const int data = std::fgetc(fp); if (data == EOF) return EOF; ungetc(data, fp); @@ -1217,7 +1218,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf case 'u': { // Read packed 2-value binary data const int bytes = VL_BYTES_I(obits); - char* out = reinterpret_cast(owp); + char* const out = reinterpret_cast(owp); if (!_vl_vsss_read_bin(fp, floc, fromp, fstr, out, bytes)) goto done; const int last = bytes % 4; if (last != 0 @@ -1242,7 +1243,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf break; } default: { // LCOV_EXCL_START - const std::string msg = std::string("Unknown _vl_vsscanf code: ") + pos[0]; + const std::string msg = std::string{"Unknown _vl_vsscanf code: "} + pos[0]; VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); break; } // LCOV_EXCL_STOP @@ -1253,19 +1254,19 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf // Reload data if non-wide (if wide, we put it in the right place directly) if (obits == 0) { // Due to inIgnore } else if (obits == -1) { // string - std::string* p = va_arg(ap, std::string*); + std::string* const p = va_arg(ap, std::string*); *p = t_tmp; } else if (obits <= VL_BYTESIZE) { - CData* p = va_arg(ap, CData*); + CData* const p = va_arg(ap, CData*); *p = owp[0]; } else if (obits <= VL_SHORTSIZE) { - SData* p = va_arg(ap, SData*); + SData* const p = va_arg(ap, SData*); *p = owp[0]; } else if (obits <= VL_IDATASIZE) { - IData* p = va_arg(ap, IData*); + IData* const p = va_arg(ap, IData*); *p = owp[0]; } else if (obits <= VL_QUADSIZE) { - QData* p = va_arg(ap, QData*); + QData* const p = va_arg(ap, QData*); *p = VL_SET_QW(owp); } } @@ -1337,7 +1338,7 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE { if (VL_UNLIKELY(str.empty())) return 0; - // V3Emit has static check that bytes < VL_TO_STRING_MAX_WORDS, but be safe + // V3Emit has static check that bytes < VL_VALUE_STRING_MAX_WORDS, but be safe if (VL_UNCOVERABLE(bytes < str.size())) { VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: fgets buffer overrun"); // LCOV_EXCL_LINE } @@ -1346,7 +1347,6 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE { return got; } -// declared in verilated_heavy.h IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE { return getLine(dest, fpi, std::numeric_limits::max()); } @@ -1354,7 +1354,7 @@ IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE { IData VL_FERROR_IN(IData, std::string& outputr) VL_MT_SAFE { // We ignore lhs/fpi - IEEE says "most recent error" so probably good enough const IData ret = errno; - outputr = std::string(::std::strerror(ret)); + outputr = std::string{::std::strerror(ret)}; return ret; } @@ -1541,19 +1541,19 @@ IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi // Shift value in IData entry = read_elements + start - array_lsb; if (width <= 8) { - CData* datap = &(reinterpret_cast(memp))[entry]; + CData* const datap = &(reinterpret_cast(memp))[entry]; if (shift == start_shift) *datap = 0; *datap |= (c << shift) & VL_MASK_I(width); } else if (width <= 16) { - SData* datap = &(reinterpret_cast(memp))[entry]; + SData* const datap = &(reinterpret_cast(memp))[entry]; if (shift == start_shift) *datap = 0; *datap |= (c << shift) & VL_MASK_I(width); } else if (width <= VL_IDATASIZE) { - IData* datap = &(reinterpret_cast(memp))[entry]; + IData* const datap = &(reinterpret_cast(memp))[entry]; if (shift == start_shift) *datap = 0; *datap |= (c << shift) & VL_MASK_I(width); } else if (width <= VL_QUADSIZE) { - QData* datap = &(reinterpret_cast(memp))[entry]; + QData* const datap = &(reinterpret_cast(memp))[entry]; if (shift == start_shift) *datap = 0; *datap |= ((static_cast(c) << static_cast(shift)) & VL_MASK_Q(width)); } else { @@ -1579,7 +1579,7 @@ IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE { return VL_SYSTEM_IW(VL_WQ_WORDS_E, lhsw); } IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE { - char filenamez[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1]; + char filenamez[VL_VALUE_STRING_MAX_CHARS + 1]; _vl_vint_to_string(lhswords * VL_EDATASIZE, filenamez, lhsp); const int code = std::system(filenamez); // Yes, std::system() is threadsafe return code >> 8; // Want exit status @@ -1615,7 +1615,7 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M } const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str()); - const char* dp = match.c_str() + 1 /*leading + */ + prefix.length(); + const char* const dp = match.c_str() + 1 /*leading + */ + prefix.length(); if (match.empty()) return 0; VL_ZERO_RESET_W(rbits, rwp); @@ -1684,9 +1684,9 @@ IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_S } } const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str()); - const char* dp = match.c_str() + 1 /*leading + */ + prefix.length(); + const char* const dp = match.c_str() + 1 /*leading + */ + prefix.length(); if (match.empty()) return 0; - rdr = std::string(dp); + rdr = std::string{dp}; return 1; } @@ -1726,22 +1726,22 @@ std::string VL_TOUPPER_NN(const std::string& ld) VL_MT_SAFE { std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE { // See also _vl_vint_to_string - char destout[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1]; + char destout[VL_VALUE_STRING_MAX_CHARS + 1]; int obits = lwords * VL_EDATASIZE; int lsb = obits - 1; bool start = true; char* destp = destout; - int len = 0; + size_t len = 0; for (; lsb >= 0; --lsb) { lsb = (lsb / 8) * 8; // Next digit IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xff; if (!start || charval) { *destp++ = (charval == 0) ? ' ' : charval; - len++; + ++len; start = false; // Drop leading 0s } } - return std::string(destout, len); + return std::string{destout, len}; } std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE { @@ -1924,19 +1924,19 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) { const int value = (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0')); if (m_bits <= 8) { - CData* datap = reinterpret_cast(valuep); + CData* const datap = reinterpret_cast(valuep); if (!innum) *datap = 0; *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits); } else if (m_bits <= 16) { - SData* datap = reinterpret_cast(valuep); + SData* const datap = reinterpret_cast(valuep); if (!innum) *datap = 0; *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits); } else if (m_bits <= VL_IDATASIZE) { - IData* datap = reinterpret_cast(valuep); + IData* const datap = reinterpret_cast(valuep); if (!innum) *datap = 0; *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits); } else if (m_bits <= VL_QUADSIZE) { - QData* datap = reinterpret_cast(valuep); + QData* const datap = reinterpret_cast(valuep); if (!innum) *datap = 0; *datap = ((*datap << static_cast(shift)) + static_cast(value)) & VL_MASK_Q(m_bits); @@ -1979,7 +1979,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) { } m_addr = addr + 1; if (m_bits <= 8) { - const CData* datap = reinterpret_cast(valuep); + const CData* const datap = reinterpret_cast(valuep); if (m_hex) { fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap); fprintf(m_fp, "\n"); @@ -1987,7 +1987,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) { fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap)); } } else if (m_bits <= 16) { - const SData* datap = reinterpret_cast(valuep); + const SData* const datap = reinterpret_cast(valuep); if (m_hex) { fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap); fprintf(m_fp, "\n"); @@ -1995,7 +1995,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) { fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap)); } } else if (m_bits <= 32) { - const IData* datap = reinterpret_cast(valuep); + const IData* const datap = reinterpret_cast(valuep); if (m_hex) { fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap); fprintf(m_fp, "\n"); @@ -2003,7 +2003,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) { fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap)); } } else if (m_bits <= 64) { - const QData* datap = reinterpret_cast(valuep); + const QData* const datap = reinterpret_cast(valuep); const vluint64_t value = VL_MASK_Q(m_bits) & *datap; const vluint32_t lo = value & 0xffffffff; const vluint32_t hi = value >> 32; @@ -2038,7 +2038,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) { fprintf(m_fp, "%s", formatBinary(32, data)); } } - word_idx--; + --word_idx; first = false; } fprintf(m_fp, "\n"); @@ -2057,7 +2057,7 @@ void VL_READMEM_N(bool hex, // Hex format, else binary ) VL_MT_SAFE { if (start < static_cast(array_lsb)) start = array_lsb; - VlReadMem rmem(hex, bits, filename, start, end); + VlReadMem rmem{hex, bits, filename, start, end}; if (VL_UNLIKELY(!rmem.isOpen())) return; while (true) { QData addr = 0; @@ -2070,16 +2070,16 @@ void VL_READMEM_N(bool hex, // Hex format, else binary } else { QData entry = addr - array_lsb; if (bits <= 8) { - CData* datap = &(reinterpret_cast(memp))[entry]; + CData* const datap = &(reinterpret_cast(memp))[entry]; rmem.setData(datap, value); } else if (bits <= 16) { - SData* datap = &(reinterpret_cast(memp))[entry]; + SData* const datap = &(reinterpret_cast(memp))[entry]; rmem.setData(datap, value); } else if (bits <= VL_IDATASIZE) { - IData* datap = &(reinterpret_cast(memp))[entry]; + IData* const datap = &(reinterpret_cast(memp))[entry]; rmem.setData(datap, value); } else if (bits <= VL_QUADSIZE) { - QData* datap = &(reinterpret_cast(memp))[entry]; + QData* const datap = &(reinterpret_cast(memp))[entry]; rmem.setData(datap, value); } else { WDataOutP datap @@ -2107,22 +2107,22 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary if (start < static_cast(array_lsb)) start = array_lsb; if (end > addr_max) end = addr_max; - VlWriteMem wmem(hex, bits, filename, start, end); + VlWriteMem wmem{hex, bits, filename, start, end}; if (VL_UNLIKELY(!wmem.isOpen())) return; for (QData addr = start; addr <= end; ++addr) { const QData row_offset = addr - array_lsb; if (bits <= 8) { - const CData* datap = &(reinterpret_cast(memp))[row_offset]; + const CData* const datap = &(reinterpret_cast(memp))[row_offset]; wmem.print(addr, false, datap); } else if (bits <= 16) { - const SData* datap = &(reinterpret_cast(memp))[row_offset]; + const SData* const datap = &(reinterpret_cast(memp))[row_offset]; wmem.print(addr, false, datap); } else if (bits <= 32) { - const IData* datap = &(reinterpret_cast(memp))[row_offset]; + const IData* const datap = &(reinterpret_cast(memp))[row_offset]; wmem.print(addr, false, datap); } else if (bits <= 64) { - const QData* datap = &(reinterpret_cast(memp))[row_offset]; + const QData* const datap = &(reinterpret_cast(memp))[row_offset]; wmem.print(addr, false, datap); } else { const WDataInP memDatap = reinterpret_cast(memp); @@ -2142,8 +2142,8 @@ int VL_TIME_STR_CONVERT(const char* strp) VL_PURE { if (!strp) return 0; if (*strp++ != '1') return 0; while (*strp == '0') { - scale++; - strp++; + ++scale; + ++strp; } switch (*strp++) { case 's': break; @@ -2259,7 +2259,7 @@ VerilatedContext::VerilatedContext() Verilated::threadContextp(this); m_ns.m_profThreadsFilename = "profile_threads.dat"; m_fdps.resize(31); - std::fill(m_fdps.begin(), m_fdps.end(), (FILE*)0); + std::fill(m_fdps.begin(), m_fdps.end(), static_cast(nullptr)); m_fdFreeMct.resize(30); for (std::size_t i = 0, id = 1; i < m_fdFreeMct.size(); ++i, ++id) m_fdFreeMct[i] = id; } @@ -2273,19 +2273,19 @@ VerilatedContext::Serialized::Serialized() { } void VerilatedContext::assertOn(bool flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_assertOn = flag; } void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + 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); + const VerilatedLockGuard lock{m_timeDumpMutex}; m_dumpfile = flag; } std::string VerilatedContext::dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { - const VerilatedLockGuard lock(m_timeDumpMutex); + const VerilatedLockGuard lock{m_timeDumpMutex}; return m_dumpfile; } std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { @@ -2297,61 +2297,61 @@ std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDu return out; } void VerilatedContext::errorCount(int val) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_errorCount = val; } void VerilatedContext::errorCountInc() VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; ++m_s.m_errorCount; } void VerilatedContext::errorLimit(int val) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_errorLimit = val; } void VerilatedContext::fatalOnError(bool flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_fatalOnError = flag; } void VerilatedContext::fatalOnVpiError(bool flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_fatalOnVpiError = flag; } void VerilatedContext::gotError(bool flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_gotError = flag; } void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_gotFinish = flag; } void VerilatedContext::profThreadsStart(vluint64_t flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_ns.m_profThreadsStart = flag; } void VerilatedContext::profThreadsWindow(vluint64_t flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_ns.m_profThreadsWindow = flag; } void VerilatedContext::profThreadsFilename(const std::string& flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_ns.m_profThreadsFilename = flag; } std::string VerilatedContext::profThreadsFilename() const VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; return m_ns.m_profThreadsFilename; } void VerilatedContext::randReset(int val) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + 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(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_timeunit = value; } void VerilatedContext::timeprecision(int value) VL_MT_SAFE { if (value < 0) value = -value; // Stored as 0..15 - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_timeprecision = value; #ifdef SYSTEMC_VERSION const sc_time sc_res = sc_get_time_resolution(); @@ -2387,13 +2387,13 @@ const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE { } void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) { - const VerilatedLockGuard lock(m_argMutex); + const VerilatedLockGuard lock{m_argMutex}; m_args.m_argVec.clear(); // Empty first, then add impp()->commandArgsAddGuts(argc, argv); } void VerilatedContext::commandArgsAdd(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) { - const VerilatedLockGuard lock(m_argMutex); + const VerilatedLockGuard lock{m_argMutex}; impp()->commandArgsAddGuts(argc, argv); } const char* VerilatedContext::commandArgsPlusMatch(const char* prefixp) @@ -2422,20 +2422,20 @@ void VerilatedContext::internalsDump() const VL_MT_SAFE { 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]); + m_args.m_argVec.emplace_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); + 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); + const VerilatedLockGuard lock{m_argMutex}; // Note prefixp does not include the leading "+" const size_t len = std::strlen(prefixp); if (VL_UNLIKELY(!m_args.m_argVecLoaded)) { @@ -2454,7 +2454,7 @@ std::string VerilatedContextImp::argPlusMatch(const char* prefixp) // 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); + const VerilatedLockGuard lock{m_argMutex}; static bool s_loaded = false; static int s_argc = 0; static char** s_argvp = nullptr; @@ -2529,7 +2529,7 @@ bool VerilatedContextImp::commandArgVlValue(const std::string& arg, const std::s 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); + const VerilatedLockGuard lock{VerilatedContextImp::s().s_randMutex}; m_s.m_randSeed = val; const vluint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1; // Obververs must see new epoch AFTER seed updated @@ -2552,10 +2552,10 @@ vluint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE { // VerilatedContext:: Methods - scopes void VerilatedContext::scopesDump() const VL_MT_SAFE { - const VerilatedLockGuard lock(m_impdatap->m_nameMutex); + 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; + const VerilatedScope* const scopep = i.second; scopep->scopeDump(); } VL_PRINTF_MT("\n"); @@ -2563,20 +2563,20 @@ void VerilatedContext::scopesDump() const VL_MT_SAFE { void VerilatedContextImp::scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE { // Slow ok - called once/scope at construction - const VerilatedLockGuard lock(m_impdatap->m_nameMutex); + 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); + 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); + 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; @@ -2670,11 +2670,11 @@ static void runCallbacks(const VoidPCbList& cbs) VL_MT_SAFE { } void Verilated::addFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE { - const VerilatedLockGuard lock(VlCbStatic.s_flushMutex); + 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(VlCbStatic.s_flushMutex); + const VerilatedLockGuard lock{VlCbStatic.s_flushMutex}; removeCb(cb, datap, VlCbStatic.s_flushCbs); } void Verilated::runFlushCallbacks() VL_MT_SAFE { @@ -2685,7 +2685,7 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE { static int s_recursing = 0; #endif if (!s_recursing++) { - const VerilatedLockGuard lock(VlCbStatic.s_flushMutex); + const VerilatedLockGuard lock{VlCbStatic.s_flushMutex}; runCallbacks(VlCbStatic.s_flushCbs); } --s_recursing; @@ -2698,11 +2698,11 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE { } void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE { - const VerilatedLockGuard lock(VlCbStatic.s_exitMutex); + 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(VlCbStatic.s_exitMutex); + const VerilatedLockGuard lock{VlCbStatic.s_exitMutex}; removeCb(cb, datap, VlCbStatic.s_exitCbs); } void Verilated::runExitCallbacks() VL_MT_SAFE { @@ -2712,7 +2712,7 @@ void Verilated::runExitCallbacks() VL_MT_SAFE { static int s_recursing = 0; #endif if (!s_recursing++) { - const VerilatedLockGuard lock(VlCbStatic.s_exitMutex); + const VerilatedLockGuard lock{VlCbStatic.s_exitMutex}; runCallbacks(VlCbStatic.s_exitCbs); } --s_recursing; @@ -2729,7 +2729,7 @@ void Verilated::nullPointerError(const char* filename, int linenum) VL_MT_SAFE { void Verilated::overWidthError(const char* signame) VL_MT_SAFE { // Slowpath - Called only when signal sets too high of a bit - const std::string msg = (std::string("Testbench C set input '") + signame + const std::string msg = (std::string{"Testbench C set input '"} + signame + "' to value that overflows what the signal's width can fit"); VL_FATAL_MT("unknown", 0, "", msg.c_str()); VL_UNREACHABLE @@ -2846,7 +2846,7 @@ void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const m_type = type; m_timeunit = timeunit; { - char* namep = new char[std::strlen(prefixp) + std::strlen(suffixp) + 2]; + char* const namep = new char[std::strlen(prefixp) + std::strlen(suffixp) + 2]; char* dp = namep; for (const char* sp = prefixp; *sp;) *dp++ = *sp++; if (*prefixp && *suffixp) *dp++ = '.'; @@ -2886,7 +2886,7 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, boo // statically construct from that. if (!finalize) return; - if (!m_varsp) m_varsp = new VerilatedVarNameMap(); + if (!m_varsp) m_varsp = new VerilatedVarNameMap; VerilatedVar var(namep, datap, vltype, static_cast(vlflags), dims, isParam); va_list ap; @@ -2903,9 +2903,9 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, boo } else { // We could have a linked list of ranges, but really this whole thing needs // to be generalized to support structs and unions, etc. - VL_FATAL_MT( - __FILE__, __LINE__, "", - (std::string("Unsupported multi-dimensional public varInsert: ") + namep).c_str()); + std::string msg + = std::string{"Unsupported multi-dimensional public varInsert: "} + namep; + VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); } } va_end(ap); @@ -2925,7 +2925,7 @@ VerilatedVar* VerilatedScope::varFind(const char* namep) const VL_MT_SAFE_POSTIN void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE { // Slowpath - Called only when find has failed const std::string msg - = (std::string("Testbench C called '") + VerilatedImp::exportName(funcnum) + = (std::string{"Testbench C called '"} + VerilatedImp::exportName(funcnum) + "' but scope wasn't set, perhaps due to dpi import call without " + "'context', or missing svSetScope. See IEEE 1800-2017 35.5.3."); VL_FATAL_MT("unknown", 0, "", msg.c_str()); @@ -2935,7 +2935,7 @@ void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE { void* VerilatedScope::exportFindError(int funcnum) const { // Slowpath - Called only when find has failed const std::string msg - = (std::string("Testbench C called '") + VerilatedImp::exportName(funcnum) + = (std::string{"Testbench C called '"} + VerilatedImp::exportName(funcnum) + "' but this DPI export function exists only in other scopes, not scope '" + name() + "'"); VL_FATAL_MT("unknown", 0, "", msg.c_str()); @@ -2950,7 +2950,7 @@ void VerilatedScope::scopeDump() const { VerilatedImp::exportName(i)); } } - if (VerilatedVarNameMap* varsp = this->varsp()) { + if (VerilatedVarNameMap* const varsp = this->varsp()) { for (const auto& i : *varsp) VL_PRINTF_MT(" VAR %p: %s\n", &(i.second), i.first); } } diff --git a/include/verilated.h b/include/verilated.h index cb375d27c..1866e34fb 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -29,6 +29,7 @@ #ifndef VERILATOR_VERILATED_H_ #define VERILATOR_VERILATED_H_ +#define VERILATOR_VERILATED_H_INTERNAL_ // clang-format off #include "verilatedos.h" @@ -36,18 +37,22 @@ # include "verilated_sc.h" // Get SYSTEMC_VERSION and time declarations #endif +#include +#include #include #include #include #include #include #include +#include +#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 #ifdef VL_THREADED # include # include @@ -259,31 +264,7 @@ public: const char* name() const { return m_namep; } ///< Return name of module }; -//========================================================================= -// Declare nets - -#define VL_SIG8(name, msb, lsb) CData name ///< Declare signal, 1-8 bits -#define VL_SIG16(name, msb, lsb) SData name ///< Declare signal, 9-16 bits -#define VL_SIG64(name, msb, lsb) QData name ///< Declare signal, 33-64 bits -#define VL_SIG(name, msb, lsb) IData name ///< Declare signal, 17-32 bits -#define VL_SIGW(name, msb, lsb, words) WData name[words] ///< Declare signal, 65+ bits -#define VL_IN8(name, msb, lsb) CData name ///< Declare input signal, 1-8 bits -#define VL_IN16(name, msb, lsb) SData name ///< Declare input signal, 9-16 bits -#define VL_IN64(name, msb, lsb) QData name ///< Declare input signal, 33-64 bits -#define VL_IN(name, msb, lsb) IData name ///< Declare input signal, 17-32 bits -#define VL_INW(name, msb, lsb, words) WData name[words] ///< Declare input signal, 65+ bits -#define VL_INOUT8(name, msb, lsb) CData name ///< Declare bidir signal, 1-8 bits -#define VL_INOUT16(name, msb, lsb) SData name ///< Declare bidir signal, 9-16 bits -#define VL_INOUT64(name, msb, lsb) QData name ///< Declare bidir signal, 33-64 bits -#define VL_INOUT(name, msb, lsb) IData name ///< Declare bidir signal, 17-32 bits -#define VL_INOUTW(name, msb, lsb, words) WData name[words] ///< Declare bidir signal, 65+ bits -#define VL_OUT8(name, msb, lsb) CData name ///< Declare output signal, 1-8 bits -#define VL_OUT16(name, msb, lsb) SData name ///< Declare output signal, 9-16 bits -#define VL_OUT64(name, msb, lsb) QData name ///< Declare output signal, 33-64bits -#define VL_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits -#define VL_OUTW(name, msb, lsb, words) WData name[words] ///< Declare output signal, 65+ bits - -///< Declare a module, ala SC_MODULE +/// Declare a module, ala SC_MODULE #define VL_MODULE(modname) class modname VL_NOT_FINAL : public VerilatedModule // Not class final in VL_MODULE, as users might be abstracting our models (--hierarchical) @@ -887,2122 +868,16 @@ 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 +// Data Types -/// Routine to call for $finish -/// User code may wish to replace this function, to do so, define VL_USER_FINISH. -/// This code does not have to be thread safe. -/// 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 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. -extern void vl_stop(const char* filename, int linenum, const char* hier); - -/// Routine to call for a couple of fatal messages -/// User code may wish to replace this function, to do so, define VL_USER_FATAL. -/// This code does not have to be thread safe. -/// Verilator internal code must call VL_FINISH_MT instead, which eventually calls this. -extern void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg); +#include "verilated_types.h" //========================================================================= -// Extern functions -- Slow path +// Functions -/// Multithread safe wrapper for calls to $finish -extern void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE; -/// Multithread safe wrapper for calls to $stop -extern void VL_STOP_MT(const char* filename, int linenum, const char* hier, - bool maybe = true) VL_MT_SAFE; -/// Multithread safe wrapper to call for a couple of fatal messages -extern void VL_FATAL_MT(const char* filename, int linenum, const char* hier, - const char* msg) VL_MT_SAFE; - -// clang-format off -/// Print a string, multithread safe. Eventually VL_PRINTF will get called. -#ifdef VL_THREADED -extern void VL_PRINTF_MT(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE; -#else -# define VL_PRINTF_MT VL_PRINTF // The following parens will take care of themselves -#endif -// clang-format on - -/// Print a debug message from internals with standard prefix, with printf style format -extern void VL_DBG_MSGF(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE; - -extern vluint64_t vl_rand64() VL_MT_SAFE; -inline IData VL_RANDOM_I(int obits) VL_MT_SAFE { return vl_rand64() & VL_MASK_I(obits); } -inline QData VL_RANDOM_Q(int obits) VL_MT_SAFE { return vl_rand64() & VL_MASK_Q(obits); } -#ifndef VL_NO_LEGACY -extern WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp); -#endif -extern IData VL_RANDOM_SEEDED_II(int obits, IData seed) VL_MT_SAFE; -inline IData VL_URANDOM_RANGE_I(IData hi, IData lo) { - vluint64_t rnd = vl_rand64(); - if (VL_LIKELY(hi > lo)) { - // Modulus isn't very fast but it's common that hi-low is power-of-two - return (rnd % (hi - lo + 1)) + lo; - } else { - return (rnd % (lo - hi + 1)) + hi; - } -} - -// These are init time only, so slow is fine -/// Random reset a signal of given width -extern IData VL_RAND_RESET_I(int obits); -/// Random reset a signal of given width -extern QData VL_RAND_RESET_Q(int obits); -/// Random reset a signal of given width -extern WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp); -/// Zero reset a signal (slow - else use VL_ZERO_W) -extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp); - -#if VL_THREADED -/// Return high-precision counter for profiling, or 0x0 if not available -inline QData VL_RDTSC_Q() { - vluint64_t val; - VL_RDTSC(val); - return val; -} -#endif - -extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp, - const VerilatedContext* contextp) VL_MT_SAFE; - -extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP const lwp, WDataInP const rwp, - bool is_modulus); - -extern IData VL_FGETS_IXI(int obits, void* destp, IData fpi); - -extern void VL_FFLUSH_I(IData fdi); -extern IData VL_FSEEK_I(IData fdi, IData offset, IData origin); -extern IData VL_FTELL_I(IData fdi); -extern void VL_FCLOSE_I(IData fdi); - -extern IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi, - IData start, IData count); - -extern void VL_WRITEF(const char* formatp, ...); -extern void VL_FWRITEF(IData fpi, const char* formatp, ...); - -extern IData VL_FSCANF_IX(IData fpi, const char* formatp, ...); -extern IData VL_SSCANF_IIX(int lbits, IData ld, const char* formatp, ...); -extern IData VL_SSCANF_IQX(int lbits, QData ld, const char* formatp, ...); -extern IData VL_SSCANF_IWX(int lbits, WDataInP const lwp, const char* formatp, ...); - -extern void VL_SFORMAT_X(int obits, CData& destr, const char* formatp, ...); -extern void VL_SFORMAT_X(int obits, SData& destr, const char* formatp, ...); -extern void VL_SFORMAT_X(int obits, IData& destr, const char* formatp, ...); -extern void VL_SFORMAT_X(int obits, QData& destr, const char* formatp, ...); -extern void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...); - -extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp); -extern IData VL_SYSTEM_IQ(QData lhs); -inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); } - -extern IData VL_TESTPLUSARGS_I(const char* formatp); -extern const char* vl_mc_scan_plusargs(const char* prefixp); // PLIish - -//========================================================================= -// Base macros - -// Return true if data[bit] set; not 0/1 return, but 0/non-zero return. -#define VL_BITISSET_I(data, bit) ((data) & (VL_UL(1) << VL_BITBIT_I(bit))) -#define VL_BITISSET_Q(data, bit) ((data) & (1ULL << VL_BITBIT_Q(bit))) -#define VL_BITISSET_E(data, bit) ((data) & (VL_EUL(1) << VL_BITBIT_E(bit))) -#define VL_BITISSET_W(data, bit) ((data)[VL_BITWORD_E(bit)] & (VL_EUL(1) << VL_BITBIT_E(bit))) -#define VL_BITISSETLIMIT_W(data, width, bit) (((bit) < (width)) && VL_BITISSET_W(data, bit)) - -// Shift appropriate word by bit. Does not account for wrapping between two words -#define VL_BITRSHIFT_W(data, bit) ((data)[VL_BITWORD_E(bit)] >> VL_BITBIT_E(bit)) - -// Create two 32-bit words from quadword -// WData is always at least 2 words; does not clean upper bits -#define VL_SET_WQ(owp, data) \ - do { \ - (owp)[0] = static_cast(data); \ - (owp)[1] = static_cast((data) >> VL_EDATASIZE); \ - } while (false) -#define VL_SET_WI(owp, data) \ - do { \ - (owp)[0] = static_cast(data); \ - (owp)[1] = 0; \ - } while (false) -#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)) - -// Return FILE* from IData -extern FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE; - -// clang-format off -// Use a union to avoid cast-to-different-size warnings -// Return void* from QData -static inline void* VL_CVT_Q_VP(QData lhs) VL_PURE { - union { void* fp; QData q; } u; - u.q = lhs; - return u.fp; -} -// Return QData from const void* -static inline QData VL_CVT_VP_Q(const void* fp) VL_PURE { - union { const void* fp; QData q; } u; - u.q = 0; - u.fp = fp; - return u.q; -} -// Return double from QData (bits, not numerically) -static inline double VL_CVT_D_Q(QData lhs) VL_PURE { - union { double d; QData q; } u; - u.q = lhs; - return u.d; -} -// Return QData from double (bits, not numerically) -static inline QData VL_CVT_Q_D(double lhs) VL_PURE { - union { double d; QData q; } u; - u.d = lhs; - return u.q; -} -// clang-format on - -// Return double from lhs (numeric) unsigned -double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE; -static inline double VL_ITOR_D_I(int, IData lhs) VL_PURE { - return static_cast(static_cast(lhs)); -} -static inline double VL_ITOR_D_Q(int, QData lhs) VL_PURE { - return static_cast(static_cast(lhs)); -} -// Return double from lhs (numeric) signed -double VL_ISTOR_D_W(int lbits, WDataInP const lwp) VL_PURE; -static inline double VL_ISTOR_D_I(int lbits, IData lhs) VL_PURE { - if (lbits == 32) return static_cast(static_cast(lhs)); - WData lwp[VL_WQ_WORDS_E]; - VL_SET_WI(lwp, lhs); - return VL_ISTOR_D_W(lbits, lwp); -} -static inline double VL_ISTOR_D_Q(int lbits, QData lhs) VL_PURE { - if (lbits == 64) return static_cast(static_cast(lhs)); - WData lwp[VL_WQ_WORDS_E]; - VL_SET_WQ(lwp, lhs); - return VL_ISTOR_D_W(lbits, lwp); -} -// Return QData from double (numeric) -static inline IData VL_RTOI_I_D(double lhs) VL_PURE { - return static_cast(VL_TRUNC(lhs)); -} - -// Sign extend such that if MSB set, we get ffff_ffff, else 0s -// (Requires clean input) -#define VL_SIGN_I(nbits, lhs) ((lhs) >> VL_BITBIT_I((nbits)-VL_UL(1))) -#define VL_SIGN_Q(nbits, lhs) ((lhs) >> VL_BITBIT_Q((nbits)-1ULL)) -#define VL_SIGN_E(nbits, lhs) ((lhs) >> VL_BITBIT_E((nbits)-VL_EUL(1))) -#define VL_SIGN_W(nbits, rwp) \ - ((rwp)[VL_BITWORD_E((nbits)-VL_EUL(1))] >> VL_BITBIT_E((nbits)-VL_EUL(1))) -#define VL_SIGNONES_E(nbits, lhs) (-(VL_SIGN_E(nbits, lhs))) - -// Sign bit extended up to MSB, doesn't include unsigned portion -// Optimization bug in GCC 3.3 returns different bitmasks to later states for -static inline IData VL_EXTENDSIGN_I(int lbits, IData lhs) VL_PURE { - return (-((lhs) & (VL_UL(1) << (lbits - 1)))); -} -static inline QData VL_EXTENDSIGN_Q(int lbits, QData lhs) VL_PURE { - return (-((lhs) & (1ULL << (lbits - 1)))); -} - -// Debugging prints -extern void _vl_debug_print_w(int lbits, WDataInP const iwp); - -//========================================================================= -// Pli macros - -extern int VL_TIME_STR_CONVERT(const char* strp) VL_PURE; - -// These are deprecated and used only to establish the default precision/units. -// Use Verilator timescale-override for better control. -// clang-format off -#ifndef VL_TIME_PRECISION -# ifdef VL_TIME_PRECISION_STR -# define VL_TIME_PRECISION VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR)) -# else -# define VL_TIME_PRECISION (-12) ///< Timescale default units if not in Verilog - picoseconds -# endif -#endif -#ifndef VL_TIME_UNIT -# ifdef VL_TIME_UNIT_STR -# define VL_TIME_UNIT VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR)) -# else -# define VL_TIME_UNIT (-12) ///< Timescale default units if not in Verilog - picoseconds -# endif -#endif - -#if defined(SYSTEMC_VERSION) -/// Return current simulation time -// Already defined: extern sc_time sc_time_stamp(); -inline vluint64_t vl_time_stamp64() { return sc_time_stamp().value(); } -#else // Non-SystemC -# 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 - -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. -// Can't use multiply in Q flavor, as might lose precision -#define VL_TIME_UNITED_Q(scale) (VL_TIME_Q() / static_cast(scale)) -#define VL_TIME_UNITED_D(scale) (VL_TIME_D() / static_cast(scale)) - -// Return time precision as multiplier of time units -double vl_time_multiplier(int scale) VL_PURE; -// Return power of 10. e.g. returns 100 if n==2 -vluint64_t vl_time_pow10(int n) VL_PURE; - -#ifdef VL_DEBUG -/// Evaluate statement if Verilated::debug() enabled -# define VL_DEBUG_IF(stmt) \ - do { \ - if (VL_UNLIKELY(Verilated::debug())) {stmt} \ - } while (false) -#else -// We intentionally do not compile the stmt to improve compile speed -# define VL_DEBUG_IF(stmt) do {} while (false) -#endif - -// clang-format on - -//========================================================================= -// Functional macros/routines -// These all take the form -// VL_func_IW(bits, bits, op, op) -// VL_func_WW(bits, bits, out, op, op) -// The I/W indicates if it's a integer or wide for the output and each operand. -// The bits indicate the bit width of the output and each operand. -// If wide output, a temporary storage location is specified. - -//=================================================================== -// SETTING OPERATORS - -// Output clean -// EMIT_RULE: VL_CLEAN: oclean=clean; obits=lbits; -#define VL_CLEAN_II(obits, lbits, lhs) ((lhs)&VL_MASK_I(obits)) -#define VL_CLEAN_QQ(obits, lbits, lhs) ((lhs)&VL_MASK_Q(obits)) - -// 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 { - const int words = VL_WORDS_I(obits); - owp[words - 1] &= VL_MASK_E(obits); - return owp; -} -static inline WDataOutP VL_CLEAN_WW(int obits, int, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { - const 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_E(obits); - return owp; -} -static inline WDataOutP VL_ZERO_W(int obits, WDataOutP owp) VL_MT_SAFE { - const int words = VL_WORDS_I(obits); - for (int i = 0; i < words; ++i) owp[i] = 0; - return owp; -} -static inline WDataOutP VL_ALLONES_W(int obits, WDataOutP owp) VL_MT_SAFE { - const int words = VL_WORDS_I(obits); - for (int i = 0; i < (words - 1); ++i) owp[i] = ~VL_EUL(0); - owp[words - 1] = VL_MASK_E(obits); - return owp; -} - -// EMIT_RULE: VL_ASSIGN: oclean=rclean; obits==lbits; -// For now, we always have a clean rhs. -// Note: If a ASSIGN isn't clean, use VL_ASSIGNCLEAN instead to do the same thing. -static inline WDataOutP VL_ASSIGN_W(int obits, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { - const int words = VL_WORDS_I(obits); - for (int i = 0; i < words; ++i) owp[i] = lwp[i]; - return owp; -} - -// EMIT_RULE: VL_ASSIGNBIT: rclean=clean; -static inline void VL_ASSIGNBIT_II(int, int bit, CData& lhsr, IData rhs) VL_PURE { - lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit))); -} -static inline void VL_ASSIGNBIT_II(int, int bit, SData& lhsr, IData rhs) VL_PURE { - lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit))); -} -static inline void VL_ASSIGNBIT_II(int, int bit, IData& lhsr, IData rhs) VL_PURE { - lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit))); -} -static inline void VL_ASSIGNBIT_QI(int, int bit, QData& lhsr, QData rhs) VL_PURE { - lhsr = ((lhsr & ~(1ULL << VL_BITBIT_Q(bit))) | (static_cast(rhs) << VL_BITBIT_Q(bit))); -} -static inline void VL_ASSIGNBIT_WI(int, int bit, WDataOutP owp, IData rhs) VL_MT_SAFE { - EData orig = owp[VL_BITWORD_E(bit)]; - owp[VL_BITWORD_E(bit)] = ((orig & ~(VL_EUL(1) << VL_BITBIT_E(bit))) - | (static_cast(rhs) << VL_BITBIT_E(bit))); -} -// Alternative form that is an instruction faster when rhs is constant one. -static inline void VL_ASSIGNBIT_IO(int, int bit, CData& lhsr, IData) VL_PURE { - lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit))); -} -static inline void VL_ASSIGNBIT_IO(int, int bit, SData& lhsr, IData) VL_PURE { - lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit))); -} -static inline void VL_ASSIGNBIT_IO(int, int bit, IData& lhsr, IData) VL_PURE { - lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit))); -} -static inline void VL_ASSIGNBIT_QO(int, int bit, QData& lhsr, IData) VL_PURE { - lhsr = (lhsr | (1ULL << VL_BITBIT_Q(bit))); -} -static inline void VL_ASSIGNBIT_WO(int, int bit, WDataOutP owp, IData) VL_MT_SAFE { - const EData orig = owp[VL_BITWORD_E(bit)]; - owp[VL_BITWORD_E(bit)] = (orig | (VL_EUL(1) << VL_BITBIT_E(bit))); -} - -//=================================================================== -// SYSTEMC OPERATORS -// Copying verilog format to systemc integers and bit vectors. -// Get a SystemC variable - -#define VL_ASSIGN_ISI(obits, vvar, svar) \ - { (vvar) = VL_CLEAN_II((obits), (obits), (svar).read()); } -#define VL_ASSIGN_QSQ(obits, vvar, svar) \ - { (vvar) = VL_CLEAN_QQ((obits), (obits), (svar).read()); } - -#define VL_ASSIGN_ISW(obits, od, svar) \ - { (od) = ((svar).read().get_word(0)) & VL_MASK_I(obits); } -#define VL_ASSIGN_QSW(obits, od, svar) \ - { \ - (od) = ((static_cast((svar).read().get_word(1))) << VL_IDATASIZE \ - | (svar).read().get_word(0)) \ - & VL_MASK_Q(obits); \ - } -#define VL_ASSIGN_WSW(obits, owp, svar) \ - { \ - const int words = VL_WORDS_I(obits); \ - for (int i = 0; i < words; ++i) (owp)[i] = (svar).read().get_word(i); \ - (owp)[words - 1] &= VL_MASK_E(obits); \ - } - -#define VL_ASSIGN_ISU(obits, vvar, svar) \ - { (vvar) = VL_CLEAN_II((obits), (obits), (svar).read().to_uint()); } -#define VL_ASSIGN_QSU(obits, vvar, svar) \ - { (vvar) = VL_CLEAN_QQ((obits), (obits), (svar).read().to_uint64()); } -#define VL_ASSIGN_WSB(obits, owp, svar) \ - { \ - const int words = VL_WORDS_I(obits); \ - sc_biguint<(obits)> _butemp = (svar).read(); \ - for (int i = 0; i < words; ++i) { \ - int msb = ((i + 1) * VL_IDATASIZE) - 1; \ - msb = (msb >= (obits)) ? ((obits)-1) : msb; \ - (owp)[i] = _butemp.range(msb, i * VL_IDATASIZE).to_uint(); \ - } \ - (owp)[words - 1] &= VL_MASK_E(obits); \ - } - -// Copying verilog format from systemc integers and bit vectors. -// Set a SystemC variable - -#define VL_ASSIGN_SII(obits, svar, vvar) \ - { (svar).write(vvar); } -#define VL_ASSIGN_SQQ(obits, svar, vvar) \ - { (svar).write(vvar); } - -#define VL_ASSIGN_SWI(obits, svar, rd) \ - { \ - sc_bv<(obits)> _bvtemp; \ - _bvtemp.set_word(0, (rd)); \ - (svar).write(_bvtemp); \ - } -#define VL_ASSIGN_SWQ(obits, svar, rd) \ - { \ - sc_bv<(obits)> _bvtemp; \ - _bvtemp.set_word(0, static_cast(rd)); \ - _bvtemp.set_word(1, static_cast((rd) >> VL_IDATASIZE)); \ - (svar).write(_bvtemp); \ - } -#define VL_ASSIGN_SWW(obits, svar, rwp) \ - { \ - sc_bv<(obits)> _bvtemp; \ - for (int i = 0; i < VL_WORDS_I(obits); ++i) _bvtemp.set_word(i, (rwp)[i]); \ - (svar).write(_bvtemp); \ - } - -#define VL_ASSIGN_SUI(obits, svar, rd) \ - { (svar).write(rd); } -#define VL_ASSIGN_SUQ(obits, svar, rd) \ - { (svar).write(rd); } -#define VL_ASSIGN_SBI(obits, svar, rd) \ - { (svar).write(rd); } -#define VL_ASSIGN_SBQ(obits, svar, rd) \ - { (svar).write(rd); } -#define VL_ASSIGN_SBW(obits, svar, rwp) \ - { \ - sc_biguint<(obits)> _butemp; \ - for (int i = 0; i < VL_WORDS_I(obits); ++i) { \ - int msb = ((i + 1) * VL_IDATASIZE) - 1; \ - msb = (msb >= (obits)) ? ((obits)-1) : msb; \ - _butemp.range(msb, i* VL_IDATASIZE) = (rwp)[i]; \ - } \ - (svar).write(_butemp); \ - } - -//=================================================================== -// Extending sizes - -// CAREFUL, we're width changing, so obits!=lbits - -// Right must be clean because otherwise size increase would pick up bad bits -// EMIT_RULE: VL_EXTEND: oclean=clean; rclean==clean; -#define VL_EXTEND_II(obits, lbits, lhs) ((lhs)) -#define VL_EXTEND_QI(obits, lbits, lhs) (static_cast(lhs)) -#define VL_EXTEND_QQ(obits, lbits, lhs) ((lhs)) - -static inline WDataOutP VL_EXTEND_WI(int obits, int, WDataOutP owp, IData ld) VL_MT_SAFE { - // Note for extracts that obits != lbits - owp[0] = ld; - for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - return owp; -} -static inline WDataOutP VL_EXTEND_WQ(int obits, int, WDataOutP owp, QData ld) VL_MT_SAFE { - VL_SET_WQ(owp, ld); - for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - return owp; -} -static inline WDataOutP VL_EXTEND_WW(int obits, int lbits, WDataOutP owp, - WDataInP const lwp) VL_MT_SAFE { - for (int i = 0; i < VL_WORDS_I(lbits); ++i) owp[i] = lwp[i]; - for (int i = VL_WORDS_I(lbits); i < VL_WORDS_I(obits); ++i) owp[i] = 0; - return owp; -} - -// EMIT_RULE: VL_EXTENDS: oclean=*dirty*; obits=lbits; -// Sign extension; output dirty -static inline IData VL_EXTENDS_II(int, int lbits, IData lhs) VL_PURE { - return VL_EXTENDSIGN_I(lbits, lhs) | lhs; -} -static inline QData VL_EXTENDS_QI(int, int lbits, QData lhs /*Q_as_need_extended*/) VL_PURE { - return VL_EXTENDSIGN_Q(lbits, lhs) | lhs; -} -static inline QData VL_EXTENDS_QQ(int, int lbits, QData lhs) VL_PURE { - return VL_EXTENDSIGN_Q(lbits, lhs) | lhs; -} - -static inline WDataOutP VL_EXTENDS_WI(int obits, int lbits, WDataOutP owp, IData ld) VL_MT_SAFE { - const EData sign = VL_SIGNONES_E(lbits, static_cast(ld)); - owp[0] = ld | (sign & ~VL_MASK_E(lbits)); - for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = sign; - return owp; -} -static inline WDataOutP VL_EXTENDS_WQ(int obits, int lbits, WDataOutP owp, QData ld) VL_MT_SAFE { - VL_SET_WQ(owp, ld); - const EData sign = VL_SIGNONES_E(lbits, owp[1]); - owp[1] |= sign & ~VL_MASK_E(lbits); - for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = sign; - return owp; -} -static inline WDataOutP VL_EXTENDS_WW(int obits, int lbits, WDataOutP owp, - WDataInP const lwp) VL_MT_SAFE { - for (int i = 0; i < VL_WORDS_I(lbits) - 1; ++i) owp[i] = lwp[i]; - const int lmsw = VL_WORDS_I(lbits) - 1; - const EData sign = VL_SIGNONES_E(lbits, lwp[lmsw]); - owp[lmsw] = lwp[lmsw] | (sign & ~VL_MASK_E(lbits)); - for (int i = VL_WORDS_I(lbits); i < VL_WORDS_I(obits); ++i) owp[i] = sign; - return owp; -} - -//=================================================================== -// REDUCTION OPERATORS - -// EMIT_RULE: VL_REDAND: oclean=clean; lclean==clean; obits=1; -#define VL_REDAND_II(obits, lbits, lhs) ((lhs) == VL_MASK_I(lbits)) -#define VL_REDAND_IQ(obits, lbits, lhs) ((lhs) == VL_MASK_Q(lbits)) -static inline IData VL_REDAND_IW(int, int lbits, WDataInP const lwp) VL_MT_SAFE { - const int words = VL_WORDS_I(lbits); - EData combine = lwp[0]; - for (int i = 1; i < words - 1; ++i) combine &= lwp[i]; - combine &= ~VL_MASK_E(lbits) | lwp[words - 1]; - return ((~combine) == 0); -} - -// EMIT_RULE: VL_REDOR: oclean=clean; lclean==clean; obits=1; -#define VL_REDOR_I(lhs) ((lhs) != 0) -#define VL_REDOR_Q(lhs) ((lhs) != 0) -static inline IData VL_REDOR_W(int words, WDataInP const lwp) VL_MT_SAFE { - EData equal = 0; - for (int i = 0; i < words; ++i) equal |= lwp[i]; - return (equal != 0); -} - -// EMIT_RULE: VL_REDXOR: oclean=dirty; obits=1; -static inline IData VL_REDXOR_2(IData r) VL_PURE { - // Experiments show VL_REDXOR_2 is faster than __builtin_parityl - r = (r ^ (r >> 1)); - return r; -} -static inline IData VL_REDXOR_4(IData r) VL_PURE { -#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) - return __builtin_parityl(r); -#else - r = (r ^ (r >> 1)); - r = (r ^ (r >> 2)); - return r; -#endif -} -static inline IData VL_REDXOR_8(IData r) VL_PURE { -#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) - return __builtin_parityl(r); -#else - r = (r ^ (r >> 1)); - r = (r ^ (r >> 2)); - r = (r ^ (r >> 4)); - return r; -#endif -} -static inline IData VL_REDXOR_16(IData r) VL_PURE { -#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) - return __builtin_parityl(r); -#else - r = (r ^ (r >> 1)); - r = (r ^ (r >> 2)); - r = (r ^ (r >> 4)); - r = (r ^ (r >> 8)); - return r; -#endif -} -static inline IData VL_REDXOR_32(IData r) VL_PURE { -#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) - return __builtin_parityl(r); -#else - r = (r ^ (r >> 1)); - r = (r ^ (r >> 2)); - r = (r ^ (r >> 4)); - r = (r ^ (r >> 8)); - r = (r ^ (r >> 16)); - return r; -#endif -} -static inline IData VL_REDXOR_64(QData r) VL_PURE { -#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) - return __builtin_parityll(r); -#else - r = (r ^ (r >> 1)); - r = (r ^ (r >> 2)); - r = (r ^ (r >> 4)); - r = (r ^ (r >> 8)); - r = (r ^ (r >> 16)); - r = (r ^ (r >> 32)); - return static_cast(r); -#endif -} -static inline IData VL_REDXOR_W(int words, WDataInP const lwp) VL_MT_SAFE { - EData r = lwp[0]; - for (int i = 1; i < words; ++i) r ^= lwp[i]; - return VL_REDXOR_32(r); -} - -// EMIT_RULE: VL_COUNTONES_II: oclean = false; lhs clean -static inline IData VL_COUNTONES_I(IData lhs) VL_PURE { - // This is faster than __builtin_popcountl - IData r = lhs - ((lhs >> 1) & 033333333333) - ((lhs >> 2) & 011111111111); - r = (r + (r >> 3)) & 030707070707; - r = (r + (r >> 6)); - r = (r + (r >> 12) + (r >> 24)) & 077; - return r; -} -static inline IData VL_COUNTONES_Q(QData lhs) VL_PURE { - return VL_COUNTONES_I(static_cast(lhs)) + VL_COUNTONES_I(static_cast(lhs >> 32)); -} -#define VL_COUNTONES_E VL_COUNTONES_I -static inline IData VL_COUNTONES_W(int words, WDataInP const lwp) VL_MT_SAFE { - EData r = 0; - for (int i = 0; i < words; ++i) r += VL_COUNTONES_E(lwp[i]); - return r; -} - -// EMIT_RULE: VL_COUNTBITS_II: oclean = false; lhs clean -static inline IData VL_COUNTBITS_I(int lbits, IData lhs, IData ctrl0, IData ctrl1, - IData ctrl2) VL_PURE { - int ctrlSum = (ctrl0 & 0x1) + (ctrl1 & 0x1) + (ctrl2 & 0x1); - if (ctrlSum == 3) { - return VL_COUNTONES_I(lhs); - } else if (ctrlSum == 0) { - IData mask = (lbits == 32) ? -1 : ((1 << lbits) - 1); - return VL_COUNTONES_I(~lhs & mask); - } else { - return (lbits == 32) ? 32 : lbits; - } -} -static inline IData VL_COUNTBITS_Q(int lbits, QData lhs, IData ctrl0, IData ctrl1, - IData ctrl2) VL_PURE { - return VL_COUNTBITS_I(32, static_cast(lhs), ctrl0, ctrl1, ctrl2) - + VL_COUNTBITS_I(lbits - 32, static_cast(lhs >> 32), ctrl0, ctrl1, ctrl2); -} -#define VL_COUNTBITS_E VL_COUNTBITS_I -static inline IData VL_COUNTBITS_W(int lbits, int words, WDataInP const lwp, IData ctrl0, - IData ctrl1, IData ctrl2) VL_MT_SAFE { - EData r = 0; - IData wordLbits = 32; - for (int i = 0; i < words; ++i) { - if (i == words - 1) wordLbits = lbits % 32; - r += VL_COUNTBITS_E(wordLbits, lwp[i], ctrl0, ctrl1, ctrl2); - } - return r; -} - -static inline IData VL_ONEHOT_I(IData lhs) VL_PURE { - return (((lhs & (lhs - 1)) == 0) & (lhs != 0)); -} -static inline IData VL_ONEHOT_Q(QData lhs) VL_PURE { - return (((lhs & (lhs - 1)) == 0) & (lhs != 0)); -} -static inline IData VL_ONEHOT_W(int words, WDataInP const lwp) VL_MT_SAFE { - EData one = 0; - for (int i = 0; (i < words); ++i) { - if (lwp[i]) { - if (one) return 0; - one = 1; - if (lwp[i] & (lwp[i] - 1)) return 0; - } - } - return one; -} - -static inline IData VL_ONEHOT0_I(IData lhs) VL_PURE { return ((lhs & (lhs - 1)) == 0); } -static inline IData VL_ONEHOT0_Q(QData lhs) VL_PURE { return ((lhs & (lhs - 1)) == 0); } -static inline IData VL_ONEHOT0_W(int words, WDataInP const lwp) VL_MT_SAFE { - bool one = false; - for (int i = 0; (i < words); ++i) { - if (lwp[i]) { - if (one) return 0; - one = true; - if (lwp[i] & (lwp[i] - 1)) return 0; - } - } - return 1; -} - -static inline IData VL_CLOG2_I(IData lhs) VL_PURE { - // There are faster algorithms, or fls GCC4 builtins, but rarely used - if (VL_UNLIKELY(!lhs)) return 0; - lhs--; - int shifts = 0; - for (; lhs != 0; ++shifts) lhs = lhs >> 1; - return shifts; -} -static inline IData VL_CLOG2_Q(QData lhs) VL_PURE { - if (VL_UNLIKELY(!lhs)) return 0; - lhs--; - int shifts = 0; - for (; lhs != 0; ++shifts) lhs = lhs >> 1ULL; - return shifts; -} -static inline IData VL_CLOG2_W(int words, WDataInP const lwp) VL_MT_SAFE { - EData adjust = (VL_COUNTONES_W(words, lwp) == 1) ? 0 : 1; - for (int i = words - 1; i >= 0; --i) { - if (VL_UNLIKELY(lwp[i])) { // Shorter worst case if predict not taken - for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) { - if (VL_UNLIKELY(VL_BITISSET_E(lwp[i], bit))) { - return i * VL_EDATASIZE + bit + adjust; - } - } - // Can't get here - one bit must be set - } - } - return 0; -} - -static inline IData VL_MOSTSETBITP1_W(int words, WDataInP const lwp) VL_MT_SAFE { - // MSB set bit plus one; similar to FLS. 0=value is zero - for (int i = words - 1; i >= 0; --i) { - if (VL_UNLIKELY(lwp[i])) { // Shorter worst case if predict not taken - for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) { - if (VL_UNLIKELY(VL_BITISSET_E(lwp[i], bit))) return i * VL_EDATASIZE + bit + 1; - } - // Can't get here - one bit must be set - } - } - return 0; -} - -//=================================================================== -// SIMPLE LOGICAL OPERATORS - -// EMIT_RULE: VL_AND: oclean=lclean||rclean; obits=lbits; lbits==rbits; -static inline WDataOutP VL_AND_W(int words, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] & rwp[i]); - return owp; -} -// EMIT_RULE: VL_OR: oclean=lclean&&rclean; obits=lbits; lbits==rbits; -static inline WDataOutP VL_OR_W(int words, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] | rwp[i]); - return owp; -} -// EMIT_RULE: VL_CHANGEXOR: oclean=1; obits=32; lbits==rbits; -static inline IData VL_CHANGEXOR_W(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { - IData od = 0; - for (int i = 0; (i < words); ++i) od |= (lwp[i] ^ rwp[i]); - return od; -} -// EMIT_RULE: VL_XOR: oclean=lclean&&rclean; obits=lbits; lbits==rbits; -static inline WDataOutP VL_XOR_W(int words, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] ^ rwp[i]); - return owp; -} -// EMIT_RULE: VL_NOT: oclean=dirty; obits=lbits; -static inline WDataOutP VL_NOT_W(int words, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { - for (int i = 0; i < words; ++i) owp[i] = ~(lwp[i]); - return owp; -} - -//========================================================================= -// Logical comparisons - -// EMIT_RULE: VL_EQ: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; -// EMIT_RULE: VL_NEQ: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; -// EMIT_RULE: VL_LT: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; -// EMIT_RULE: VL_GT: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; -// 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) - -// Output clean, AND MUST BE CLEAN -static inline IData VL_EQ_W(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { - EData nequal = 0; - for (int i = 0; (i < words); ++i) nequal |= (lwp[i] ^ rwp[i]); - return (nequal == 0); -} - -// Internal usage -static inline int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const 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; - } - 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) - -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. - // GCC 3.3.4 sign extension bugs on AMD64 architecture force us to use quad logic - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc - return lhs_signed > rhs_signed; -} -static inline IData VL_GTS_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); - return lhs_signed > rhs_signed; -} - -static inline IData VL_GTES_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc - return lhs_signed >= rhs_signed; -} -static inline IData VL_GTES_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); - return lhs_signed >= rhs_signed; -} - -static inline IData VL_LTS_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc - return lhs_signed < rhs_signed; -} -static inline IData VL_LTS_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); - return lhs_signed < rhs_signed; -} - -static inline IData VL_LTES_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc - return lhs_signed <= rhs_signed; -} -static inline IData VL_LTES_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); - return lhs_signed <= rhs_signed; -} - -static inline int _vl_cmps_w(int lbits, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { - const int words = VL_WORDS_I(lbits); - int i = words - 1; - // We need to flip sense if negative comparison - const EData lsign = VL_SIGN_E(lbits, lwp[i]); - const EData rsign = VL_SIGN_E(lbits, rwp[i]); - if (!lsign && rsign) return 1; // + > - - if (lsign && !rsign) return -1; // - < + - for (; i >= 0; --i) { - if (lwp[i] > rwp[i]) return 1; - if (lwp[i] < rwp[i]) return -1; - } - return 0; // == -} - -//========================================================================= -// Math - -// Output NOT clean -static inline WDataOutP VL_NEGATE_W(int words, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { - EData carry = 1; - for (int i = 0; i < words; ++i) { - owp[i] = ~lwp[i] + carry; - carry = (owp[i] < ~lwp[i]); - } - return owp; -} -static inline void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE { - EData carry = 1; - for (int i = 0; i < words; ++i) { - EData word = ~owp_lwp[i] + carry; - carry = (word < ~owp_lwp[i]); - owp_lwp[i] = word; - } -} - -// EMIT_RULE: VL_MUL: oclean=dirty; lclean==clean; rclean==clean; -// EMIT_RULE: VL_DIV: oclean=dirty; lclean==clean; rclean==clean; -// EMIT_RULE: VL_MODDIV: oclean=dirty; lclean==clean; rclean==clean; -#define VL_DIV_III(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) / (rhs)) -#define VL_DIV_QQQ(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) / (rhs)) -#define VL_DIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 0)) -#define VL_MODDIV_III(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) % (rhs)) -#define VL_MODDIV_QQQ(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) % (rhs)) -#define VL_MODDIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 1)) - -static inline WDataOutP VL_ADD_W(int words, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { - QData carry = 0; - for (int i = 0; i < words; ++i) { - carry = carry + static_cast(lwp[i]) + static_cast(rwp[i]); - owp[i] = (carry & 0xffffffffULL); - carry = (carry >> 32ULL) & 0xffffffffULL; - } - // Last output word is dirty - return owp; -} - -static inline WDataOutP VL_SUB_W(int words, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { - QData carry = 0; - for (int i = 0; i < words; ++i) { - carry = (carry + static_cast(lwp[i]) - + static_cast(static_cast(~rwp[i]))); - if (i == 0) ++carry; // Negation of rwp - owp[i] = (carry & 0xffffffffULL); - carry = (carry >> 32ULL) & 0xffffffffULL; - } - // Last output word is dirty - return owp; -} - -static inline WDataOutP VL_MUL_W(int words, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 0; i < words; ++i) owp[i] = 0; - for (int lword = 0; lword < words; ++lword) { - for (int rword = 0; rword < words; ++rword) { - QData mul = static_cast(lwp[lword]) * static_cast(rwp[rword]); - for (int qword = lword + rword; qword < words; ++qword) { - mul += static_cast(owp[qword]); - owp[qword] = (mul & 0xffffffffULL); - mul = (mul >> 32ULL) & 0xffffffffULL; - } - } - } - // Last output word is dirty - return owp; -} - -static inline IData VL_MULS_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { - const vlsint32_t lhs_signed = VL_EXTENDS_II(32, lbits, lhs); - const vlsint32_t rhs_signed = VL_EXTENDS_II(32, lbits, rhs); - return lhs_signed * rhs_signed; -} -static inline QData VL_MULS_QQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); - return lhs_signed * rhs_signed; -} - -static inline WDataOutP VL_MULS_WWW(int, int lbits, int, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { - const int words = VL_WORDS_I(lbits); - // cppcheck-suppress variableScope - WData lwstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here - // cppcheck-suppress variableScope - WData rwstore[VL_MULS_MAX_WORDS]; - WDataInP lwusp = lwp; - WDataInP rwusp = rwp; - EData lneg = VL_SIGN_E(lbits, lwp[words - 1]); - if (lneg) { // Negate lhs - lwusp = lwstore; - VL_NEGATE_W(words, lwstore, lwp); - lwstore[words - 1] &= VL_MASK_E(lbits); // Clean it - } - EData rneg = VL_SIGN_E(lbits, rwp[words - 1]); - if (rneg) { // Negate rhs - rwusp = rwstore; - VL_NEGATE_W(words, rwstore, rwp); - rwstore[words - 1] &= VL_MASK_E(lbits); // Clean it - } - VL_MUL_W(words, owp, lwusp, rwusp); - owp[words - 1] &= VL_MASK_E( - lbits); // Clean. Note it's ok for the multiply to overflow into the sign bit - if ((lneg ^ rneg) & 1) { // Negate output (not using NEGATE, as owp==lwp) - QData carry = 0; - for (int i = 0; i < words; ++i) { - carry = carry + static_cast(static_cast(~owp[i])); - if (i == 0) ++carry; // Negation of temp2 - owp[i] = (carry & 0xffffffffULL); - carry = (carry >> 32ULL) & 0xffffffffULL; - } - // Not needed: owp[words-1] |= 1< 0) power = power * power; - if (rhs & (1ULL << i)) out *= power; - } - return out; -} -static inline QData VL_POW_QQQ(int, int, int rbits, QData lhs, QData rhs) VL_PURE { - if (VL_UNLIKELY(rhs == 0)) return 1; - if (VL_UNLIKELY(lhs == 0)) return 0; - QData power = lhs; - QData out = 1ULL; - for (int i = 0; i < rbits; ++i) { - if (i > 0) power = power * power; - if (rhs & (1ULL << i)) out *= power; - } - return out; -} -WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp); -WDataOutP VL_POW_WWQ(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, QData rhs); -QData VL_POW_QQW(int obits, int, int rbits, QData lhs, WDataInP const rwp); - -#define VL_POWSS_IIQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) \ - VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) -#define VL_POWSS_IIQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) \ - VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) -#define VL_POWSS_IIW(obits, lbits, rbits, lhs, rwp, lsign, rsign) \ - VL_POWSS_QQW(obits, lbits, rbits, lhs, rwp, lsign, rsign) -#define VL_POWSS_QQI(obits, lbits, rbits, lhs, rhs, lsign, rsign) \ - VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) -#define VL_POWSS_WWI(obits, lbits, rbits, owp, lwp, rhs, lsign, rsign) \ - VL_POWSS_WWQ(obits, lbits, rbits, owp, lwp, rhs, lsign, rsign) - -static inline IData VL_POWSS_III(int obits, int, int rbits, IData lhs, IData rhs, bool lsign, - bool rsign) VL_MT_SAFE { - if (VL_UNLIKELY(rhs == 0)) return 1; - if (rsign && VL_SIGN_I(rbits, rhs)) { - if (lhs == 0) { - return 0; // "X" - } else if (lhs == 1) { - return 1; - } else if (lsign && lhs == VL_MASK_I(obits)) { // -1 - if (rhs & 1) { - return VL_MASK_I(obits); // -1^odd=-1 - } else { - return 1; // -1^even=1 - } - } - return 0; - } - return VL_POW_III(obits, rbits, rbits, lhs, rhs); -} -static inline QData VL_POWSS_QQQ(int obits, int, int rbits, QData lhs, QData rhs, bool lsign, - bool rsign) VL_MT_SAFE { - if (VL_UNLIKELY(rhs == 0)) return 1; - if (rsign && VL_SIGN_Q(rbits, rhs)) { - if (lhs == 0) { - return 0; // "X" - } else if (lhs == 1) { - return 1; - } else if (lsign && lhs == VL_MASK_Q(obits)) { // -1 - if (rhs & 1) { - return VL_MASK_Q(obits); // -1^odd=-1 - } else { - return 1; // -1^even=1 - } - } - return 0; - } - return VL_POW_QQQ(obits, rbits, rbits, lhs, rhs); -} -WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp, bool lsign, bool rsign); -WDataOutP VL_POWSS_WWQ(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, QData rhs, - bool lsign, bool rsign); -QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, WDataInP const rwp, bool lsign, - bool rsign); - -//=================================================================== -// Concat/replication - -// 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, - int rbits) VL_PURE { - const IData cleanmask = VL_MASK_I(rbits); - const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; - lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); -} -static inline void _vl_insert_II(int, SData& lhsr, IData ld, int hbit, int lbit, - int rbits) VL_PURE { - const IData cleanmask = VL_MASK_I(rbits); - const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; - lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); -} -static inline void _vl_insert_II(int, IData& lhsr, IData ld, int hbit, int lbit, - int rbits) VL_PURE { - const IData cleanmask = VL_MASK_I(rbits); - const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; - lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); -} -static inline void _vl_insert_QQ(int, QData& lhsr, QData ld, int hbit, int lbit, - int rbits) VL_PURE { - const QData cleanmask = VL_MASK_Q(rbits); - const QData insmask = (VL_MASK_Q(hbit - lbit + 1)) << lbit; - lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); -} -static inline void _vl_insert_WI(int, WDataOutP owp, IData ld, int hbit, int lbit, - int rbits = 0) VL_MT_SAFE { - const int hoffset = VL_BITBIT_E(hbit); - const int loffset = VL_BITBIT_E(lbit); - const int roffset = VL_BITBIT_E(rbits); - const int hword = VL_BITWORD_E(hbit); - const int lword = VL_BITWORD_E(lbit); - const int rword = VL_BITWORD_E(rbits); - const 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 & cleanmask; - } else { - const EData lde = static_cast(ld); - if (hword == lword) { // know < EData bits because above checks it - // Assignment is contained within one word of destination - const EData insmask = (VL_MASK_E(hoffset - loffset + 1)) << loffset; - owp[lword] = (owp[lword] & ~insmask) | ((lde << loffset) & (insmask & cleanmask)); - } else { - // Assignment crosses a word boundary in destination - const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << 0; - const EData linsmask = (VL_MASK_E((VL_EDATASIZE - 1) - loffset + 1)) << loffset; - const 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 & 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 const lwp, int hbit, int lbit, - int rbits = 0) VL_MT_SAFE { - const int hoffset = VL_BITBIT_E(hbit); - const int loffset = VL_BITBIT_E(lbit); - const int roffset = VL_BITBIT_E(rbits); - const int lword = VL_BITWORD_E(lbit); - const int hword = VL_BITWORD_E(hbit); - const int rword = VL_BITWORD_E(rbits); - const 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. - const 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 - 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 - const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)); - owp[hword] = (owp[hword] & ~hinsmask) | (lwp[words - 1] & (hinsmask & cleanmask)); - } else { - const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << 0; - const EData linsmask = (VL_MASK_E((VL_EDATASIZE - 1) - loffset + 1)) << loffset; - const int nbitsonright - = VL_EDATASIZE - loffset; // bits that end up in lword (know loffset!=0) - // Middle words - for (int i = 0; i < words; ++i) { - { // Lower word - const int oword = lword + i; - const EData d = lwp[i] << loffset; - const EData od = (owp[oword] & ~linsmask) | (d & linsmask); - if (oword == hword) { - owp[oword] = (owp[oword] & ~hinsmask) | (od & (hinsmask & cleanmask)); - } else { - owp[oword] = od; - } - } - { // Upper word - const int oword = lword + i + 1; - if (oword <= hword) { - const EData d = lwp[i] >> nbitsonright; - const EData od = (d & ~linsmask) | (owp[oword] & linsmask); - if (oword == hword) { - owp[oword] = (owp[oword] & ~hinsmask) | (od & (hinsmask & cleanmask)); - } else { - owp[oword] = od; - } - } - } - } - } -} - -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, rbits); -} - -// EMIT_RULE: VL_REPLICATE: oclean=clean>width32, dirty<=width32; lclean=clean; rclean==clean; -// RHS MUST BE CLEAN CONSTANT. -#define VL_REPLICATE_IOI(obits, lbits, rbits, ld, rep) (-(ld)) // Iff lbits==1 -#define VL_REPLICATE_QOI(obits, lbits, rbits, ld, rep) (-(static_cast(ld))) // Iff lbits==1 - -static inline IData VL_REPLICATE_III(int, int lbits, int, IData ld, IData rep) VL_PURE { - IData returndata = ld; - for (unsigned i = 1; i < rep; ++i) { - returndata = returndata << lbits; - returndata |= ld; - } - return returndata; -} -static inline QData VL_REPLICATE_QII(int, int lbits, int, IData ld, IData rep) VL_PURE { - QData returndata = ld; - for (unsigned i = 1; i < rep; ++i) { - returndata = returndata << lbits; - returndata |= static_cast(ld); - } - return returndata; -} -static inline WDataOutP VL_REPLICATE_WII(int obits, int lbits, int, WDataOutP owp, IData ld, - 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); - } - return owp; -} -static inline WDataOutP VL_REPLICATE_WQI(int obits, int lbits, int, WDataOutP owp, QData ld, - 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); - } - return owp; -} -static inline WDataOutP VL_REPLICATE_WWI(int obits, int lbits, int, WDataOutP owp, - WDataInP const lwp, 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); - } - return owp; -} - -// Left stream operator. Output will always be clean. LHS and RHS must be clean. -// Special "fast" versions for slice sizes that are a power of 2. These use -// shifts and masks to execute faster than the slower for-loop approach where a -// subset of bits is copied in during each iteration. -static inline IData VL_STREAML_FAST_III(int, int lbits, int, IData ld, IData rd_log2) VL_PURE { - // Pre-shift bits in most-significant slice: - // - // If lbits is not a multiple of the slice size (i.e., lbits % rd != 0), - // then we end up with a "gap" in our reversed result. For example, if we - // have a 5-bit Verlilog signal (lbits=5) in an 8-bit C data type: - // - // ld = ---43210 - // - // (where numbers are the Verilog signal bit numbers and '-' is an unused bit). - // Executing the switch statement below with a slice size of two (rd=2, - // rd_log2=1) produces: - // - // ret = 1032-400 - // - // Pre-shifting the bits in the most-significant slice allows us to avoid - // this gap in the shuffled data: - // - // ld_adjusted = --4-3210 - // ret = 10324--- - IData ret = ld; - if (rd_log2) { - const vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); // max multiple of rd <= lbits - const vluint32_t lbitsRem = lbits - lbitsFloor; // number of bits in most-sig slice (MSS) - const IData msbMask = VL_MASK_I(lbitsRem) << lbitsFloor; // mask to sel only bits in MSS - ret = (ret & ~msbMask) | ((ret & msbMask) << ((VL_UL(1) << rd_log2) - lbitsRem)); - } - switch (rd_log2) { - case 0: ret = ((ret >> 1) & VL_UL(0x55555555)) | ((ret & VL_UL(0x55555555)) << 1); // FALLTHRU - case 1: ret = ((ret >> 2) & VL_UL(0x33333333)) | ((ret & VL_UL(0x33333333)) << 2); // FALLTHRU - case 2: ret = ((ret >> 4) & VL_UL(0x0f0f0f0f)) | ((ret & VL_UL(0x0f0f0f0f)) << 4); // FALLTHRU - case 3: ret = ((ret >> 8) & VL_UL(0x00ff00ff)) | ((ret & VL_UL(0x00ff00ff)) << 8); // FALLTHRU - case 4: ret = ((ret >> 16) | (ret << 16)); // FALLTHRU - default:; - } - return ret >> (VL_IDATASIZE - lbits); -} - -static inline QData VL_STREAML_FAST_QQI(int, int lbits, int, QData ld, IData rd_log2) VL_PURE { - // Pre-shift bits in most-significant slice (see comment in VL_STREAML_FAST_III) - QData ret = ld; - if (rd_log2) { - const vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); - const vluint32_t lbitsRem = lbits - lbitsFloor; - const QData msbMask = VL_MASK_Q(lbitsRem) << lbitsFloor; - ret = (ret & ~msbMask) | ((ret & msbMask) << ((1ULL << rd_log2) - lbitsRem)); - } - switch (rd_log2) { - case 0: - ret = (((ret >> 1) & 0x5555555555555555ULL) - | ((ret & 0x5555555555555555ULL) << 1)); // FALLTHRU - case 1: - ret = (((ret >> 2) & 0x3333333333333333ULL) - | ((ret & 0x3333333333333333ULL) << 2)); // FALLTHRU - case 2: - ret = (((ret >> 4) & 0x0f0f0f0f0f0f0f0fULL) - | ((ret & 0x0f0f0f0f0f0f0f0fULL) << 4)); // FALLTHRU - case 3: - ret = (((ret >> 8) & 0x00ff00ff00ff00ffULL) - | ((ret & 0x00ff00ff00ff00ffULL) << 8)); // FALLTHRU - case 4: - ret = (((ret >> 16) & 0x0000ffff0000ffffULL) - | ((ret & 0x0000ffff0000ffffULL) << 16)); // FALLTHRU - case 5: ret = ((ret >> 32) | (ret << 32)); // FALLTHRU - default:; - } - return ret >> (VL_QUADSIZE - lbits); -} - -// Regular "slow" streaming operators -static inline IData VL_STREAML_III(int, int lbits, int, IData ld, IData rd) VL_PURE { - IData ret = 0; - // Slice size should never exceed the lhs width - const IData mask = VL_MASK_I(rd); - for (int istart = 0; istart < lbits; istart += rd) { - int ostart = lbits - rd - istart; - ostart = ostart > 0 ? ostart : 0; - ret |= ((ld >> istart) & mask) << ostart; - } - return ret; -} - -static inline QData VL_STREAML_QQI(int, int lbits, int, QData ld, IData rd) VL_PURE { - QData ret = 0; - // Slice size should never exceed the lhs width - const QData mask = VL_MASK_Q(rd); - for (int istart = 0; istart < lbits; istart += rd) { - int ostart = lbits - rd - istart; - ostart = ostart > 0 ? ostart : 0; - ret |= ((ld >> istart) & mask) << ostart; - } - return ret; -} - -static inline WDataOutP VL_STREAML_WWI(int, int lbits, int, WDataOutP owp, WDataInP const lwp, - IData rd) VL_MT_SAFE { - VL_ZERO_W(lbits, owp); - // Slice size should never exceed the lhs width - const int ssize = (rd < static_cast(lbits)) ? rd : (static_cast(lbits)); - for (int istart = 0; istart < lbits; istart += rd) { - int ostart = lbits - rd - istart; - ostart = ostart > 0 ? ostart : 0; - for (int sbit = 0; sbit < ssize && sbit < lbits - istart; ++sbit) { - // Extract a single bit from lwp and shift it to the correct - // location for owp. - EData bit = (VL_BITRSHIFT_W(lwp, (istart + sbit)) & 1) << VL_BITBIT_E(ostart + sbit); - owp[VL_BITWORD_E(ostart + sbit)] |= bit; - } - } - return owp; -} - -// Because concats are common and wide, it's valuable to always have a clean output. -// Thus we specify inputs must be clean, so we don't need to clean the output. -// Note the bit shifts are always constants, so the adds in these constify out. -// Casts required, as args may be 8 bit entities, and need to shift to appropriate output size -#define VL_CONCAT_III(obits, lbits, rbits, ld, rd) \ - (static_cast(ld) << (rbits) | static_cast(rd)) -#define VL_CONCAT_QII(obits, lbits, rbits, ld, rd) \ - (static_cast(ld) << (rbits) | static_cast(rd)) -#define VL_CONCAT_QIQ(obits, lbits, rbits, ld, rd) \ - (static_cast(ld) << (rbits) | static_cast(rd)) -#define VL_CONCAT_QQI(obits, lbits, rbits, ld, rd) \ - (static_cast(ld) << (rbits) | static_cast(rd)) -#define VL_CONCAT_QQQ(obits, lbits, rbits, ld, rd) \ - (static_cast(ld) << (rbits) | static_cast(rd)) - -static inline WDataOutP VL_CONCAT_WII(int obits, int lbits, int rbits, WDataOutP owp, IData ld, - 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); - return owp; -} -static inline WDataOutP VL_CONCAT_WWI(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const 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); - return owp; -} -static inline WDataOutP VL_CONCAT_WIW(int obits, int lbits, int rbits, WDataOutP owp, IData ld, - WDataInP const 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); - 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); - 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); - 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); - return owp; -} -static inline WDataOutP VL_CONCAT_WWQ(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const 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); - return owp; -} -static inline WDataOutP VL_CONCAT_WQW(int obits, int lbits, int rbits, WDataOutP owp, QData ld, - WDataInP const 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); - return owp; -} -static inline WDataOutP VL_CONCAT_WWW(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const lwp, WDataInP const 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); - return owp; -} - -//=================================================================== -// Shifts - -// 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, - IData rd /*1 or 4*/) VL_MT_SAFE { - const int words = VL_WORDS_I(obits); - const EData linsmask = VL_MASK_E(rd); - for (int i = words - 1; i >= 1; --i) { - iowp[i] - = ((iowp[i] << rd) & ~linsmask) | ((iowp[i - 1] >> (VL_EDATASIZE - rd)) & linsmask); - } - iowp[0] = ((iowp[0] << rd) & ~linsmask); - iowp[VL_WORDS_I(obits) - 1] &= VL_MASK_E(obits); -} - -// EMIT_RULE: VL_SHIFTL: oclean=lclean; rclean==clean; -// Important: Unlike most other funcs, the shift might well be a computed -// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?) -static inline WDataOutP VL_SHIFTL_WWI(int obits, int, int, WDataOutP owp, WDataInP const lwp, - IData rd) VL_MT_SAFE { - const int word_shift = VL_BITWORD_E(rd); - const int bit_shift = VL_BITBIT_E(rd); - if (rd >= static_cast(obits)) { // rd may be huge with MSB set - for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - } else if (bit_shift == 0) { // Aligned word shift (<<0,<<32,<<64 etc) - for (int i = 0; i < word_shift; ++i) owp[i] = 0; - 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); - } - return owp; -} -static inline WDataOutP VL_SHIFTL_WWW(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { - for (int i = 1; i < VL_WORDS_I(rbits); ++i) { - if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more - return VL_ZERO_W(obits, owp); - } - } - return VL_SHIFTL_WWI(obits, lbits, 32, owp, lwp, rwp[0]); -} -static inline WDataOutP VL_SHIFTL_WWQ(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const lwp, QData rd) VL_MT_SAFE { - WData rwp[VL_WQ_WORDS_E]; - VL_SET_WQ(rwp, rd); - return VL_SHIFTL_WWW(obits, lbits, rbits, owp, lwp, rwp); -} -static inline IData VL_SHIFTL_IIW(int obits, int, int rbits, IData lhs, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 1; i < VL_WORDS_I(rbits); ++i) { - if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more - return 0; - } - } - return VL_CLEAN_II(obits, obits, lhs << rwp[0]); -} -static inline IData VL_SHIFTL_IIQ(int obits, int, int, IData lhs, QData rhs) VL_MT_SAFE { - if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0; - return VL_CLEAN_II(obits, obits, lhs << rhs); -} -static inline QData VL_SHIFTL_QQW(int obits, int, int rbits, QData lhs, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 1; i < VL_WORDS_I(rbits); ++i) { - if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more - return 0; - } - } - // Above checks rwp[1]==0 so not needed in below shift - return VL_CLEAN_QQ(obits, obits, lhs << (static_cast(rwp[0]))); -} -static inline QData VL_SHIFTL_QQQ(int obits, int, int, QData lhs, QData rhs) VL_MT_SAFE { - if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0; - return VL_CLEAN_QQ(obits, obits, lhs << rhs); -} - -// EMIT_RULE: VL_SHIFTR: oclean=lclean; rclean==clean; -// Important: Unlike most other funcs, the shift might well be a computed -// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?) -static inline WDataOutP VL_SHIFTR_WWI(int obits, int, int, WDataOutP owp, WDataInP const lwp, - IData rd) VL_MT_SAFE { - const int word_shift = VL_BITWORD_E(rd); // Maybe 0 - const int bit_shift = VL_BITBIT_E(rd); - if (rd >= static_cast(obits)) { // rd may be huge with MSB set - for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - } else if (bit_shift == 0) { // Aligned word shift (>>0,>>32,>>64 etc) - const int copy_words = (VL_WORDS_I(obits) - word_shift); - for (int i = 0; i < copy_words; ++i) owp[i] = lwp[i + word_shift]; - for (int i = copy_words; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - } else { - const int loffset = rd & VL_SIZEBITS_E; - const int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword (know - // loffset!=0) Middle words - const int words = VL_WORDS_I(obits - rd); - for (int i = 0; i < words; ++i) { - owp[i] = lwp[i + word_shift] >> loffset; - const int upperword = i + word_shift + 1; - if (upperword < VL_WORDS_I(obits)) owp[i] |= lwp[upperword] << nbitsonright; - } - for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - } - return owp; -} -static inline WDataOutP VL_SHIFTR_WWW(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { - for (int i = 1; i < VL_WORDS_I(rbits); ++i) { - if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more - return VL_ZERO_W(obits, owp); - } - } - return VL_SHIFTR_WWI(obits, lbits, 32, owp, lwp, rwp[0]); -} -static inline WDataOutP VL_SHIFTR_WWQ(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const lwp, QData rd) VL_MT_SAFE { - WData rwp[VL_WQ_WORDS_E]; - VL_SET_WQ(rwp, rd); - return VL_SHIFTR_WWW(obits, lbits, rbits, owp, lwp, rwp); -} - -static inline IData VL_SHIFTR_IIW(int obits, int, int rbits, IData lhs, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 1; i < VL_WORDS_I(rbits); ++i) { - if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more - return 0; - } - } - return VL_CLEAN_II(obits, obits, lhs >> rwp[0]); -} -static inline QData VL_SHIFTR_QQW(int obits, int, int rbits, QData lhs, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 1; i < VL_WORDS_I(rbits); ++i) { - if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more - return 0; - } - } - // Above checks rwp[1]==0 so not needed in below shift - return VL_CLEAN_QQ(obits, obits, lhs >> (static_cast(rwp[0]))); -} -static inline IData VL_SHIFTR_IIQ(int obits, int, int, IData lhs, QData rhs) VL_MT_SAFE { - if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0; - return VL_CLEAN_QQ(obits, obits, lhs >> rhs); -} -static inline QData VL_SHIFTR_QQQ(int obits, int, int, QData lhs, QData rhs) VL_MT_SAFE { - if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0; - return VL_CLEAN_QQ(obits, obits, lhs >> rhs); -} - -// EMIT_RULE: VL_SHIFTRS: oclean=false; lclean=clean, rclean==clean; -static inline IData VL_SHIFTRS_III(int obits, int lbits, int, IData lhs, IData rhs) VL_PURE { - // Note the C standard does not specify the >> operator as a arithmetic shift! - // IEEE says signed if output signed, but bit position from lbits; - // must use lbits for sign; lbits might != obits, - // an EXTEND(SHIFTRS(...)) can became a SHIFTRS(...) within same 32/64 bit word length - const IData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative - const IData signext = ~(VL_MASK_I(lbits) >> rhs); // One with bits where we've shifted "past" - return (lhs >> rhs) | (sign & VL_CLEAN_II(obits, obits, signext)); -} -static inline QData VL_SHIFTRS_QQI(int obits, int lbits, int, QData lhs, IData rhs) VL_PURE { - const QData sign = -(lhs >> (lbits - 1)); - const QData signext = ~(VL_MASK_Q(lbits) >> rhs); - return (lhs >> rhs) | (sign & VL_CLEAN_QQ(obits, obits, signext)); -} -static inline IData VL_SHIFTRS_IQI(int obits, int lbits, int rbits, QData lhs, IData rhs) VL_PURE { - return static_cast(VL_SHIFTRS_QQI(obits, lbits, rbits, lhs, rhs)); -} -static inline WDataOutP VL_SHIFTRS_WWI(int obits, int lbits, int, WDataOutP owp, - WDataInP const lwp, IData rd) VL_MT_SAFE { - const int word_shift = VL_BITWORD_E(rd); - const int bit_shift = VL_BITBIT_E(rd); - const int lmsw = VL_WORDS_I(obits) - 1; - const EData sign = VL_SIGNONES_E(lbits, lwp[lmsw]); - if (rd >= static_cast(obits)) { // Shifting past end, sign in all of lbits - for (int i = 0; i <= lmsw; ++i) owp[i] = sign; - owp[lmsw] &= VL_MASK_E(lbits); - } else if (bit_shift == 0) { // Aligned word shift (>>0,>>32,>>64 etc) - const int copy_words = (VL_WORDS_I(obits) - word_shift); - for (int i = 0; i < copy_words; ++i) owp[i] = lwp[i + word_shift]; - if (copy_words >= 0) owp[copy_words - 1] |= ~VL_MASK_E(obits) & sign; - for (int i = copy_words; i < VL_WORDS_I(obits); ++i) owp[i] = sign; - owp[lmsw] &= VL_MASK_E(lbits); - } else { - const int loffset = rd & VL_SIZEBITS_E; - int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword (know loffset!=0) - // Middle words - const int words = VL_WORDS_I(obits - rd); - for (int i = 0; i < words; ++i) { - owp[i] = lwp[i + word_shift] >> loffset; - const int upperword = i + word_shift + 1; - if (upperword < VL_WORDS_I(obits)) owp[i] |= lwp[upperword] << nbitsonright; - } - if (words) owp[words - 1] |= sign & ~VL_MASK_E(obits - loffset); - for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = sign; - owp[lmsw] &= VL_MASK_E(lbits); - } - return owp; -} -static inline WDataOutP VL_SHIFTRS_WWW(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { - EData overshift = 0; // Huge shift 1>>32 or more - for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i]; - if (VL_UNLIKELY(overshift || rwp[0] >= obits)) { - const int lmsw = VL_WORDS_I(obits) - 1; - const EData sign = VL_SIGNONES_E(lbits, lwp[lmsw]); - for (int j = 0; j <= lmsw; ++j) owp[j] = sign; - owp[lmsw] &= VL_MASK_E(lbits); - return owp; - } - return VL_SHIFTRS_WWI(obits, lbits, 32, owp, lwp, rwp[0]); -} -static inline WDataOutP VL_SHIFTRS_WWQ(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const lwp, QData rd) VL_MT_SAFE { - WData rwp[VL_WQ_WORDS_E]; - VL_SET_WQ(rwp, rd); - return VL_SHIFTRS_WWW(obits, lbits, rbits, owp, lwp, rwp); -} -static inline IData VL_SHIFTRS_IIW(int obits, int lbits, int rbits, IData lhs, - WDataInP const rwp) VL_MT_SAFE { - EData overshift = 0; // Huge shift 1>>32 or more - for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i]; - if (VL_UNLIKELY(overshift || rwp[0] >= obits)) { - const IData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative - return VL_CLEAN_II(obits, obits, sign); - } - return VL_SHIFTRS_III(obits, lbits, 32, lhs, rwp[0]); -} -static inline QData VL_SHIFTRS_QQW(int obits, int lbits, int rbits, QData lhs, - WDataInP const rwp) VL_MT_SAFE { - EData overshift = 0; // Huge shift 1>>32 or more - for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i]; - if (VL_UNLIKELY(overshift || rwp[0] >= obits)) { - const QData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative - return VL_CLEAN_QQ(obits, obits, sign); - } - return VL_SHIFTRS_QQI(obits, lbits, 32, lhs, rwp[0]); -} -static inline IData VL_SHIFTRS_IIQ(int obits, int lbits, int rbits, IData lhs, - QData rhs) VL_MT_SAFE { - WData rwp[VL_WQ_WORDS_E]; - VL_SET_WQ(rwp, rhs); - return VL_SHIFTRS_IIW(obits, lbits, rbits, lhs, rwp); -} -static inline QData VL_SHIFTRS_QQQ(int obits, int lbits, int rbits, QData lhs, QData rhs) VL_PURE { - WData rwp[VL_WQ_WORDS_E]; - VL_SET_WQ(rwp, rhs); - return VL_SHIFTRS_QQW(obits, lbits, rbits, lhs, rwp); -} - -//=================================================================== -// Bit selection - -// EMIT_RULE: VL_BITSEL: oclean=dirty; rclean==clean; -#define VL_BITSEL_IIII(obits, lbits, rbits, zbits, lhs, rhs) ((lhs) >> (rhs)) -#define VL_BITSEL_QIII(obits, lbits, rbits, zbits, lhs, rhs) ((lhs) >> (rhs)) -#define VL_BITSEL_QQII(obits, lbits, rbits, zbits, lhs, rhs) ((lhs) >> (rhs)) -#define VL_BITSEL_IQII(obits, lbits, rbits, zbits, lhs, rhs) (static_cast((lhs) >> (rhs))) - -static inline IData VL_BITSEL_IWII(int, int lbits, int, int, WDataInP const lwp, - IData rd) VL_MT_SAFE { - int word = VL_BITWORD_E(rd); - if (VL_UNLIKELY(rd > static_cast(lbits))) { - return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. - // We return all 1's as that's more likely to find bugs (?) than 0's. - } else { - return (lwp[word] >> VL_BITBIT_E(rd)); - } -} - -// EMIT_RULE: VL_RANGE: oclean=lclean; out=dirty -// & MUST BE CLEAN (currently constant) -#define VL_SEL_IIII(obits, lbits, rbits, tbits, lhs, lsb, width) ((lhs) >> (lsb)) -#define VL_SEL_QQII(obits, lbits, rbits, tbits, lhs, lsb, width) ((lhs) >> (lsb)) -#define VL_SEL_IQII(obits, lbits, rbits, tbits, lhs, lsb, width) \ - (static_cast((lhs) >> (lsb))) - -static inline IData VL_SEL_IWII(int, int lbits, int, int, WDataInP const lwp, IData lsb, - IData width) VL_MT_SAFE { - int msb = lsb + width - 1; - if (VL_UNLIKELY(msb >= lbits)) { - return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. - } else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast(lsb))) { - return VL_BITRSHIFT_W(lwp, lsb); - } else { - // 32 bit extraction may span two words - int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); // bits that come from low word - return ((lwp[VL_BITWORD_E(msb)] << nbitsfromlow) | VL_BITRSHIFT_W(lwp, lsb)); - } -} - -static inline QData VL_SEL_QWII(int, int lbits, int, int, WDataInP const lwp, IData lsb, - IData width) VL_MT_SAFE { - const int msb = lsb + width - 1; - if (VL_UNLIKELY(msb > lbits)) { - return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. - } else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast(lsb))) { - return VL_BITRSHIFT_W(lwp, lsb); - } else if (VL_BITWORD_E(msb) == 1 + VL_BITWORD_E(static_cast(lsb))) { - const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); - const QData hi = (lwp[VL_BITWORD_E(msb)]); - const QData lo = VL_BITRSHIFT_W(lwp, lsb); - return (hi << nbitsfromlow) | lo; - } else { - // 64 bit extraction may span three words - int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); - const QData hi = (lwp[VL_BITWORD_E(msb)]); - const QData mid = (lwp[VL_BITWORD_E(lsb) + 1]); - const QData lo = VL_BITRSHIFT_W(lwp, lsb); - return (hi << (nbitsfromlow + VL_EDATASIZE)) | (mid << nbitsfromlow) | lo; - } -} - -static inline WDataOutP VL_SEL_WWII(int obits, int lbits, int, int, WDataOutP owp, - WDataInP const lwp, IData lsb, IData width) VL_MT_SAFE { - const int msb = lsb + width - 1; - const int word_shift = VL_BITWORD_E(lsb); - if (VL_UNLIKELY(msb > lbits)) { // Outside bounds, - for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) owp[i] = ~0; - owp[VL_WORDS_I(obits) - 1] = VL_MASK_E(obits); - } else if (VL_BITBIT_E(lsb) == 0) { - // 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 - const int loffset = lsb & VL_SIZEBITS_E; - const int nbitsfromlow = VL_EDATASIZE - loffset; // bits that end up in lword (know - // loffset!=0) Middle words - const int words = VL_WORDS_I(msb - lsb + 1); - for (int i = 0; i < words; ++i) { - owp[i] = lwp[i + word_shift] >> loffset; - const int upperword = i + word_shift + 1; - if (upperword <= static_cast(VL_BITWORD_E(msb))) { - owp[i] |= lwp[upperword] << nbitsfromlow; - } - } - for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - } - return owp; -} - -//====================================================================== -// Math needing insert/select - -// Return QData from double (numeric) -// EMIT_RULE: VL_RTOIROUND_Q_D: oclean=dirty; lclean==clean/real -static inline QData VL_RTOIROUND_Q_D(int, double lhs) VL_PURE { - // IEEE format: [63]=sign [62:52]=exp+1023 [51:0]=mantissa - // This does not need to support subnormals as they are sub-integral - lhs = VL_ROUND(lhs); - if (lhs == 0.0) return 0; - const QData q = VL_CVT_Q_D(lhs); - const int lsb = static_cast((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52; - const vluint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52); - vluint64_t out = 0; - if (lsb < 0) { - out = mantissa >> -lsb; - } else if (lsb < 64) { - out = mantissa << lsb; - } - if (lhs < 0) out = -out; - return out; -} -static inline IData VL_RTOIROUND_I_D(int bits, double lhs) VL_PURE { - return static_cast(VL_RTOIROUND_Q_D(bits, lhs)); -} -static inline WDataOutP VL_RTOIROUND_W_D(int obits, WDataOutP owp, double lhs) VL_PURE { - // IEEE format: [63]=sign [62:52]=exp+1023 [51:0]=mantissa - // This does not need to support subnormals as they are sub-integral - lhs = VL_ROUND(lhs); - VL_ZERO_W(obits, owp); - if (lhs == 0.0) return owp; - const QData q = VL_CVT_Q_D(lhs); - const int lsb = static_cast((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52; - const vluint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52); - if (lsb < 0) { - VL_SET_WQ(owp, mantissa >> -lsb); - } else if (lsb < obits) { - _vl_insert_WQ(obits, owp, mantissa, lsb + 52, lsb); - } - if (lhs < 0) VL_NEGATE_INPLACE_W(VL_WORDS_I(obits), owp); - return owp; -} - -//====================================================================== -// Range assignments - -// EMIT_RULE: VL_ASSIGNRANGE: rclean=dirty; -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 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 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 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 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 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 const rwp) -// VL_MT_SAFE { Illegal, as lhs width >= rhs width -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 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 rbits, int obits, int lsb, WDataOutP owp, - WDataInP const rwp) VL_MT_SAFE { - _vl_insert_WW(obits, owp, rwp, lsb + obits - 1, lsb, rbits); -} - -//====================================================================== -// Triops - -static inline WDataOutP VL_COND_WIWW(int obits, int, int, int, WDataOutP owp, int cond, - WDataInP const w1p, WDataInP const w2p) VL_MT_SAFE { - const int words = VL_WORDS_I(obits); - for (int i = 0; i < words; ++i) owp[i] = cond ? w1p[i] : w2p[i]; - return owp; -} - -//====================================================================== -// Constification - -// VL_CONST_W_#X(int obits, WDataOutP owp, IData data0, .... IData data(#-1)) -// Sets wide vector words to specified constant words. -// These macros are used when o might represent more words then are given as constants, -// hence all upper words must be zeroed. -// If changing the number of functions here, also change EMITCINLINES_NUM_CONSTW - -#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; - 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; - 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; - 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; - 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; - 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; - 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; - 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; - 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; - 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; - 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; - 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; - VL_C_END_(obits, VL_WORDS_I(lsb) + 4); -} -static inline WDataOutP VL_CONSTHI_W_5X(int obits, int lsb, WDataOutP obase, - EData d4, - 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; - o[4] = d4; - 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, - 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; - o[4] = d4; o[5] = d5; - 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, - 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; - o[4] = d4; o[5] = d5; o[6] = d6; - 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, - 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; - o[4] = d4; o[5] = d5; o[6] = d6; o[7] = d7; - VL_C_END_(obits, VL_WORDS_I(lsb) + 8); -} - -#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, - EData d7, EData d6, EData d5, EData d4, - 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; o[4] = d4; o[5] = d5; o[6] = d6; o[7] = d7; -} -// clang-format on +#include "verilated_funcs.h" //====================================================================== +#undef VERILATOR_VERILATED_H_INTERNAL_ #endif // Guard diff --git a/include/verilated_cov.cpp b/include/verilated_cov.cpp index 1fe14ba8d..9f04c5429 100644 --- a/include/verilated_cov.cpp +++ b/include/verilated_cov.cpp @@ -132,7 +132,7 @@ private: int valueIndex(const std::string& value) VL_REQUIRES(m_mutex) { const auto iter = m_valueIndexes.find(value); if (iter != m_valueIndexes.end()) return iter->second; - m_nextIndex++; + ++m_nextIndex; assert(m_nextIndex > 0); // Didn't rollover m_valueIndexes.emplace(value, m_nextIndex); m_indexValues.emplace(m_nextIndex, value); @@ -143,8 +143,9 @@ private: std::string rtn; for (const char* pos = text.c_str(); *pos; ++pos) { if (!std::isprint(*pos) || *pos == '%' || *pos == '"') { - char hex[10]; - VL_SNPRINTF(hex, 10, "%%%02X", pos[0]); + constexpr size_t LEN_MAX_HEX = 20; + char hex[LEN_MAX_HEX]; + VL_SNPRINTF(hex, LEN_MAX_HEX, "%%%02X", pos[0]); rtn += hex; } else { rtn += *pos; @@ -165,11 +166,11 @@ private: const std::string& value) VL_PURE { std::string name; if (key.length() == 1 && std::isalpha(key[0])) { - name += std::string("\001") + key; + name += std::string{"\001"} + key; } else { - name += std::string("\001") + dequote(key); + name += std::string{"\001"} + dequote(key); } - name += std::string("\002") + dequote(value); + name += std::string{"\002"} + dequote(value); return name; } static std::string combineHier(const std::string& old, const std::string& add) VL_PURE { @@ -180,31 +181,32 @@ private: if (old.empty()) return add; if (add.empty()) return old; - const char* a = old.c_str(); - const char* b = add.c_str(); + const char* const a = old.c_str(); + const char* const b = add.c_str(); // Scan forward to first mismatch const char* apre = a; const char* bpre = b; while (*apre == *bpre) { - apre++; - bpre++; + ++apre; + ++bpre; } // We used to backup and split on only .'s but it seems better to be verbose // and not assume . is the separator - const std::string prefix = std::string(a, apre - a); + const size_t prefix_len = apre - a; + const std::string prefix = std::string{a, prefix_len}; // Scan backward to last mismatch const char* apost = a + std::strlen(a) - 1; const char* bpost = b + std::strlen(b) - 1; while (*apost == *bpost && apost > apre && bpost > bpre) { - apost--; - bpost--; + --apost; + --bpost; } // Forward to . so we have a whole word - std::string suffix = *bpost ? std::string(bpost + 1) : ""; + std::string suffix = *bpost ? std::string{bpost + 1} : ""; std::string out = prefix + "*" + suffix; @@ -253,17 +255,17 @@ public: // PUBLIC METHODS void forcePerInstance(const bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_forcePerInstance = flag; } void clear() VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; clearGuts(); } void clearNonMatch(const char* const matchp) VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; if (matchp && matchp[0]) { ItemList newlist; for (const auto& itemp : m_items) { @@ -278,24 +280,24 @@ public: } void zero() VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); - const VerilatedLockGuard lock(m_mutex); + 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_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; assert(!m_insertp); m_insertp = itemp; } void insertf(const char* const filenamep, const int lineno) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_insertFilenamep = filenamep; m_insertLineno = lineno; } 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); + const VerilatedLockGuard lock{m_mutex}; assert(m_insertp); // First two key/vals are filename ckeyps[0] = "filename"; @@ -308,7 +310,8 @@ public: while (const char* foundp = std::strchr(fnstartp, '/')) fnstartp = foundp + 1; const char* fnendp = fnstartp; for (; *fnendp && *fnendp != '.'; fnendp++) {} - std::string page_default = "sp_user/" + std::string(fnstartp, fnendp - fnstartp); + const size_t page_len = fnendp - fnstartp; + const std::string page_default = "sp_user/" + std::string{fnstartp, page_len}; ckeyps[2] = "page"; valps[2] = page_default.c_str(); @@ -337,7 +340,7 @@ public: // cout<<" "<<__FUNCTION__<<" "<m_keys[addKeynum] = valueIndex(key); m_insertp->m_vals[addKeynum] = valueIndex(val); - addKeynum++; + ++addKeynum; if (VL_UNCOVERABLE(!legalKey(key))) { std::string msg = ("%Error: Coverage keys of one character, or letter+digit are illegal: " @@ -353,15 +356,15 @@ public: void write(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; #ifndef VM_COVERAGE VL_FATAL_MT("", 0, "", "%Error: Called VerilatedCov::write when VM_COVERAGE disabled\n"); #endif selftest(); - std::ofstream os(filename); + std::ofstream os{filename}; if (os.fail()) { - std::string msg = std::string("%Error: Can't write '") + filename + "'"; + std::string msg = std::string{"%Error: Can't write '"} + filename + "'"; VL_FATAL_MT("", 0, "", msg.c_str()); return; } @@ -435,10 +438,10 @@ void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE { 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)); + impp()->inserti(new VerilatedCoverItemSpec{itemp}); } void VerilatedCovContext::_inserti(vluint64_t* itemp) VL_MT_SAFE { - impp()->inserti(new VerilatedCoverItemSpec(itemp)); + impp()->inserti(new VerilatedCoverItemSpec{itemp}); } void VerilatedCovContext::_insertf(const char* filename, int lineno) VL_MT_SAFE { impp()->insertf(filename, lineno); @@ -511,7 +514,7 @@ VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE { VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE { static VerilatedMutex s_mutex; if (VL_UNLIKELY(!m_coveragep)) { - const VerilatedLockGuard lock(s_mutex); + const VerilatedLockGuard lock{s_mutex}; if (VL_LIKELY(!m_coveragep)) { // Not redundant, prevents race m_coveragep.reset(new VerilatedCovImp); } diff --git a/include/verilated_dpi.cpp b/include/verilated_dpi.cpp index 8ec3fc457..69fbada12 100644 --- a/include/verilated_dpi.cpp +++ b/include/verilated_dpi.cpp @@ -184,7 +184,7 @@ static inline const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHa VL_FATAL_MT(__FILE__, __LINE__, "", "%%Error: DPI svOpenArrayHandle function called with nullptr handle"); } - const VerilatedDpiOpenVar* varp = reinterpret_cast(h); + const VerilatedDpiOpenVar* const varp = reinterpret_cast(h); if (VL_UNLIKELY(!varp->magicOk())) { VL_FATAL_MT(__FILE__, __LINE__, "", "%%Error: DPI svOpenArrayHandle function called with non-Verilator handle"); @@ -205,13 +205,13 @@ int svDimensions(const svOpenArrayHandle h) { return _vl_openhandle_varp(h)->udi // Return pointer to open array data, or nullptr if not in IEEE standard C layout void* svGetArrayPtr(const svOpenArrayHandle h) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h); if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr; return varp->datap(); } // Return size of open array, or 0 if not in IEEE standard C layout int svSizeOfArray(const svOpenArrayHandle h) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h); if (VL_UNLIKELY(!varp->isDpiStdLayout())) return 0; // Truncate 64 bits to int; DPI is limited to 4GB return static_cast(varp->totalSize()); @@ -264,15 +264,15 @@ static void* _vl_svGetArrElemPtr(const svOpenArrayHandle h, int nargs, int indx1 int indx3) VL_MT_SAFE { const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h); if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr; - void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); return datap; } // Copy to user bit array from simulator open array static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s); - void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); if (VL_UNLIKELY(!datap)) return; switch (varp->vltype()) { // LCOV_EXCL_BR_LINE case VLVT_UINT8: d[0] = *(reinterpret_cast(datap)); return; @@ -299,8 +299,8 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, // Copy to user logic array from simulator open array static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s); - void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); if (VL_UNLIKELY(!datap)) return; switch (varp->vltype()) { // LCOV_EXCL_BR_LINE case VLVT_UINT8: @@ -342,8 +342,8 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl // Copy to simulator open array from from user bit array static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d); - void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); if (VL_UNLIKELY(!datap)) return; switch (varp->vltype()) { // LCOV_EXCL_BR_LINE case VLVT_UINT8: *(reinterpret_cast(datap)) = s[0]; return; @@ -364,8 +364,8 @@ static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecV // Copy to simulator open array from from user logic array static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d); - void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); if (VL_UNLIKELY(!datap)) return; switch (varp->vltype()) { // LCOV_EXCL_BR_LINE case VLVT_UINT8: *(reinterpret_cast(datap)) = s[0].aval; return; @@ -386,10 +386,10 @@ static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogic // Return bit from simulator open array static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1, int indx2, - int indx3, int indx4) VL_MT_SAFE { + int indx3, int) VL_MT_SAFE { // One extra index supported, as need bit number - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s); - void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); if (VL_UNLIKELY(!datap)) return 0; switch (varp->vltype()) { // LCOV_EXCL_BR_LINE case VLVT_UINT8: return (*(reinterpret_cast(datap))) & 1; @@ -401,11 +401,11 @@ static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1 } // Update simulator open array from bit static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int nargs, int indx1, - int indx2, int indx3, int indx4) VL_MT_SAFE { + int indx2, int indx3, int) VL_MT_SAFE { // One extra index supported, as need bit number value &= 1; // Make sure clean - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d); - void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); if (VL_UNLIKELY(!datap)) return; switch (varp->vltype()) { // LCOV_EXCL_BR_LINE case VLVT_UINT8: *(reinterpret_cast(datap)) = value; return; @@ -420,7 +420,7 @@ static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int narg // DPI accessors that simply call above functions void* svGetArrElemPtr(const svOpenArrayHandle h, int indx1, ...) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h); void* datap; va_list ap; va_start(ap, indx1); @@ -454,7 +454,7 @@ void* svGetArrElemPtr3(const svOpenArrayHandle h, int indx1, int indx2, int indx } void svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1, ...) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); va_list ap; va_start(ap, indx1); switch (varp->udims()) { @@ -486,7 +486,7 @@ void svPutBitArrElem3VecVal(const svOpenArrayHandle d, const svBitVecVal* s, int _vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3); } void svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1, ...) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); va_list ap; va_start(ap, indx1); switch (varp->udims()) { @@ -522,7 +522,7 @@ void svPutLogicArrElem3VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, // From simulator storage into user space void svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1, ...) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); va_list ap; va_start(ap, indx1); switch (varp->udims()) { @@ -553,7 +553,7 @@ void svGetBitArrElem3VecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1 _vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3); } void svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1, ...) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); va_list ap; va_start(ap, indx1); switch (varp->udims()) { @@ -585,7 +585,7 @@ void svGetLogicArrElem3VecVal(svLogicVecVal* d, const svOpenArrayHandle s, int i } svBit svGetBitArrElem(const svOpenArrayHandle s, int indx1, ...) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); svBit out; va_list ap; va_start(ap, indx1); @@ -618,7 +618,7 @@ svBit svGetBitArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int indx } svLogic svGetLogicArrElem(const svOpenArrayHandle s, int indx1, ...) { // Verilator doesn't support X/Z so can just call Bit version - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); svBit out; va_list ap; va_start(ap, indx1); @@ -654,7 +654,7 @@ svLogic svGetLogicArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int } void svPutBitArrElem(const svOpenArrayHandle d, svBit value, int indx1, ...) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); va_list ap; va_start(ap, indx1); switch (varp->udims()) { @@ -685,7 +685,7 @@ void svPutBitArrElem3(const svOpenArrayHandle d, svBit value, int indx1, int ind } void svPutLogicArrElem(const svOpenArrayHandle d, svLogic value, int indx1, ...) { // Verilator doesn't support X/Z so can just call Bit version - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); va_list ap; va_start(ap, indx1); switch (varp->udims()) { @@ -732,15 +732,15 @@ svScope svGetScope() { } svScope svSetScope(const svScope scope) { - const VerilatedScope* prevScopep = Verilated::dpiScope(); - const VerilatedScope* vscopep = reinterpret_cast(scope); + const VerilatedScope* const prevScopep = Verilated::dpiScope(); + const VerilatedScope* const vscopep = reinterpret_cast(scope); Verilated::dpiScope(vscopep); // NOLINTNEXTLINE(google-readability-casting) return (svScope)(prevScopep); } const char* svGetNameFromScope(const svScope scope) { - const VerilatedScope* vscopep = reinterpret_cast(scope); + const VerilatedScope* const vscopep = reinterpret_cast(scope); return vscopep->name(); } diff --git a/include/verilated_dpi.h b/include/verilated_dpi.h index d86e741fa..c1638f5d1 100644 --- a/include/verilated_dpi.h +++ b/include/verilated_dpi.h @@ -28,7 +28,6 @@ #include "verilatedos.h" #include "verilated.h" // Also presumably included by caller -#include "verilated_heavy.h" // Also presumably included by caller #include "verilated_sym_props.h" #include "svdpi.h" diff --git a/include/verilated_fst_c.cpp b/include/verilated_fst_c.cpp index 9d91029de..73275f9c7 100644 --- a/include/verilated_fst_c.cpp +++ b/include/verilated_fst_c.cpp @@ -53,29 +53,31 @@ //============================================================================= // Check that vltscope_t matches fstScopeType -static_assert((int)FST_ST_VCD_MODULE == (int)VLT_TRACE_SCOPE_MODULE, + +static_assert(static_cast(FST_ST_VCD_MODULE) == static_cast(VLT_TRACE_SCOPE_MODULE), "VLT_TRACE_SCOPE_MODULE mismatches"); -static_assert((int)FST_ST_VCD_TASK == (int)VLT_TRACE_SCOPE_TASK, +static_assert(static_cast(FST_ST_VCD_TASK) == static_cast(VLT_TRACE_SCOPE_TASK), "VLT_TRACE_SCOPE_TASK mismatches"); -static_assert((int)FST_ST_VCD_FUNCTION == (int)VLT_TRACE_SCOPE_FUNCTION, +static_assert(static_cast(FST_ST_VCD_FUNCTION) == static_cast(VLT_TRACE_SCOPE_FUNCTION), "VLT_TRACE_SCOPE_FUNCTION mismatches"); -static_assert((int)FST_ST_VCD_BEGIN == (int)VLT_TRACE_SCOPE_BEGIN, +static_assert(static_cast(FST_ST_VCD_BEGIN) == static_cast(VLT_TRACE_SCOPE_BEGIN), "VLT_TRACE_SCOPE_BEGIN mismatches"); -static_assert((int)FST_ST_VCD_FORK == (int)VLT_TRACE_SCOPE_FORK, +static_assert(static_cast(FST_ST_VCD_FORK) == static_cast(VLT_TRACE_SCOPE_FORK), "VLT_TRACE_SCOPE_FORK mismatches"); -static_assert((int)FST_ST_VCD_GENERATE == (int)VLT_TRACE_SCOPE_GENERATE, +static_assert(static_cast(FST_ST_VCD_GENERATE) == static_cast(VLT_TRACE_SCOPE_GENERATE), "VLT_TRACE_SCOPE_GENERATE mismatches"); -static_assert((int)FST_ST_VCD_STRUCT == (int)VLT_TRACE_SCOPE_STRUCT, +static_assert(static_cast(FST_ST_VCD_STRUCT) == static_cast(VLT_TRACE_SCOPE_STRUCT), "VLT_TRACE_SCOPE_STRUCT mismatches"); -static_assert((int)FST_ST_VCD_UNION == (int)VLT_TRACE_SCOPE_UNION, +static_assert(static_cast(FST_ST_VCD_UNION) == static_cast(VLT_TRACE_SCOPE_UNION), "VLT_TRACE_SCOPE_UNION mismatches"); -static_assert((int)FST_ST_VCD_CLASS == (int)VLT_TRACE_SCOPE_CLASS, +static_assert(static_cast(FST_ST_VCD_CLASS) == static_cast(VLT_TRACE_SCOPE_CLASS), "VLT_TRACE_SCOPE_CLASS mismatches"); -static_assert((int)FST_ST_VCD_INTERFACE == (int)VLT_TRACE_SCOPE_INTERFACE, +static_assert(static_cast(FST_ST_VCD_INTERFACE) + == static_cast(VLT_TRACE_SCOPE_INTERFACE), "VLT_TRACE_SCOPE_INTERFACE mismatches"); -static_assert((int)FST_ST_VCD_PACKAGE == (int)VLT_TRACE_SCOPE_PACKAGE, +static_assert(static_cast(FST_ST_VCD_PACKAGE) == static_cast(VLT_TRACE_SCOPE_PACKAGE), "VLT_TRACE_SCOPE_PACKAGE mismatches"); -static_assert((int)FST_ST_VCD_PROGRAM == (int)VLT_TRACE_SCOPE_PROGRAM, +static_assert(static_cast(FST_ST_VCD_PROGRAM) == static_cast(VLT_TRACE_SCOPE_PROGRAM), "VLT_TRACE_SCOPE_PROGRAM mismatches"); //============================================================================= @@ -98,7 +100,7 @@ VerilatedFst::~VerilatedFst() { } void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(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 @@ -130,14 +132,14 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) { } void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; VerilatedTrace::closeBase(); fstWriterClose(m_fst); m_fst = nullptr; } void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; VerilatedTrace::flushBase(); fstWriterFlushContext(m_fst); } @@ -161,11 +163,11 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV VerilatedTrace::declCode(code, bits, false); - std::istringstream nameiss(name); + std::istringstream nameiss{name}; std::istream_iterator beg(nameiss); std::istream_iterator end; std::list tokens(beg, end); // Split name - std::string symbol_name(tokens.back()); + std::string symbol_name{tokens.back()}; tokens.pop_back(); // Remove symbol name from hierarchy tokens.insert(tokens.begin(), moduleName()); // Add current module to the hierarchy std::string tmpModName; @@ -191,8 +193,8 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV tmpModName = *new_it; tmpModName.pop_back(); // If the scope ends with a non-ascii character, it will be 0x80 + fstScopeType - fstWriterSetScope(m_fst, (fstScopeType)(new_it->back() & 0x7f), tmpModName.c_str(), - nullptr); + fstWriterSetScope(m_fst, static_cast(new_it->back() & 0x7f), + tmpModName.c_str(), nullptr); } else fstWriterSetScope(m_fst, FST_ST_VCD_SCOPE, new_it->c_str(), nullptr); @@ -202,7 +204,7 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV std::stringstream name_ss; name_ss << symbol_name; - if (array) name_ss << "(" << arraynum << ")"; + if (array) name_ss << "[" << arraynum << "]"; std::string name_str = name_ss.str(); if (dtypenum > 0) { diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h new file mode 100644 index 000000000..0c11c6551 --- /dev/null +++ b/include/verilated_funcs.h @@ -0,0 +1,2252 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// Copyright 2003-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 +// +//************************************************************************* +/// +/// \file +/// \brief Verilated common functions +/// +/// verilated.h should be included instead of this file. +/// +/// Those macro/function/variable starting or ending in _ are internal, +/// however many of the other function/macros here are also internal. +/// +//************************************************************************* + +#ifndef VERILATOR_VERILATED_FUNCS_H_ +#define VERILATOR_VERILATED_FUNCS_H_ + +#ifndef VERILATOR_VERILATED_H_INTERNAL_ +#error "verilated_funcs.h should only be included by verilated.h" +#endif + +//========================================================================= +// Extern functions -- User may override -- See verilated.cpp + +/// Routine to call for $finish +/// User code may wish to replace this function, to do so, define VL_USER_FINISH. +/// This code does not have to be thread safe. +/// 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 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. +extern void vl_stop(const char* filename, int linenum, const char* hier); + +/// Routine to call for a couple of fatal messages +/// User code may wish to replace this function, to do so, define VL_USER_FATAL. +/// This code does not have to be thread safe. +/// Verilator internal code must call VL_FINISH_MT instead, which eventually calls this. +extern void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg); + +//========================================================================= +// Extern functions -- Slow path + +/// Multithread safe wrapper for calls to $finish +extern void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE; +/// Multithread safe wrapper for calls to $stop +extern void VL_STOP_MT(const char* filename, int linenum, const char* hier, + bool maybe = true) VL_MT_SAFE; +/// Multithread safe wrapper to call for a couple of fatal messages +extern void VL_FATAL_MT(const char* filename, int linenum, const char* hier, + const char* msg) VL_MT_SAFE; + +// clang-format off +/// Print a string, multithread safe. Eventually VL_PRINTF will get called. +#ifdef VL_THREADED +extern void VL_PRINTF_MT(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE; +#else +# define VL_PRINTF_MT VL_PRINTF // The following parens will take care of themselves +#endif +// clang-format on + +/// Print a debug message from internals with standard prefix, with printf style format +extern void VL_DBG_MSGF(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE; + +inline IData VL_RANDOM_I(int obits) VL_MT_SAFE { return vl_rand64() & VL_MASK_I(obits); } +inline QData VL_RANDOM_Q(int obits) VL_MT_SAFE { return vl_rand64() & VL_MASK_Q(obits); } +#ifndef VL_NO_LEGACY +extern WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp); +#endif +extern IData VL_RANDOM_SEEDED_II(int obits, IData seed) VL_MT_SAFE; +inline IData VL_URANDOM_RANGE_I(IData hi, IData lo) { + vluint64_t rnd = vl_rand64(); + if (VL_LIKELY(hi > lo)) { + // Modulus isn't very fast but it's common that hi-low is power-of-two + return (rnd % (hi - lo + 1)) + lo; + } else { + return (rnd % (lo - hi + 1)) + hi; + } +} + +// These are init time only, so slow is fine +/// Random reset a signal of given width +extern IData VL_RAND_RESET_I(int obits); +/// Random reset a signal of given width +extern QData VL_RAND_RESET_Q(int obits); +/// Random reset a signal of given width +extern WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp); +/// Zero reset a signal (slow - else use VL_ZERO_W) +extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp); + +#if VL_THREADED +/// Return high-precision counter for profiling, or 0x0 if not available +inline QData VL_RDTSC_Q() { + vluint64_t val; + VL_RDTSC(val); + return val; +} +#endif + +extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp, + const VerilatedContext* contextp) VL_MT_SAFE; + +extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP const lwp, WDataInP const rwp, + bool is_modulus); + +extern IData VL_FGETS_IXI(int obits, void* destp, IData fpi); + +extern void VL_FFLUSH_I(IData fdi); +extern IData VL_FSEEK_I(IData fdi, IData offset, IData origin); +extern IData VL_FTELL_I(IData fdi); +extern void VL_FCLOSE_I(IData fdi); + +extern IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi, + IData start, IData count); + +extern void VL_WRITEF(const char* formatp, ...); +extern void VL_FWRITEF(IData fpi, const char* formatp, ...); + +extern IData VL_FSCANF_IX(IData fpi, const char* formatp, ...); +extern IData VL_SSCANF_IIX(int lbits, IData ld, const char* formatp, ...); +extern IData VL_SSCANF_IQX(int lbits, QData ld, const char* formatp, ...); +extern IData VL_SSCANF_IWX(int lbits, WDataInP const lwp, const char* formatp, ...); + +extern void VL_SFORMAT_X(int obits, CData& destr, const char* formatp, ...); +extern void VL_SFORMAT_X(int obits, SData& destr, const char* formatp, ...); +extern void VL_SFORMAT_X(int obits, IData& destr, const char* formatp, ...); +extern void VL_SFORMAT_X(int obits, QData& destr, const char* formatp, ...); +extern void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...); + +extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp); +extern IData VL_SYSTEM_IQ(QData lhs); +inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); } + +extern IData VL_TESTPLUSARGS_I(const char* formatp); +extern const char* vl_mc_scan_plusargs(const char* prefixp); // PLIish + +//========================================================================= +// Base macros + +// Return true if data[bit] set; not 0/1 return, but 0/non-zero return. +#define VL_BITISSET_I(data, bit) ((data) & (VL_UL(1) << VL_BITBIT_I(bit))) +#define VL_BITISSET_Q(data, bit) ((data) & (1ULL << VL_BITBIT_Q(bit))) +#define VL_BITISSET_E(data, bit) ((data) & (VL_EUL(1) << VL_BITBIT_E(bit))) +#define VL_BITISSET_W(data, bit) ((data)[VL_BITWORD_E(bit)] & (VL_EUL(1) << VL_BITBIT_E(bit))) +#define VL_BITISSETLIMIT_W(data, width, bit) (((bit) < (width)) && VL_BITISSET_W(data, bit)) + +// Shift appropriate word by bit. Does not account for wrapping between two words +#define VL_BITRSHIFT_W(data, bit) ((data)[VL_BITWORD_E(bit)] >> VL_BITBIT_E(bit)) + +// Create two 32-bit words from quadword +// WData is always at least 2 words; does not clean upper bits +#define VL_SET_WQ(owp, data) \ + do { \ + (owp)[0] = static_cast(data); \ + (owp)[1] = static_cast((data) >> VL_EDATASIZE); \ + } while (false) +#define VL_SET_WI(owp, data) \ + do { \ + (owp)[0] = static_cast(data); \ + (owp)[1] = 0; \ + } while (false) +#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)) + +// Return FILE* from IData +extern FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE; + +// clang-format off +// Use a union to avoid cast-to-different-size warnings +// Return void* from QData +static inline void* VL_CVT_Q_VP(QData lhs) VL_PURE { + union { void* fp; QData q; } u; + u.q = lhs; + return u.fp; +} +// Return QData from const void* +static inline QData VL_CVT_VP_Q(const void* fp) VL_PURE { + union { const void* fp; QData q; } u; + u.q = 0; + u.fp = fp; + return u.q; +} +// Return double from QData (bits, not numerically) +static inline double VL_CVT_D_Q(QData lhs) VL_PURE { + union { double d; QData q; } u; + u.q = lhs; + return u.d; +} +// Return QData from double (bits, not numerically) +static inline QData VL_CVT_Q_D(double lhs) VL_PURE { + union { double d; QData q; } u; + u.d = lhs; + return u.q; +} +// clang-format on + +// Return double from lhs (numeric) unsigned +double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE; +static inline double VL_ITOR_D_I(int, IData lhs) VL_PURE { + return static_cast(static_cast(lhs)); +} +static inline double VL_ITOR_D_Q(int, QData lhs) VL_PURE { + return static_cast(static_cast(lhs)); +} +// Return double from lhs (numeric) signed +double VL_ISTOR_D_W(int lbits, WDataInP const lwp) VL_PURE; +static inline double VL_ISTOR_D_I(int lbits, IData lhs) VL_PURE { + if (lbits == 32) return static_cast(static_cast(lhs)); + VlWide lwp; + VL_SET_WI(lwp, lhs); + return VL_ISTOR_D_W(lbits, lwp); +} +static inline double VL_ISTOR_D_Q(int lbits, QData lhs) VL_PURE { + if (lbits == 64) return static_cast(static_cast(lhs)); + VlWide lwp; + VL_SET_WQ(lwp, lhs); + return VL_ISTOR_D_W(lbits, lwp); +} +// Return QData from double (numeric) +static inline IData VL_RTOI_I_D(double lhs) VL_PURE { + return static_cast(VL_TRUNC(lhs)); +} + +// Sign extend such that if MSB set, we get ffff_ffff, else 0s +// (Requires clean input) +#define VL_SIGN_I(nbits, lhs) ((lhs) >> VL_BITBIT_I((nbits)-VL_UL(1))) +#define VL_SIGN_Q(nbits, lhs) ((lhs) >> VL_BITBIT_Q((nbits)-1ULL)) +#define VL_SIGN_E(nbits, lhs) ((lhs) >> VL_BITBIT_E((nbits)-VL_EUL(1))) +#define VL_SIGN_W(nbits, rwp) \ + ((rwp)[VL_BITWORD_E((nbits)-VL_EUL(1))] >> VL_BITBIT_E((nbits)-VL_EUL(1))) +#define VL_SIGNONES_E(nbits, lhs) (-(VL_SIGN_E(nbits, lhs))) + +// Sign bit extended up to MSB, doesn't include unsigned portion +// Optimization bug in GCC 3.3 returns different bitmasks to later states for +static inline IData VL_EXTENDSIGN_I(int lbits, IData lhs) VL_PURE { + return (-((lhs) & (VL_UL(1) << (lbits - 1)))); +} +static inline QData VL_EXTENDSIGN_Q(int lbits, QData lhs) VL_PURE { + return (-((lhs) & (1ULL << (lbits - 1)))); +} + +// Debugging prints +extern void _vl_debug_print_w(int lbits, WDataInP const iwp); + +//========================================================================= +// Pli macros + +extern int VL_TIME_STR_CONVERT(const char* strp) VL_PURE; + +// These are deprecated and used only to establish the default precision/units. +// Use Verilator timescale-override for better control. +// clang-format off +#ifndef VL_TIME_PRECISION +# ifdef VL_TIME_PRECISION_STR +# define VL_TIME_PRECISION VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR)) +# else +# define VL_TIME_PRECISION (-12) ///< Timescale default units if not in Verilog - picoseconds +# endif +#endif +#ifndef VL_TIME_UNIT +# ifdef VL_TIME_UNIT_STR +# define VL_TIME_UNIT VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR)) +# else +# define VL_TIME_UNIT (-12) ///< Timescale default units if not in Verilog - picoseconds +# endif +#endif + +#if defined(SYSTEMC_VERSION) +/// Return current simulation time +// Already defined: extern sc_time sc_time_stamp(); +inline vluint64_t vl_time_stamp64() { return sc_time_stamp().value(); } +#else // Non-SystemC +# 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 + +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. +// Can't use multiply in Q flavor, as might lose precision +#define VL_TIME_UNITED_Q(scale) (VL_TIME_Q() / static_cast(scale)) +#define VL_TIME_UNITED_D(scale) (VL_TIME_D() / static_cast(scale)) + +// Return time precision as multiplier of time units +double vl_time_multiplier(int scale) VL_PURE; +// Return power of 10. e.g. returns 100 if n==2 +vluint64_t vl_time_pow10(int n) VL_PURE; + +#ifdef VL_DEBUG +/// Evaluate statement if Verilated::debug() enabled +# define VL_DEBUG_IF(stmt) \ + do { \ + if (VL_UNLIKELY(Verilated::debug())) {stmt} \ + } while (false) +#else +// We intentionally do not compile the stmt to improve compile speed +# define VL_DEBUG_IF(stmt) do {} while (false) +#endif + +// clang-format on + +//========================================================================= +// Functional macros/routines +// These all take the form +// VL_func_IW(bits, bits, op, op) +// VL_func_WW(bits, bits, out, op, op) +// The I/W indicates if it's a integer or wide for the output and each operand. +// The bits indicate the bit width of the output and each operand. +// If wide output, a temporary storage location is specified. + +//=================================================================== +// SETTING OPERATORS + +// Output clean +// EMIT_RULE: VL_CLEAN: oclean=clean; obits=lbits; +#define VL_CLEAN_II(obits, lbits, lhs) ((lhs)&VL_MASK_I(obits)) +#define VL_CLEAN_QQ(obits, lbits, lhs) ((lhs)&VL_MASK_Q(obits)) + +// 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 { + const int words = VL_WORDS_I(obits); + owp[words - 1] &= VL_MASK_E(obits); + return owp; +} +static inline WDataOutP VL_CLEAN_WW(int obits, int, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { + const 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_E(obits); + return owp; +} +static inline WDataOutP VL_ZERO_W(int obits, WDataOutP owp) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + for (int i = 0; i < words; ++i) owp[i] = 0; + return owp; +} +static inline WDataOutP VL_ALLONES_W(int obits, WDataOutP owp) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + for (int i = 0; i < (words - 1); ++i) owp[i] = ~VL_EUL(0); + owp[words - 1] = VL_MASK_E(obits); + return owp; +} + +// EMIT_RULE: VL_ASSIGN: oclean=rclean; obits==lbits; +// For now, we always have a clean rhs. +// Note: If a ASSIGN isn't clean, use VL_ASSIGNCLEAN instead to do the same thing. +static inline WDataOutP VL_ASSIGN_W(int obits, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + for (int i = 0; i < words; ++i) owp[i] = lwp[i]; + return owp; +} + +// EMIT_RULE: VL_ASSIGNBIT: rclean=clean; +static inline void VL_ASSIGNBIT_II(int, int bit, CData& lhsr, IData rhs) VL_PURE { + lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_II(int, int bit, SData& lhsr, IData rhs) VL_PURE { + lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_II(int, int bit, IData& lhsr, IData rhs) VL_PURE { + lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_QI(int, int bit, QData& lhsr, QData rhs) VL_PURE { + lhsr = ((lhsr & ~(1ULL << VL_BITBIT_Q(bit))) | (static_cast(rhs) << VL_BITBIT_Q(bit))); +} +static inline void VL_ASSIGNBIT_WI(int, int bit, WDataOutP owp, IData rhs) VL_MT_SAFE { + EData orig = owp[VL_BITWORD_E(bit)]; + owp[VL_BITWORD_E(bit)] = ((orig & ~(VL_EUL(1) << VL_BITBIT_E(bit))) + | (static_cast(rhs) << VL_BITBIT_E(bit))); +} +// Alternative form that is an instruction faster when rhs is constant one. +static inline void VL_ASSIGNBIT_IO(int, int bit, CData& lhsr, IData) VL_PURE { + lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_IO(int, int bit, SData& lhsr, IData) VL_PURE { + lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_IO(int, int bit, IData& lhsr, IData) VL_PURE { + lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_QO(int, int bit, QData& lhsr, IData) VL_PURE { + lhsr = (lhsr | (1ULL << VL_BITBIT_Q(bit))); +} +static inline void VL_ASSIGNBIT_WO(int, int bit, WDataOutP owp, IData) VL_MT_SAFE { + const EData orig = owp[VL_BITWORD_E(bit)]; + owp[VL_BITWORD_E(bit)] = (orig | (VL_EUL(1) << VL_BITBIT_E(bit))); +} + +//=================================================================== +// SYSTEMC OPERATORS +// Copying verilog format to systemc integers and bit vectors. +// Get a SystemC variable + +#define VL_ASSIGN_ISI(obits, vvar, svar) \ + { (vvar) = VL_CLEAN_II((obits), (obits), (svar).read()); } +#define VL_ASSIGN_QSQ(obits, vvar, svar) \ + { (vvar) = VL_CLEAN_QQ((obits), (obits), (svar).read()); } + +#define VL_ASSIGN_ISW(obits, od, svar) \ + { (od) = ((svar).read().get_word(0)) & VL_MASK_I(obits); } +#define VL_ASSIGN_QSW(obits, od, svar) \ + { \ + (od) = ((static_cast((svar).read().get_word(1))) << VL_IDATASIZE \ + | (svar).read().get_word(0)) \ + & VL_MASK_Q(obits); \ + } +#define VL_ASSIGN_WSW(obits, owp, svar) \ + { \ + const int words = VL_WORDS_I(obits); \ + for (int i = 0; i < words; ++i) (owp)[i] = (svar).read().get_word(i); \ + (owp)[words - 1] &= VL_MASK_E(obits); \ + } + +#define VL_ASSIGN_ISU(obits, vvar, svar) \ + { (vvar) = VL_CLEAN_II((obits), (obits), (svar).read().to_uint()); } +#define VL_ASSIGN_QSU(obits, vvar, svar) \ + { (vvar) = VL_CLEAN_QQ((obits), (obits), (svar).read().to_uint64()); } +#define VL_ASSIGN_WSB(obits, owp, svar) \ + { \ + const int words = VL_WORDS_I(obits); \ + sc_biguint<(obits)> _butemp = (svar).read(); \ + for (int i = 0; i < words; ++i) { \ + int msb = ((i + 1) * VL_IDATASIZE) - 1; \ + msb = (msb >= (obits)) ? ((obits)-1) : msb; \ + (owp)[i] = _butemp.range(msb, i * VL_IDATASIZE).to_uint(); \ + } \ + (owp)[words - 1] &= VL_MASK_E(obits); \ + } + +// Copying verilog format from systemc integers and bit vectors. +// Set a SystemC variable + +#define VL_ASSIGN_SII(obits, svar, vvar) \ + { (svar).write(vvar); } +#define VL_ASSIGN_SQQ(obits, svar, vvar) \ + { (svar).write(vvar); } + +#define VL_ASSIGN_SWI(obits, svar, rd) \ + { \ + sc_bv<(obits)> _bvtemp; \ + _bvtemp.set_word(0, (rd)); \ + (svar).write(_bvtemp); \ + } +#define VL_ASSIGN_SWQ(obits, svar, rd) \ + { \ + sc_bv<(obits)> _bvtemp; \ + _bvtemp.set_word(0, static_cast(rd)); \ + _bvtemp.set_word(1, static_cast((rd) >> VL_IDATASIZE)); \ + (svar).write(_bvtemp); \ + } +#define VL_ASSIGN_SWW(obits, svar, rwp) \ + { \ + sc_bv<(obits)> _bvtemp; \ + for (int i = 0; i < VL_WORDS_I(obits); ++i) _bvtemp.set_word(i, (rwp)[i]); \ + (svar).write(_bvtemp); \ + } + +#define VL_ASSIGN_SUI(obits, svar, rd) \ + { (svar).write(rd); } +#define VL_ASSIGN_SUQ(obits, svar, rd) \ + { (svar).write(rd); } +#define VL_ASSIGN_SBI(obits, svar, rd) \ + { (svar).write(rd); } +#define VL_ASSIGN_SBQ(obits, svar, rd) \ + { (svar).write(rd); } +#define VL_ASSIGN_SBW(obits, svar, rwp) \ + { \ + sc_biguint<(obits)> _butemp; \ + for (int i = 0; i < VL_WORDS_I(obits); ++i) { \ + int msb = ((i + 1) * VL_IDATASIZE) - 1; \ + msb = (msb >= (obits)) ? ((obits)-1) : msb; \ + _butemp.range(msb, i* VL_IDATASIZE) = (rwp)[i]; \ + } \ + (svar).write(_butemp); \ + } + +//=================================================================== +// Extending sizes + +// CAREFUL, we're width changing, so obits!=lbits + +// Right must be clean because otherwise size increase would pick up bad bits +// EMIT_RULE: VL_EXTEND: oclean=clean; rclean==clean; +#define VL_EXTEND_II(obits, lbits, lhs) ((lhs)) +#define VL_EXTEND_QI(obits, lbits, lhs) (static_cast(lhs)) +#define VL_EXTEND_QQ(obits, lbits, lhs) ((lhs)) + +static inline WDataOutP VL_EXTEND_WI(int obits, int, WDataOutP owp, IData ld) VL_MT_SAFE { + // Note for extracts that obits != lbits + owp[0] = ld; + for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + return owp; +} +static inline WDataOutP VL_EXTEND_WQ(int obits, int, WDataOutP owp, QData ld) VL_MT_SAFE { + VL_SET_WQ(owp, ld); + for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + return owp; +} +static inline WDataOutP VL_EXTEND_WW(int obits, int lbits, WDataOutP owp, + WDataInP const lwp) VL_MT_SAFE { + for (int i = 0; i < VL_WORDS_I(lbits); ++i) owp[i] = lwp[i]; + for (int i = VL_WORDS_I(lbits); i < VL_WORDS_I(obits); ++i) owp[i] = 0; + return owp; +} + +// EMIT_RULE: VL_EXTENDS: oclean=*dirty*; obits=lbits; +// Sign extension; output dirty +static inline IData VL_EXTENDS_II(int, int lbits, IData lhs) VL_PURE { + return VL_EXTENDSIGN_I(lbits, lhs) | lhs; +} +static inline QData VL_EXTENDS_QI(int, int lbits, QData lhs /*Q_as_need_extended*/) VL_PURE { + return VL_EXTENDSIGN_Q(lbits, lhs) | lhs; +} +static inline QData VL_EXTENDS_QQ(int, int lbits, QData lhs) VL_PURE { + return VL_EXTENDSIGN_Q(lbits, lhs) | lhs; +} + +static inline WDataOutP VL_EXTENDS_WI(int obits, int lbits, WDataOutP owp, IData ld) VL_MT_SAFE { + const EData sign = VL_SIGNONES_E(lbits, static_cast(ld)); + owp[0] = ld | (sign & ~VL_MASK_E(lbits)); + for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = sign; + return owp; +} +static inline WDataOutP VL_EXTENDS_WQ(int obits, int lbits, WDataOutP owp, QData ld) VL_MT_SAFE { + VL_SET_WQ(owp, ld); + const EData sign = VL_SIGNONES_E(lbits, owp[1]); + owp[1] |= sign & ~VL_MASK_E(lbits); + for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = sign; + return owp; +} +static inline WDataOutP VL_EXTENDS_WW(int obits, int lbits, WDataOutP owp, + WDataInP const lwp) VL_MT_SAFE { + for (int i = 0; i < VL_WORDS_I(lbits) - 1; ++i) owp[i] = lwp[i]; + const int lmsw = VL_WORDS_I(lbits) - 1; + const EData sign = VL_SIGNONES_E(lbits, lwp[lmsw]); + owp[lmsw] = lwp[lmsw] | (sign & ~VL_MASK_E(lbits)); + for (int i = VL_WORDS_I(lbits); i < VL_WORDS_I(obits); ++i) owp[i] = sign; + return owp; +} + +//=================================================================== +// REDUCTION OPERATORS + +// EMIT_RULE: VL_REDAND: oclean=clean; lclean==clean; obits=1; +#define VL_REDAND_II(obits, lbits, lhs) ((lhs) == VL_MASK_I(lbits)) +#define VL_REDAND_IQ(obits, lbits, lhs) ((lhs) == VL_MASK_Q(lbits)) +static inline IData VL_REDAND_IW(int, int lbits, WDataInP const lwp) VL_MT_SAFE { + const int words = VL_WORDS_I(lbits); + EData combine = lwp[0]; + for (int i = 1; i < words - 1; ++i) combine &= lwp[i]; + combine &= ~VL_MASK_E(lbits) | lwp[words - 1]; + return ((~combine) == 0); +} + +// EMIT_RULE: VL_REDOR: oclean=clean; lclean==clean; obits=1; +#define VL_REDOR_I(lhs) ((lhs) != 0) +#define VL_REDOR_Q(lhs) ((lhs) != 0) +static inline IData VL_REDOR_W(int words, WDataInP const lwp) VL_MT_SAFE { + EData equal = 0; + for (int i = 0; i < words; ++i) equal |= lwp[i]; + return (equal != 0); +} + +// EMIT_RULE: VL_REDXOR: oclean=dirty; obits=1; +static inline IData VL_REDXOR_2(IData r) VL_PURE { + // Experiments show VL_REDXOR_2 is faster than __builtin_parityl + r = (r ^ (r >> 1)); + return r; +} +static inline IData VL_REDXOR_4(IData r) VL_PURE { +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return __builtin_parityl(r); +#else + r = (r ^ (r >> 1)); + r = (r ^ (r >> 2)); + return r; +#endif +} +static inline IData VL_REDXOR_8(IData r) VL_PURE { +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return __builtin_parityl(r); +#else + r = (r ^ (r >> 1)); + r = (r ^ (r >> 2)); + r = (r ^ (r >> 4)); + return r; +#endif +} +static inline IData VL_REDXOR_16(IData r) VL_PURE { +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return __builtin_parityl(r); +#else + r = (r ^ (r >> 1)); + r = (r ^ (r >> 2)); + r = (r ^ (r >> 4)); + r = (r ^ (r >> 8)); + return r; +#endif +} +static inline IData VL_REDXOR_32(IData r) VL_PURE { +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return __builtin_parityl(r); +#else + r = (r ^ (r >> 1)); + r = (r ^ (r >> 2)); + r = (r ^ (r >> 4)); + r = (r ^ (r >> 8)); + r = (r ^ (r >> 16)); + return r; +#endif +} +static inline IData VL_REDXOR_64(QData r) VL_PURE { +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return __builtin_parityll(r); +#else + r = (r ^ (r >> 1)); + r = (r ^ (r >> 2)); + r = (r ^ (r >> 4)); + r = (r ^ (r >> 8)); + r = (r ^ (r >> 16)); + r = (r ^ (r >> 32)); + return static_cast(r); +#endif +} +static inline IData VL_REDXOR_W(int words, WDataInP const lwp) VL_MT_SAFE { + EData r = lwp[0]; + for (int i = 1; i < words; ++i) r ^= lwp[i]; + return VL_REDXOR_32(r); +} + +// EMIT_RULE: VL_COUNTONES_II: oclean = false; lhs clean +static inline IData VL_COUNTONES_I(IData lhs) VL_PURE { + // This is faster than __builtin_popcountl + IData r = lhs - ((lhs >> 1) & 033333333333) - ((lhs >> 2) & 011111111111); + r = (r + (r >> 3)) & 030707070707; + r = (r + (r >> 6)); + r = (r + (r >> 12) + (r >> 24)) & 077; + return r; +} +static inline IData VL_COUNTONES_Q(QData lhs) VL_PURE { + return VL_COUNTONES_I(static_cast(lhs)) + VL_COUNTONES_I(static_cast(lhs >> 32)); +} +#define VL_COUNTONES_E VL_COUNTONES_I +static inline IData VL_COUNTONES_W(int words, WDataInP const lwp) VL_MT_SAFE { + EData r = 0; + for (int i = 0; i < words; ++i) r += VL_COUNTONES_E(lwp[i]); + return r; +} + +// EMIT_RULE: VL_COUNTBITS_II: oclean = false; lhs clean +static inline IData VL_COUNTBITS_I(int lbits, IData lhs, IData ctrl0, IData ctrl1, + IData ctrl2) VL_PURE { + int ctrlSum = (ctrl0 & 0x1) + (ctrl1 & 0x1) + (ctrl2 & 0x1); + if (ctrlSum == 3) { + return VL_COUNTONES_I(lhs); + } else if (ctrlSum == 0) { + IData mask = (lbits == 32) ? -1 : ((1 << lbits) - 1); + return VL_COUNTONES_I(~lhs & mask); + } else { + return (lbits == 32) ? 32 : lbits; + } +} +static inline IData VL_COUNTBITS_Q(int lbits, QData lhs, IData ctrl0, IData ctrl1, + IData ctrl2) VL_PURE { + return VL_COUNTBITS_I(32, static_cast(lhs), ctrl0, ctrl1, ctrl2) + + VL_COUNTBITS_I(lbits - 32, static_cast(lhs >> 32), ctrl0, ctrl1, ctrl2); +} +#define VL_COUNTBITS_E VL_COUNTBITS_I +static inline IData VL_COUNTBITS_W(int lbits, int words, WDataInP const lwp, IData ctrl0, + IData ctrl1, IData ctrl2) VL_MT_SAFE { + EData r = 0; + IData wordLbits = 32; + for (int i = 0; i < words; ++i) { + if (i == words - 1) wordLbits = lbits % 32; + r += VL_COUNTBITS_E(wordLbits, lwp[i], ctrl0, ctrl1, ctrl2); + } + return r; +} + +static inline IData VL_ONEHOT_I(IData lhs) VL_PURE { + return (((lhs & (lhs - 1)) == 0) & (lhs != 0)); +} +static inline IData VL_ONEHOT_Q(QData lhs) VL_PURE { + return (((lhs & (lhs - 1)) == 0) & (lhs != 0)); +} +static inline IData VL_ONEHOT_W(int words, WDataInP const lwp) VL_MT_SAFE { + EData one = 0; + for (int i = 0; (i < words); ++i) { + if (lwp[i]) { + if (one) return 0; + one = 1; + if (lwp[i] & (lwp[i] - 1)) return 0; + } + } + return one; +} + +static inline IData VL_ONEHOT0_I(IData lhs) VL_PURE { return ((lhs & (lhs - 1)) == 0); } +static inline IData VL_ONEHOT0_Q(QData lhs) VL_PURE { return ((lhs & (lhs - 1)) == 0); } +static inline IData VL_ONEHOT0_W(int words, WDataInP const lwp) VL_MT_SAFE { + bool one = false; + for (int i = 0; (i < words); ++i) { + if (lwp[i]) { + if (one) return 0; + one = true; + if (lwp[i] & (lwp[i] - 1)) return 0; + } + } + return 1; +} + +static inline IData VL_CLOG2_I(IData lhs) VL_PURE { + // There are faster algorithms, or fls GCC4 builtins, but rarely used + if (VL_UNLIKELY(!lhs)) return 0; + --lhs; + int shifts = 0; + for (; lhs != 0; ++shifts) lhs = lhs >> 1; + return shifts; +} +static inline IData VL_CLOG2_Q(QData lhs) VL_PURE { + if (VL_UNLIKELY(!lhs)) return 0; + --lhs; + int shifts = 0; + for (; lhs != 0; ++shifts) lhs = lhs >> 1ULL; + return shifts; +} +static inline IData VL_CLOG2_W(int words, WDataInP const lwp) VL_MT_SAFE { + EData adjust = (VL_COUNTONES_W(words, lwp) == 1) ? 0 : 1; + for (int i = words - 1; i >= 0; --i) { + if (VL_UNLIKELY(lwp[i])) { // Shorter worst case if predict not taken + for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) { + if (VL_UNLIKELY(VL_BITISSET_E(lwp[i], bit))) { + return i * VL_EDATASIZE + bit + adjust; + } + } + // Can't get here - one bit must be set + } + } + return 0; +} + +static inline IData VL_MOSTSETBITP1_W(int words, WDataInP const lwp) VL_MT_SAFE { + // MSB set bit plus one; similar to FLS. 0=value is zero + for (int i = words - 1; i >= 0; --i) { + if (VL_UNLIKELY(lwp[i])) { // Shorter worst case if predict not taken + for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) { + if (VL_UNLIKELY(VL_BITISSET_E(lwp[i], bit))) return i * VL_EDATASIZE + bit + 1; + } + // Can't get here - one bit must be set + } + } + return 0; +} + +//=================================================================== +// SIMPLE LOGICAL OPERATORS + +// EMIT_RULE: VL_AND: oclean=lclean||rclean; obits=lbits; lbits==rbits; +static inline WDataOutP VL_AND_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] & rwp[i]); + return owp; +} +// EMIT_RULE: VL_OR: oclean=lclean&&rclean; obits=lbits; lbits==rbits; +static inline WDataOutP VL_OR_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] | rwp[i]); + return owp; +} +// EMIT_RULE: VL_CHANGEXOR: oclean=1; obits=32; lbits==rbits; +static inline IData VL_CHANGEXOR_W(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + IData od = 0; + for (int i = 0; (i < words); ++i) od |= (lwp[i] ^ rwp[i]); + return od; +} +// EMIT_RULE: VL_XOR: oclean=lclean&&rclean; obits=lbits; lbits==rbits; +static inline WDataOutP VL_XOR_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] ^ rwp[i]); + return owp; +} +// EMIT_RULE: VL_NOT: oclean=dirty; obits=lbits; +static inline WDataOutP VL_NOT_W(int words, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { + for (int i = 0; i < words; ++i) owp[i] = ~(lwp[i]); + return owp; +} + +//========================================================================= +// Logical comparisons + +// EMIT_RULE: VL_EQ: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_NEQ: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_LT: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_GT: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// 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) + +// Output clean, AND MUST BE CLEAN +static inline IData VL_EQ_W(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + EData nequal = 0; + for (int i = 0; (i < words); ++i) nequal |= (lwp[i] ^ rwp[i]); + return (nequal == 0); +} + +// Internal usage +static inline int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const 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; + } + 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) + +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. + // GCC 3.3.4 sign extension bugs on AMD64 architecture force us to use quad logic + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc + return lhs_signed > rhs_signed; +} +static inline IData VL_GTS_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed > rhs_signed; +} + +static inline IData VL_GTES_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc + return lhs_signed >= rhs_signed; +} +static inline IData VL_GTES_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed >= rhs_signed; +} + +static inline IData VL_LTS_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc + return lhs_signed < rhs_signed; +} +static inline IData VL_LTS_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed < rhs_signed; +} + +static inline IData VL_LTES_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc + return lhs_signed <= rhs_signed; +} +static inline IData VL_LTES_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed <= rhs_signed; +} + +static inline int _vl_cmps_w(int lbits, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + const int words = VL_WORDS_I(lbits); + int i = words - 1; + // We need to flip sense if negative comparison + const EData lsign = VL_SIGN_E(lbits, lwp[i]); + const EData rsign = VL_SIGN_E(lbits, rwp[i]); + if (!lsign && rsign) return 1; // + > - + if (lsign && !rsign) return -1; // - < + + for (; i >= 0; --i) { + if (lwp[i] > rwp[i]) return 1; + if (lwp[i] < rwp[i]) return -1; + } + return 0; // == +} + +//========================================================================= +// Math + +// Output NOT clean +static inline WDataOutP VL_NEGATE_W(int words, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { + EData carry = 1; + for (int i = 0; i < words; ++i) { + owp[i] = ~lwp[i] + carry; + carry = (owp[i] < ~lwp[i]); + } + return owp; +} +static inline void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE { + EData carry = 1; + for (int i = 0; i < words; ++i) { + EData word = ~owp_lwp[i] + carry; + carry = (word < ~owp_lwp[i]); + owp_lwp[i] = word; + } +} + +// EMIT_RULE: VL_MUL: oclean=dirty; lclean==clean; rclean==clean; +// EMIT_RULE: VL_DIV: oclean=dirty; lclean==clean; rclean==clean; +// EMIT_RULE: VL_MODDIV: oclean=dirty; lclean==clean; rclean==clean; +#define VL_DIV_III(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) / (rhs)) +#define VL_DIV_QQQ(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) / (rhs)) +#define VL_DIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 0)) +#define VL_MODDIV_III(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) % (rhs)) +#define VL_MODDIV_QQQ(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) % (rhs)) +#define VL_MODDIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 1)) + +static inline WDataOutP VL_ADD_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + QData carry = 0; + for (int i = 0; i < words; ++i) { + carry = carry + static_cast(lwp[i]) + static_cast(rwp[i]); + owp[i] = (carry & 0xffffffffULL); + carry = (carry >> 32ULL) & 0xffffffffULL; + } + // Last output word is dirty + return owp; +} + +static inline WDataOutP VL_SUB_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + QData carry = 0; + for (int i = 0; i < words; ++i) { + carry = (carry + static_cast(lwp[i]) + + static_cast(static_cast(~rwp[i]))); + if (i == 0) ++carry; // Negation of rwp + owp[i] = (carry & 0xffffffffULL); + carry = (carry >> 32ULL) & 0xffffffffULL; + } + // Last output word is dirty + return owp; +} + +static inline WDataOutP VL_MUL_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 0; i < words; ++i) owp[i] = 0; + for (int lword = 0; lword < words; ++lword) { + for (int rword = 0; rword < words; ++rword) { + QData mul = static_cast(lwp[lword]) * static_cast(rwp[rword]); + for (int qword = lword + rword; qword < words; ++qword) { + mul += static_cast(owp[qword]); + owp[qword] = (mul & 0xffffffffULL); + mul = (mul >> 32ULL) & 0xffffffffULL; + } + } + } + // Last output word is dirty + return owp; +} + +static inline IData VL_MULS_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { + const vlsint32_t lhs_signed = VL_EXTENDS_II(32, lbits, lhs); + const vlsint32_t rhs_signed = VL_EXTENDS_II(32, lbits, rhs); + return lhs_signed * rhs_signed; +} +static inline QData VL_MULS_QQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed * rhs_signed; +} + +static inline WDataOutP VL_MULS_WWW(int, int lbits, int, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + const int words = VL_WORDS_I(lbits); + // cppcheck-suppress variableScope + WData lwstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here + // cppcheck-suppress variableScope + WData rwstore[VL_MULS_MAX_WORDS]; + WDataInP lwusp = lwp; + WDataInP rwusp = rwp; + EData lneg = VL_SIGN_E(lbits, lwp[words - 1]); + if (lneg) { // Negate lhs + lwusp = lwstore; + VL_NEGATE_W(words, lwstore, lwp); + lwstore[words - 1] &= VL_MASK_E(lbits); // Clean it + } + EData rneg = VL_SIGN_E(lbits, rwp[words - 1]); + if (rneg) { // Negate rhs + rwusp = rwstore; + VL_NEGATE_W(words, rwstore, rwp); + rwstore[words - 1] &= VL_MASK_E(lbits); // Clean it + } + VL_MUL_W(words, owp, lwusp, rwusp); + owp[words - 1] &= VL_MASK_E( + lbits); // Clean. Note it's ok for the multiply to overflow into the sign bit + if ((lneg ^ rneg) & 1) { // Negate output (not using NEGATE, as owp==lwp) + QData carry = 0; + for (int i = 0; i < words; ++i) { + carry = carry + static_cast(static_cast(~owp[i])); + if (i == 0) ++carry; // Negation of temp2 + owp[i] = (carry & 0xffffffffULL); + carry = (carry >> 32ULL) & 0xffffffffULL; + } + // Not needed: owp[words-1] |= 1< 0) power = power * power; + if (rhs & (1ULL << i)) out *= power; + } + return out; +} +static inline QData VL_POW_QQQ(int, int, int rbits, QData lhs, QData rhs) VL_PURE { + if (VL_UNLIKELY(rhs == 0)) return 1; + if (VL_UNLIKELY(lhs == 0)) return 0; + QData power = lhs; + QData out = 1ULL; + for (int i = 0; i < rbits; ++i) { + if (i > 0) power = power * power; + if (rhs & (1ULL << i)) out *= power; + } + return out; +} +WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp); +WDataOutP VL_POW_WWQ(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, QData rhs); +QData VL_POW_QQW(int obits, int, int rbits, QData lhs, WDataInP const rwp); + +#define VL_POWSS_IIQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) \ + VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) +#define VL_POWSS_IIQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) \ + VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) +#define VL_POWSS_IIW(obits, lbits, rbits, lhs, rwp, lsign, rsign) \ + VL_POWSS_QQW(obits, lbits, rbits, lhs, rwp, lsign, rsign) +#define VL_POWSS_QQI(obits, lbits, rbits, lhs, rhs, lsign, rsign) \ + VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) +#define VL_POWSS_WWI(obits, lbits, rbits, owp, lwp, rhs, lsign, rsign) \ + VL_POWSS_WWQ(obits, lbits, rbits, owp, lwp, rhs, lsign, rsign) + +static inline IData VL_POWSS_III(int obits, int, int rbits, IData lhs, IData rhs, bool lsign, + bool rsign) VL_MT_SAFE { + if (VL_UNLIKELY(rhs == 0)) return 1; + if (rsign && VL_SIGN_I(rbits, rhs)) { + if (lhs == 0) { + return 0; // "X" + } else if (lhs == 1) { + return 1; + } else if (lsign && lhs == VL_MASK_I(obits)) { // -1 + if (rhs & 1) { + return VL_MASK_I(obits); // -1^odd=-1 + } else { + return 1; // -1^even=1 + } + } + return 0; + } + return VL_POW_III(obits, rbits, rbits, lhs, rhs); +} +static inline QData VL_POWSS_QQQ(int obits, int, int rbits, QData lhs, QData rhs, bool lsign, + bool rsign) VL_MT_SAFE { + if (VL_UNLIKELY(rhs == 0)) return 1; + if (rsign && VL_SIGN_Q(rbits, rhs)) { + if (lhs == 0) { + return 0; // "X" + } else if (lhs == 1) { + return 1; + } else if (lsign && lhs == VL_MASK_Q(obits)) { // -1 + if (rhs & 1) { + return VL_MASK_Q(obits); // -1^odd=-1 + } else { + return 1; // -1^even=1 + } + } + return 0; + } + return VL_POW_QQQ(obits, rbits, rbits, lhs, rhs); +} +WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp, bool lsign, bool rsign); +WDataOutP VL_POWSS_WWQ(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, QData rhs, + bool lsign, bool rsign); +QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, WDataInP const rwp, bool lsign, + bool rsign); + +//=================================================================== +// Concat/replication + +// 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, + int rbits) VL_PURE { + const IData cleanmask = VL_MASK_I(rbits); + const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; + lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); +} +static inline void _vl_insert_II(int, SData& lhsr, IData ld, int hbit, int lbit, + int rbits) VL_PURE { + const IData cleanmask = VL_MASK_I(rbits); + const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; + lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); +} +static inline void _vl_insert_II(int, IData& lhsr, IData ld, int hbit, int lbit, + int rbits) VL_PURE { + const IData cleanmask = VL_MASK_I(rbits); + const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; + lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); +} +static inline void _vl_insert_QQ(int, QData& lhsr, QData ld, int hbit, int lbit, + int rbits) VL_PURE { + const QData cleanmask = VL_MASK_Q(rbits); + const QData insmask = (VL_MASK_Q(hbit - lbit + 1)) << lbit; + lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); +} +static inline void _vl_insert_WI(int, WDataOutP owp, IData ld, int hbit, int lbit, + int rbits = 0) VL_MT_SAFE { + const int hoffset = VL_BITBIT_E(hbit); + const int loffset = VL_BITBIT_E(lbit); + const int roffset = VL_BITBIT_E(rbits); + const int hword = VL_BITWORD_E(hbit); + const int lword = VL_BITWORD_E(lbit); + const int rword = VL_BITWORD_E(rbits); + const 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 & cleanmask; + } else { + const EData lde = static_cast(ld); + if (hword == lword) { // know < EData bits because above checks it + // Assignment is contained within one word of destination + const EData insmask = (VL_MASK_E(hoffset - loffset + 1)) << loffset; + owp[lword] = (owp[lword] & ~insmask) | ((lde << loffset) & (insmask & cleanmask)); + } else { + // Assignment crosses a word boundary in destination + const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << 0; + const EData linsmask = (VL_MASK_E((VL_EDATASIZE - 1) - loffset + 1)) << loffset; + const 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 & 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 const lwp, int hbit, int lbit, + int rbits = 0) VL_MT_SAFE { + const int hoffset = VL_BITBIT_E(hbit); + const int loffset = VL_BITBIT_E(lbit); + const int roffset = VL_BITBIT_E(rbits); + const int lword = VL_BITWORD_E(lbit); + const int hword = VL_BITWORD_E(hbit); + const int rword = VL_BITWORD_E(rbits); + const 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. + const 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 - 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 + const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)); + owp[hword] = (owp[hword] & ~hinsmask) | (lwp[words - 1] & (hinsmask & cleanmask)); + } else { + const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << 0; + const EData linsmask = (VL_MASK_E((VL_EDATASIZE - 1) - loffset + 1)) << loffset; + const int nbitsonright + = VL_EDATASIZE - loffset; // bits that end up in lword (know loffset!=0) + // Middle words + for (int i = 0; i < words; ++i) { + { // Lower word + const int oword = lword + i; + const EData d = lwp[i] << loffset; + const EData od = (owp[oword] & ~linsmask) | (d & linsmask); + if (oword == hword) { + owp[oword] = (owp[oword] & ~hinsmask) | (od & (hinsmask & cleanmask)); + } else { + owp[oword] = od; + } + } + { // Upper word + const int oword = lword + i + 1; + if (oword <= hword) { + const EData d = lwp[i] >> nbitsonright; + const EData od = (d & ~linsmask) | (owp[oword] & linsmask); + if (oword == hword) { + owp[oword] = (owp[oword] & ~hinsmask) | (od & (hinsmask & cleanmask)); + } else { + owp[oword] = od; + } + } + } + } + } +} + +static inline void _vl_insert_WQ(int obits, WDataOutP owp, QData ld, int hbit, int lbit, + int rbits = 0) VL_MT_SAFE { + VlWide lwp; + VL_SET_WQ(lwp, ld); + _vl_insert_WW(obits, owp, lwp, hbit, lbit, rbits); +} + +// EMIT_RULE: VL_REPLICATE: oclean=clean>width32, dirty<=width32; lclean=clean; rclean==clean; +// RHS MUST BE CLEAN CONSTANT. +#define VL_REPLICATE_IOI(obits, lbits, rbits, ld, rep) (-(ld)) // Iff lbits==1 +#define VL_REPLICATE_QOI(obits, lbits, rbits, ld, rep) (-(static_cast(ld))) // Iff lbits==1 + +static inline IData VL_REPLICATE_III(int, int lbits, int, IData ld, IData rep) VL_PURE { + IData returndata = ld; + for (unsigned i = 1; i < rep; ++i) { + returndata = returndata << lbits; + returndata |= ld; + } + return returndata; +} +static inline QData VL_REPLICATE_QII(int, int lbits, int, IData ld, IData rep) VL_PURE { + QData returndata = ld; + for (unsigned i = 1; i < rep; ++i) { + returndata = returndata << lbits; + returndata |= static_cast(ld); + } + return returndata; +} +static inline WDataOutP VL_REPLICATE_WII(int obits, int lbits, int, WDataOutP owp, IData ld, + 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); + } + return owp; +} +static inline WDataOutP VL_REPLICATE_WQI(int obits, int lbits, int, WDataOutP owp, QData ld, + 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); + } + return owp; +} +static inline WDataOutP VL_REPLICATE_WWI(int obits, int lbits, int, WDataOutP owp, + WDataInP const lwp, 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); + } + return owp; +} + +// Left stream operator. Output will always be clean. LHS and RHS must be clean. +// Special "fast" versions for slice sizes that are a power of 2. These use +// shifts and masks to execute faster than the slower for-loop approach where a +// subset of bits is copied in during each iteration. +static inline IData VL_STREAML_FAST_III(int, int lbits, int, IData ld, IData rd_log2) VL_PURE { + // Pre-shift bits in most-significant slice: + // + // If lbits is not a multiple of the slice size (i.e., lbits % rd != 0), + // then we end up with a "gap" in our reversed result. For example, if we + // have a 5-bit Verlilog signal (lbits=5) in an 8-bit C data type: + // + // ld = ---43210 + // + // (where numbers are the Verilog signal bit numbers and '-' is an unused bit). + // Executing the switch statement below with a slice size of two (rd=2, + // rd_log2=1) produces: + // + // ret = 1032-400 + // + // Pre-shifting the bits in the most-significant slice allows us to avoid + // this gap in the shuffled data: + // + // ld_adjusted = --4-3210 + // ret = 10324--- + IData ret = ld; + if (rd_log2) { + const vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); // max multiple of rd <= lbits + const vluint32_t lbitsRem = lbits - lbitsFloor; // number of bits in most-sig slice (MSS) + const IData msbMask = VL_MASK_I(lbitsRem) << lbitsFloor; // mask to sel only bits in MSS + ret = (ret & ~msbMask) | ((ret & msbMask) << ((VL_UL(1) << rd_log2) - lbitsRem)); + } + switch (rd_log2) { + case 0: ret = ((ret >> 1) & VL_UL(0x55555555)) | ((ret & VL_UL(0x55555555)) << 1); // FALLTHRU + case 1: ret = ((ret >> 2) & VL_UL(0x33333333)) | ((ret & VL_UL(0x33333333)) << 2); // FALLTHRU + case 2: ret = ((ret >> 4) & VL_UL(0x0f0f0f0f)) | ((ret & VL_UL(0x0f0f0f0f)) << 4); // FALLTHRU + case 3: ret = ((ret >> 8) & VL_UL(0x00ff00ff)) | ((ret & VL_UL(0x00ff00ff)) << 8); // FALLTHRU + case 4: ret = ((ret >> 16) | (ret << 16)); // FALLTHRU + default:; + } + return ret >> (VL_IDATASIZE - lbits); +} + +static inline QData VL_STREAML_FAST_QQI(int, int lbits, int, QData ld, IData rd_log2) VL_PURE { + // Pre-shift bits in most-significant slice (see comment in VL_STREAML_FAST_III) + QData ret = ld; + if (rd_log2) { + const vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); + const vluint32_t lbitsRem = lbits - lbitsFloor; + const QData msbMask = VL_MASK_Q(lbitsRem) << lbitsFloor; + ret = (ret & ~msbMask) | ((ret & msbMask) << ((1ULL << rd_log2) - lbitsRem)); + } + switch (rd_log2) { + case 0: + ret = (((ret >> 1) & 0x5555555555555555ULL) + | ((ret & 0x5555555555555555ULL) << 1)); // FALLTHRU + case 1: + ret = (((ret >> 2) & 0x3333333333333333ULL) + | ((ret & 0x3333333333333333ULL) << 2)); // FALLTHRU + case 2: + ret = (((ret >> 4) & 0x0f0f0f0f0f0f0f0fULL) + | ((ret & 0x0f0f0f0f0f0f0f0fULL) << 4)); // FALLTHRU + case 3: + ret = (((ret >> 8) & 0x00ff00ff00ff00ffULL) + | ((ret & 0x00ff00ff00ff00ffULL) << 8)); // FALLTHRU + case 4: + ret = (((ret >> 16) & 0x0000ffff0000ffffULL) + | ((ret & 0x0000ffff0000ffffULL) << 16)); // FALLTHRU + case 5: ret = ((ret >> 32) | (ret << 32)); // FALLTHRU + default:; + } + return ret >> (VL_QUADSIZE - lbits); +} + +// Regular "slow" streaming operators +static inline IData VL_STREAML_III(int, int lbits, int, IData ld, IData rd) VL_PURE { + IData ret = 0; + // Slice size should never exceed the lhs width + const IData mask = VL_MASK_I(rd); + for (int istart = 0; istart < lbits; istart += rd) { + int ostart = lbits - rd - istart; + ostart = ostart > 0 ? ostart : 0; + ret |= ((ld >> istart) & mask) << ostart; + } + return ret; +} + +static inline QData VL_STREAML_QQI(int, int lbits, int, QData ld, IData rd) VL_PURE { + QData ret = 0; + // Slice size should never exceed the lhs width + const QData mask = VL_MASK_Q(rd); + for (int istart = 0; istart < lbits; istart += rd) { + int ostart = lbits - rd - istart; + ostart = ostart > 0 ? ostart : 0; + ret |= ((ld >> istart) & mask) << ostart; + } + return ret; +} + +static inline WDataOutP VL_STREAML_WWI(int, int lbits, int, WDataOutP owp, WDataInP const lwp, + IData rd) VL_MT_SAFE { + VL_ZERO_W(lbits, owp); + // Slice size should never exceed the lhs width + const int ssize = (rd < static_cast(lbits)) ? rd : (static_cast(lbits)); + for (int istart = 0; istart < lbits; istart += rd) { + int ostart = lbits - rd - istart; + ostart = ostart > 0 ? ostart : 0; + for (int sbit = 0; sbit < ssize && sbit < lbits - istart; ++sbit) { + // Extract a single bit from lwp and shift it to the correct + // location for owp. + EData bit = (VL_BITRSHIFT_W(lwp, (istart + sbit)) & 1) << VL_BITBIT_E(ostart + sbit); + owp[VL_BITWORD_E(ostart + sbit)] |= bit; + } + } + return owp; +} + +// Because concats are common and wide, it's valuable to always have a clean output. +// Thus we specify inputs must be clean, so we don't need to clean the output. +// Note the bit shifts are always constants, so the adds in these constify out. +// Casts required, as args may be 8 bit entities, and need to shift to appropriate output size +#define VL_CONCAT_III(obits, lbits, rbits, ld, rd) \ + (static_cast(ld) << (rbits) | static_cast(rd)) +#define VL_CONCAT_QII(obits, lbits, rbits, ld, rd) \ + (static_cast(ld) << (rbits) | static_cast(rd)) +#define VL_CONCAT_QIQ(obits, lbits, rbits, ld, rd) \ + (static_cast(ld) << (rbits) | static_cast(rd)) +#define VL_CONCAT_QQI(obits, lbits, rbits, ld, rd) \ + (static_cast(ld) << (rbits) | static_cast(rd)) +#define VL_CONCAT_QQQ(obits, lbits, rbits, ld, rd) \ + (static_cast(ld) << (rbits) | static_cast(rd)) + +static inline WDataOutP VL_CONCAT_WII(int obits, int lbits, int rbits, WDataOutP owp, IData ld, + 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); + return owp; +} +static inline WDataOutP VL_CONCAT_WWI(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const 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); + return owp; +} +static inline WDataOutP VL_CONCAT_WIW(int obits, int lbits, int rbits, WDataOutP owp, IData ld, + WDataInP const 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); + 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); + 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); + 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); + return owp; +} +static inline WDataOutP VL_CONCAT_WWQ(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const 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); + return owp; +} +static inline WDataOutP VL_CONCAT_WQW(int obits, int lbits, int rbits, WDataOutP owp, QData ld, + WDataInP const 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); + return owp; +} +static inline WDataOutP VL_CONCAT_WWW(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, WDataInP const 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); + return owp; +} + +//=================================================================== +// Shifts + +// 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, + IData rd /*1 or 4*/) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + const EData linsmask = VL_MASK_E(rd); + for (int i = words - 1; i >= 1; --i) { + iowp[i] + = ((iowp[i] << rd) & ~linsmask) | ((iowp[i - 1] >> (VL_EDATASIZE - rd)) & linsmask); + } + iowp[0] = ((iowp[0] << rd) & ~linsmask); + iowp[VL_WORDS_I(obits) - 1] &= VL_MASK_E(obits); +} + +// EMIT_RULE: VL_SHIFTL: oclean=lclean; rclean==clean; +// Important: Unlike most other funcs, the shift might well be a computed +// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?) +static inline WDataOutP VL_SHIFTL_WWI(int obits, int, int, WDataOutP owp, WDataInP const lwp, + IData rd) VL_MT_SAFE { + const int word_shift = VL_BITWORD_E(rd); + const int bit_shift = VL_BITBIT_E(rd); + if (rd >= static_cast(obits)) { // rd may be huge with MSB set + for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + } else if (bit_shift == 0) { // Aligned word shift (<<0,<<32,<<64 etc) + for (int i = 0; i < word_shift; ++i) owp[i] = 0; + 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); + } + return owp; +} +static inline WDataOutP VL_SHIFTL_WWW(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more + return VL_ZERO_W(obits, owp); + } + } + return VL_SHIFTL_WWI(obits, lbits, 32, owp, lwp, rwp[0]); +} +static inline WDataOutP VL_SHIFTL_WWQ(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, QData rd) VL_MT_SAFE { + VlWide rwp; + VL_SET_WQ(rwp, rd); + return VL_SHIFTL_WWW(obits, lbits, rbits, owp, lwp, rwp); +} +static inline IData VL_SHIFTL_IIW(int obits, int, int rbits, IData lhs, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more + return 0; + } + } + return VL_CLEAN_II(obits, obits, lhs << rwp[0]); +} +static inline IData VL_SHIFTL_IIQ(int obits, int, int, IData lhs, QData rhs) VL_MT_SAFE { + if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0; + return VL_CLEAN_II(obits, obits, lhs << rhs); +} +static inline QData VL_SHIFTL_QQW(int obits, int, int rbits, QData lhs, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more + return 0; + } + } + // Above checks rwp[1]==0 so not needed in below shift + return VL_CLEAN_QQ(obits, obits, lhs << (static_cast(rwp[0]))); +} +static inline QData VL_SHIFTL_QQQ(int obits, int, int, QData lhs, QData rhs) VL_MT_SAFE { + if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0; + return VL_CLEAN_QQ(obits, obits, lhs << rhs); +} + +// EMIT_RULE: VL_SHIFTR: oclean=lclean; rclean==clean; +// Important: Unlike most other funcs, the shift might well be a computed +// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?) +static inline WDataOutP VL_SHIFTR_WWI(int obits, int, int, WDataOutP owp, WDataInP const lwp, + IData rd) VL_MT_SAFE { + const int word_shift = VL_BITWORD_E(rd); // Maybe 0 + const int bit_shift = VL_BITBIT_E(rd); + if (rd >= static_cast(obits)) { // rd may be huge with MSB set + for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + } else if (bit_shift == 0) { // Aligned word shift (>>0,>>32,>>64 etc) + const int copy_words = (VL_WORDS_I(obits) - word_shift); + for (int i = 0; i < copy_words; ++i) owp[i] = lwp[i + word_shift]; + for (int i = copy_words; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + } else { + const int loffset = rd & VL_SIZEBITS_E; + const int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword (know + // loffset!=0) Middle words + const int words = VL_WORDS_I(obits - rd); + for (int i = 0; i < words; ++i) { + owp[i] = lwp[i + word_shift] >> loffset; + const int upperword = i + word_shift + 1; + if (upperword < VL_WORDS_I(obits)) owp[i] |= lwp[upperword] << nbitsonright; + } + for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + } + return owp; +} +static inline WDataOutP VL_SHIFTR_WWW(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more + return VL_ZERO_W(obits, owp); + } + } + return VL_SHIFTR_WWI(obits, lbits, 32, owp, lwp, rwp[0]); +} +static inline WDataOutP VL_SHIFTR_WWQ(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, QData rd) VL_MT_SAFE { + VlWide rwp; + VL_SET_WQ(rwp, rd); + return VL_SHIFTR_WWW(obits, lbits, rbits, owp, lwp, rwp); +} + +static inline IData VL_SHIFTR_IIW(int obits, int, int rbits, IData lhs, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more + return 0; + } + } + return VL_CLEAN_II(obits, obits, lhs >> rwp[0]); +} +static inline QData VL_SHIFTR_QQW(int obits, int, int rbits, QData lhs, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more + return 0; + } + } + // Above checks rwp[1]==0 so not needed in below shift + return VL_CLEAN_QQ(obits, obits, lhs >> (static_cast(rwp[0]))); +} +static inline IData VL_SHIFTR_IIQ(int obits, int, int, IData lhs, QData rhs) VL_MT_SAFE { + if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0; + return VL_CLEAN_QQ(obits, obits, lhs >> rhs); +} +static inline QData VL_SHIFTR_QQQ(int obits, int, int, QData lhs, QData rhs) VL_MT_SAFE { + if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0; + return VL_CLEAN_QQ(obits, obits, lhs >> rhs); +} + +// EMIT_RULE: VL_SHIFTRS: oclean=false; lclean=clean, rclean==clean; +static inline IData VL_SHIFTRS_III(int obits, int lbits, int, IData lhs, IData rhs) VL_PURE { + // Note the C standard does not specify the >> operator as a arithmetic shift! + // IEEE says signed if output signed, but bit position from lbits; + // must use lbits for sign; lbits might != obits, + // an EXTEND(SHIFTRS(...)) can became a SHIFTRS(...) within same 32/64 bit word length + const IData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative + const IData signext = ~(VL_MASK_I(lbits) >> rhs); // One with bits where we've shifted "past" + return (lhs >> rhs) | (sign & VL_CLEAN_II(obits, obits, signext)); +} +static inline QData VL_SHIFTRS_QQI(int obits, int lbits, int, QData lhs, IData rhs) VL_PURE { + const QData sign = -(lhs >> (lbits - 1)); + const QData signext = ~(VL_MASK_Q(lbits) >> rhs); + return (lhs >> rhs) | (sign & VL_CLEAN_QQ(obits, obits, signext)); +} +static inline IData VL_SHIFTRS_IQI(int obits, int lbits, int rbits, QData lhs, IData rhs) VL_PURE { + return static_cast(VL_SHIFTRS_QQI(obits, lbits, rbits, lhs, rhs)); +} +static inline WDataOutP VL_SHIFTRS_WWI(int obits, int lbits, int, WDataOutP owp, + WDataInP const lwp, IData rd) VL_MT_SAFE { + const int word_shift = VL_BITWORD_E(rd); + const int bit_shift = VL_BITBIT_E(rd); + const int lmsw = VL_WORDS_I(obits) - 1; + const EData sign = VL_SIGNONES_E(lbits, lwp[lmsw]); + if (rd >= static_cast(obits)) { // Shifting past end, sign in all of lbits + for (int i = 0; i <= lmsw; ++i) owp[i] = sign; + owp[lmsw] &= VL_MASK_E(lbits); + } else if (bit_shift == 0) { // Aligned word shift (>>0,>>32,>>64 etc) + const int copy_words = (VL_WORDS_I(obits) - word_shift); + for (int i = 0; i < copy_words; ++i) owp[i] = lwp[i + word_shift]; + if (copy_words >= 0) owp[copy_words - 1] |= ~VL_MASK_E(obits) & sign; + for (int i = copy_words; i < VL_WORDS_I(obits); ++i) owp[i] = sign; + owp[lmsw] &= VL_MASK_E(lbits); + } else { + const int loffset = rd & VL_SIZEBITS_E; + int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword (know loffset!=0) + // Middle words + const int words = VL_WORDS_I(obits - rd); + for (int i = 0; i < words; ++i) { + owp[i] = lwp[i + word_shift] >> loffset; + const int upperword = i + word_shift + 1; + if (upperword < VL_WORDS_I(obits)) owp[i] |= lwp[upperword] << nbitsonright; + } + if (words) owp[words - 1] |= sign & ~VL_MASK_E(obits - loffset); + for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = sign; + owp[lmsw] &= VL_MASK_E(lbits); + } + return owp; +} +static inline WDataOutP VL_SHIFTRS_WWW(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + EData overshift = 0; // Huge shift 1>>32 or more + for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i]; + if (VL_UNLIKELY(overshift || rwp[0] >= obits)) { + const int lmsw = VL_WORDS_I(obits) - 1; + const EData sign = VL_SIGNONES_E(lbits, lwp[lmsw]); + for (int j = 0; j <= lmsw; ++j) owp[j] = sign; + owp[lmsw] &= VL_MASK_E(lbits); + return owp; + } + return VL_SHIFTRS_WWI(obits, lbits, 32, owp, lwp, rwp[0]); +} +static inline WDataOutP VL_SHIFTRS_WWQ(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, QData rd) VL_MT_SAFE { + VlWide rwp; + VL_SET_WQ(rwp, rd); + return VL_SHIFTRS_WWW(obits, lbits, rbits, owp, lwp, rwp); +} +static inline IData VL_SHIFTRS_IIW(int obits, int lbits, int rbits, IData lhs, + WDataInP const rwp) VL_MT_SAFE { + EData overshift = 0; // Huge shift 1>>32 or more + for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i]; + if (VL_UNLIKELY(overshift || rwp[0] >= obits)) { + const IData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative + return VL_CLEAN_II(obits, obits, sign); + } + return VL_SHIFTRS_III(obits, lbits, 32, lhs, rwp[0]); +} +static inline QData VL_SHIFTRS_QQW(int obits, int lbits, int rbits, QData lhs, + WDataInP const rwp) VL_MT_SAFE { + EData overshift = 0; // Huge shift 1>>32 or more + for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i]; + if (VL_UNLIKELY(overshift || rwp[0] >= obits)) { + const QData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative + return VL_CLEAN_QQ(obits, obits, sign); + } + return VL_SHIFTRS_QQI(obits, lbits, 32, lhs, rwp[0]); +} +static inline IData VL_SHIFTRS_IIQ(int obits, int lbits, int rbits, IData lhs, + QData rhs) VL_MT_SAFE { + VlWide rwp; + VL_SET_WQ(rwp, rhs); + return VL_SHIFTRS_IIW(obits, lbits, rbits, lhs, rwp); +} +static inline QData VL_SHIFTRS_QQQ(int obits, int lbits, int rbits, QData lhs, QData rhs) VL_PURE { + VlWide rwp; + VL_SET_WQ(rwp, rhs); + return VL_SHIFTRS_QQW(obits, lbits, rbits, lhs, rwp); +} + +//=================================================================== +// Bit selection + +// EMIT_RULE: VL_BITSEL: oclean=dirty; rclean==clean; +#define VL_BITSEL_IIII(obits, lbits, rbits, zbits, lhs, rhs) ((lhs) >> (rhs)) +#define VL_BITSEL_QIII(obits, lbits, rbits, zbits, lhs, rhs) ((lhs) >> (rhs)) +#define VL_BITSEL_QQII(obits, lbits, rbits, zbits, lhs, rhs) ((lhs) >> (rhs)) +#define VL_BITSEL_IQII(obits, lbits, rbits, zbits, lhs, rhs) (static_cast((lhs) >> (rhs))) + +static inline IData VL_BITSEL_IWII(int, int lbits, int, int, WDataInP const lwp, + IData rd) VL_MT_SAFE { + int word = VL_BITWORD_E(rd); + if (VL_UNLIKELY(rd > static_cast(lbits))) { + return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. + // We return all 1's as that's more likely to find bugs (?) than 0's. + } else { + return (lwp[word] >> VL_BITBIT_E(rd)); + } +} + +// EMIT_RULE: VL_RANGE: oclean=lclean; out=dirty +// & MUST BE CLEAN (currently constant) +#define VL_SEL_IIII(obits, lbits, rbits, tbits, lhs, lsb, width) ((lhs) >> (lsb)) +#define VL_SEL_QQII(obits, lbits, rbits, tbits, lhs, lsb, width) ((lhs) >> (lsb)) +#define VL_SEL_IQII(obits, lbits, rbits, tbits, lhs, lsb, width) \ + (static_cast((lhs) >> (lsb))) + +static inline IData VL_SEL_IWII(int, int lbits, int, int, WDataInP const lwp, IData lsb, + IData width) VL_MT_SAFE { + int msb = lsb + width - 1; + if (VL_UNLIKELY(msb >= lbits)) { + return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. + } else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast(lsb))) { + return VL_BITRSHIFT_W(lwp, lsb); + } else { + // 32 bit extraction may span two words + int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); // bits that come from low word + return ((lwp[VL_BITWORD_E(msb)] << nbitsfromlow) | VL_BITRSHIFT_W(lwp, lsb)); + } +} + +static inline QData VL_SEL_QWII(int, int lbits, int, int, WDataInP const lwp, IData lsb, + IData width) VL_MT_SAFE { + const int msb = lsb + width - 1; + if (VL_UNLIKELY(msb > lbits)) { + return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. + } else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast(lsb))) { + return VL_BITRSHIFT_W(lwp, lsb); + } else if (VL_BITWORD_E(msb) == 1 + VL_BITWORD_E(static_cast(lsb))) { + const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); + const QData hi = (lwp[VL_BITWORD_E(msb)]); + const QData lo = VL_BITRSHIFT_W(lwp, lsb); + return (hi << nbitsfromlow) | lo; + } else { + // 64 bit extraction may span three words + int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); + const QData hi = (lwp[VL_BITWORD_E(msb)]); + const QData mid = (lwp[VL_BITWORD_E(lsb) + 1]); + const QData lo = VL_BITRSHIFT_W(lwp, lsb); + return (hi << (nbitsfromlow + VL_EDATASIZE)) | (mid << nbitsfromlow) | lo; + } +} + +static inline WDataOutP VL_SEL_WWII(int obits, int lbits, int, int, WDataOutP owp, + WDataInP const lwp, IData lsb, IData width) VL_MT_SAFE { + const int msb = lsb + width - 1; + const int word_shift = VL_BITWORD_E(lsb); + if (VL_UNLIKELY(msb > lbits)) { // Outside bounds, + for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) owp[i] = ~0; + owp[VL_WORDS_I(obits) - 1] = VL_MASK_E(obits); + } else if (VL_BITBIT_E(lsb) == 0) { + // 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 + const int loffset = lsb & VL_SIZEBITS_E; + const int nbitsfromlow = VL_EDATASIZE - loffset; // bits that end up in lword (know + // loffset!=0) Middle words + const int words = VL_WORDS_I(msb - lsb + 1); + for (int i = 0; i < words; ++i) { + owp[i] = lwp[i + word_shift] >> loffset; + const int upperword = i + word_shift + 1; + if (upperword <= static_cast(VL_BITWORD_E(msb))) { + owp[i] |= lwp[upperword] << nbitsfromlow; + } + } + for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + } + return owp; +} + +//====================================================================== +// Math needing insert/select + +// Return QData from double (numeric) +// EMIT_RULE: VL_RTOIROUND_Q_D: oclean=dirty; lclean==clean/real +static inline QData VL_RTOIROUND_Q_D(int, double lhs) VL_PURE { + // IEEE format: [63]=sign [62:52]=exp+1023 [51:0]=mantissa + // This does not need to support subnormals as they are sub-integral + lhs = VL_ROUND(lhs); + if (lhs == 0.0) return 0; + const QData q = VL_CVT_Q_D(lhs); + const int lsb = static_cast((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52; + const vluint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52); + vluint64_t out = 0; + if (lsb < 0) { + out = mantissa >> -lsb; + } else if (lsb < 64) { + out = mantissa << lsb; + } + if (lhs < 0) out = -out; + return out; +} +static inline IData VL_RTOIROUND_I_D(int bits, double lhs) VL_PURE { + return static_cast(VL_RTOIROUND_Q_D(bits, lhs)); +} +static inline WDataOutP VL_RTOIROUND_W_D(int obits, WDataOutP owp, double lhs) VL_PURE { + // IEEE format: [63]=sign [62:52]=exp+1023 [51:0]=mantissa + // This does not need to support subnormals as they are sub-integral + lhs = VL_ROUND(lhs); + VL_ZERO_W(obits, owp); + if (lhs == 0.0) return owp; + const QData q = VL_CVT_Q_D(lhs); + const int lsb = static_cast((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52; + const vluint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52); + if (lsb < 0) { + VL_SET_WQ(owp, mantissa >> -lsb); + } else if (lsb < obits) { + _vl_insert_WQ(obits, owp, mantissa, lsb + 52, lsb); + } + if (lhs < 0) VL_NEGATE_INPLACE_W(VL_WORDS_I(obits), owp); + return owp; +} + +//====================================================================== +// Range assignments + +// EMIT_RULE: VL_ASSIGNRANGE: rclean=dirty; +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 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 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 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 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 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 const rwp) +// VL_MT_SAFE { Illegal, as lhs width >= rhs width +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 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 rbits, int obits, int lsb, WDataOutP owp, + WDataInP const rwp) VL_MT_SAFE { + _vl_insert_WW(obits, owp, rwp, lsb + obits - 1, lsb, rbits); +} + +//====================================================================== +// Triops + +static inline WDataOutP VL_COND_WIWW(int obits, int, int, int, WDataOutP owp, int cond, + WDataInP const w1p, WDataInP const w2p) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + for (int i = 0; i < words; ++i) owp[i] = cond ? w1p[i] : w2p[i]; + return owp; +} + +//====================================================================== +// Constification + +// VL_CONST_W_#X(int obits, WDataOutP owp, IData data0, .... IData data(#-1)) +// Sets wide vector words to specified constant words. +// These macros are used when o might represent more words then are given as constants, +// hence all upper words must be zeroed. +// If changing the number of functions here, also change EMITCINLINES_NUM_CONSTW + +#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; + 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; + 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; + 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; + 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; + 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; + 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; + 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; + 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; + 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; + 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; + 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; + VL_C_END_(obits, VL_WORDS_I(lsb) + 4); +} +static inline WDataOutP VL_CONSTHI_W_5X(int obits, int lsb, WDataOutP obase, + EData d4, + 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; + o[4] = d4; + 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, + 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; + o[4] = d4; o[5] = d5; + 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, + 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; + o[4] = d4; o[5] = d5; o[6] = d6; + 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, + 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; + o[4] = d4; o[5] = d5; o[6] = d6; o[7] = d7; + VL_C_END_(obits, VL_WORDS_I(lsb) + 8); +} + +#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, + EData d7, EData d6, EData d5, EData d4, + 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; o[4] = d4; o[5] = d5; o[6] = d6; o[7] = d7; +} +// clang-format on + +//====================================================================== +// Strings + +extern std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE; +extern CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE; +extern std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE; + +inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool ignoreCase) VL_PURE { + // SystemVerilog does not allow a string variable to contain '\0'. + // So C functions such as strcmp() can correctly compare strings. + if (ignoreCase) { + return VL_STRCASECMP(lhs.c_str(), rhs.c_str()); + } else { + return std::strcmp(lhs.c_str(), rhs.c_str()); + } +} + +extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE; + +extern IData VL_FGETS_NI(std::string& dest, IData fpi); + +//====================================================================== +// Conversion functions + +extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE; +inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE { + VlWide lw; + VL_SET_WQ(lw, lhs); + return VL_CVT_PACK_STR_NW(VL_WQ_WORDS_E, lw); +} +inline std::string VL_CVT_PACK_STR_NN(const std::string& lhs) VL_PURE { return lhs; } +inline std::string& VL_CVT_PACK_STR_NN(std::string& lhs) VL_PURE { return lhs; } +inline std::string VL_CVT_PACK_STR_NI(IData lhs) VL_PURE { + VlWide lw; + VL_SET_WI(lw, lhs); + return VL_CVT_PACK_STR_NW(1, lw); +} +inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs) VL_PURE { + return lhs + rhs; +} +inline std::string VL_REPLICATEN_NNQ(int, int, int, const std::string& lhs, IData rep) VL_PURE { + std::string out; + out.reserve(lhs.length() * rep); + for (unsigned times = 0; times < rep; ++times) out += lhs; + return out; +} +inline std::string VL_REPLICATEN_NNI(int obits, int lbits, int rbits, const std::string& lhs, + IData rep) VL_PURE { + return VL_REPLICATEN_NNQ(obits, lbits, rbits, lhs, rep); +} + +inline IData VL_LEN_IN(const std::string& ld) { return ld.length(); } +extern std::string VL_TOLOWER_NN(const std::string& ld); +extern std::string VL_TOUPPER_NN(const std::string& ld); + +extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE; +extern IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) VL_MT_SAFE; +extern IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE; +extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb, + const std::string& filename, void* memp, QData start, + QData end) VL_MT_SAFE; +extern void VL_WRITEMEM_N(bool hex, int bits, QData depth, int array_lsb, + const std::string& filename, const void* memp, QData start, + QData end) VL_MT_SAFE; +extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp, ...) VL_MT_SAFE; +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, + 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 { + VlWide<2> rwp; + IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = rwp[0]; + return got; +} +inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, SData& rdr) VL_MT_SAFE { + VlWide<2> rwp; + IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = rwp[0]; + return got; +} +inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE { + VlWide<2> rwp; + IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = rwp[0]; + return got; +} +inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, QData& rdr) VL_MT_SAFE { + VlWide<2> rwp; + IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = VL_SET_QW(rwp); + return got; +} +inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, double& rdr) VL_MT_SAFE { + VlWide<2> rwp; + IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp)); + return got; +} +extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE; + +//====================================================================== + +#endif // Guard diff --git a/include/verilated_heavy.h b/include/verilated_heavy.h index a0f8107bd..80129772f 100644 --- a/include/verilated_heavy.h +++ b/include/verilated_heavy.h @@ -12,969 +12,20 @@ //************************************************************************* /// /// \file -/// \brief Verilated string and data-type header +/// \brief Verilated old string and data-type header /// -/// This file is included automatically by Verilator at the top of -/// all C++ files it generates. It is used when strings or other -/// heavyweight types are required; these contents are not part of -/// verilated.h to save compile time when such types aren't used. +/// This file is deprecated, and provided for backwards compatibility. +/// Include verilated.h instead. /// //************************************************************************* #ifndef VERILATOR_VERILATED_HEAVY_H_ #define VERILATOR_VERILATED_HEAVY_H_ +#ifdef VL_NO_LEGACY +#error "Include instead of " +#endif + #include "verilated.h" -#include -#include -#include -#include -#include -#include -#include -#include - -//=================================================================== -// String formatters (required by below containers) - -extern std::string VL_TO_STRING(CData lhs); -extern std::string VL_TO_STRING(SData lhs); -extern std::string VL_TO_STRING(IData lhs); -extern std::string VL_TO_STRING(QData lhs); -inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; } -extern std::string VL_TO_STRING_W(int words, const WDataInP obj); - -//=================================================================== -// Shuffle RNG - -class VlURNG final { -public: - using result_type = size_t; - static constexpr size_t min() { return 0; } - static constexpr size_t max() { return 1ULL << 31; } - size_t operator()() { return VL_MASK_I(31) & VL_RANDOM_I(32); } -}; - -//=================================================================== -// Readmem/Writemem operation classes - -class VlReadMem final { - bool m_hex; // Hex format - int m_bits; // Bit width of values - const std::string& m_filename; // Filename - QData m_end; // End address (as specified by user) - FILE* m_fp; // File handle for filename - QData m_addr; // Next address to read - int m_linenum; // Line number last read from file -public: - VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end); - ~VlReadMem(); - bool isOpen() const { return m_fp != nullptr; } - int linenum() const { return m_linenum; } - bool get(QData& addrr, std::string& valuer); - void setData(void* valuep, const std::string& rhs); -}; - -class VlWriteMem final { - bool m_hex; // Hex format - int m_bits; // Bit width of values - FILE* m_fp; // File handle for filename - QData m_addr; // Next address to write -public: - VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end); - ~VlWriteMem(); - bool isOpen() const { return m_fp != nullptr; } - void print(QData addr, bool addrstamp, const void* valuep); -}; - -//=================================================================== -/// Verilog wide packed bit container. -/// Similar to std::array, but lighter weight, only methods needed -/// by Verilator, to help compile time. -/// -/// A 'struct' as we want this to be an aggregate type that allows -/// static aggregate initialization. Consider data members private. -/// -/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32 -/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be -/// zero in memory, but during intermediate operations in the Verilated -/// internals is unpredictable. - -template struct VlWide final { - // MEMBERS - // This should be the only data member, otherwise generated static initializers need updating - EData m_storage[T_Words]; // Contents of the packed array - - // CONSTRUCTORS - // cppcheck-suppress uninitVar - VlWide() = default; - ~VlWide() = default; - VlWide(const VlWide&) = default; - VlWide(VlWide&&) = default; - - // OPERATOR METHODS - VlWide& operator=(const VlWide&) = default; - VlWide& operator=(VlWide&&) = default; - operator WDataOutP() { return &m_storage[0]; } // This also allows [] - operator WDataInP() const { return &m_storage[0]; } // This also allows [] - - // METHODS - const EData& at(size_t index) const { return m_storage[index]; } - EData& at(size_t index) { return m_storage[index]; } - WData* data() { return &m_storage[0]; } - const WData* data() const { return &m_storage[0]; } - bool operator<(const VlWide& rhs) const { - return VL_LT_W(T_Words, data(), rhs.data()); - } -}; - -// Convert a C array to std::array reference by pointer magic, without copy. -// Data type (second argument) is so the function template can automatically generate. -template -VlWide& VL_CVT_W_A(const WDataInP inp, const VlWide&) { - return *((VlWide*)inp); -} - -template std::string VL_TO_STRING(const VlWide& obj) { - return VL_TO_STRING_W(T_Words, obj.data()); -} - -//=================================================================== -// Verilog queue and dynamic array container -// There are no multithreaded locks on this; the base variable must -// be protected by other means -// -// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound -// For dynamic arrays it is always zero -template class VlQueue final { -private: - // TYPES - using Deque = std::deque; - -public: - using const_iterator = typename Deque::const_iterator; - -private: - // MEMBERS - Deque m_deque; // State of the assoc array - T_Value m_defaultValue; // Default value - -public: - // CONSTRUCTORS - // m_defaultValue isn't defaulted. Caller's constructor must do it. - VlQueue() = default; - ~VlQueue() = default; - VlQueue(const VlQueue&) = default; - VlQueue(VlQueue&&) = default; - VlQueue& operator=(const VlQueue&) = default; - VlQueue& operator=(VlQueue&&) = default; - - // Standard copy constructor works. Verilog: assoca = assocb - // Also must allow conversion from a different T_MaxSize queue - template VlQueue operator=(const VlQueue& rhs) { - m_deque = rhs.privateDeque(); - if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1); - return *this; - } - - static VlQueue cons(const T_Value& lhs) { - VlQueue out; - out.push_back(lhs); - return out; - } - static VlQueue cons(const T_Value& lhs, const T_Value& rhs) { - VlQueue out; - out.push_back(rhs); - out.push_back(lhs); - return out; - } - static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) { - VlQueue out = lhs; - out.push_front(rhs); - return out; - } - static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) { - VlQueue out = rhs; - out.push_back(lhs); - return out; - } - static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) { - VlQueue out = rhs; - for (const auto& i : lhs.m_deque) out.push_back(i); - return out; - } - - // METHODS - T_Value& atDefault() { return m_defaultValue; } - const T_Value& atDefault() const { return m_defaultValue; } - const Deque& privateDeque() const { return m_deque; } - - // Size. Verilog: function int size(), or int num() - int size() const { return m_deque.size(); } - // Clear array. Verilog: function void delete([input index]) - void clear() { m_deque.clear(); } - void erase(vlsint32_t index) { - if (VL_LIKELY(index >= 0 && index < m_deque.size())) - m_deque.erase(m_deque.begin() + index); - } - - // Dynamic array new[] becomes a renew() - void renew(size_t size) { - clear(); - m_deque.resize(size, atDefault()); - } - // Dynamic array new[]() becomes a renew_copy() - void renew_copy(size_t size, const VlQueue& rhs) { - if (size == 0) { - clear(); - } else { - *this = rhs; - m_deque.resize(size, atDefault()); - } - } - - // function void q.push_front(value) - void push_front(const T_Value& value) { - m_deque.push_front(value); - if (VL_UNLIKELY(T_MaxSize != 0 && m_deque.size() > T_MaxSize)) m_deque.pop_back(); - } - // function void q.push_back(value) - void push_back(const T_Value& value) { - if (VL_LIKELY(T_MaxSize == 0 || m_deque.size() < T_MaxSize)) m_deque.push_back(value); - } - // function value_t q.pop_front(); - T_Value pop_front() { - if (m_deque.empty()) return m_defaultValue; - T_Value v = m_deque.front(); - m_deque.pop_front(); - return v; - } - // function value_t q.pop_back(); - T_Value pop_back() { - if (m_deque.empty()) return m_defaultValue; - T_Value v = m_deque.back(); - m_deque.pop_back(); - return v; - } - - // Setting. Verilog: assoc[index] = v - // Can't just overload operator[] or provide a "at" reference to set, - // because we need to be able to insert only when the value is set - T_Value& at(vlsint32_t index) { - static T_Value s_throwAway; - // Needs to work for dynamic arrays, so does not use T_MaxSize - if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) { - s_throwAway = atDefault(); - return s_throwAway; - } else { - return m_deque[index]; - } - } - // Accessing. Verilog: v = assoc[index] - const T_Value& at(vlsint32_t index) const { - static T_Value s_throwAway; - // Needs to work for dynamic arrays, so does not use T_MaxSize - if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) { - return atDefault(); - } else { - return m_deque[index]; - } - } - // function void q.insert(index, value); - void insert(vlsint32_t index, const T_Value& value) { - if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return; - m_deque.insert(m_deque.begin() + index, value); - } - - // Return slice q[lsb:msb] - VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const { - VlQueue out; - if (VL_UNLIKELY(lsb < 0)) lsb = 0; - if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1; - if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1; - for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]); - return out; - } - - // For save/restore - const_iterator begin() const { return m_deque.begin(); } - const_iterator end() const { return m_deque.end(); } - - // Methods - void sort() { std::sort(m_deque.begin(), m_deque.end()); } - template void sort(Func with_func) { - // with_func returns arbitrary type to use for the sort comparison - std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) { - // index number is meaninless with sort, as it changes - return with_func(0, a) < with_func(0, b); - }); - } - void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); } - template void rsort(Func with_func) { - // with_func returns arbitrary type to use for the sort comparison - std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) { - // index number is meaninless with sort, as it changes - return with_func(0, a) < with_func(0, b); - }); - } - void reverse() { std::reverse(m_deque.begin(), m_deque.end()); } - void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG()); } - VlQueue unique() const { - VlQueue out; - std::unordered_set saw; - for (const auto& i : m_deque) { - auto it = saw.find(i); - if (it == saw.end()) { - saw.insert(it, i); - out.push_back(i); - } - } - return out; - } - VlQueue unique_index() const { - VlQueue out; - IData index = 0; - std::unordered_set saw; - for (const auto& i : m_deque) { - auto it = saw.find(i); - if (it == saw.end()) { - saw.insert(it, i); - out.push_back(index); - } - ++index; - } - return out; - } - template VlQueue find(Func with_func) const { - VlQueue out; - IData index = 0; - for (const auto& i : m_deque) { - if (with_func(index, i)) out.push_back(i); - ++index; - } - return out; - } - template VlQueue find_index(Func with_func) const { - VlQueue out; - IData index = 0; - for (const auto& i : m_deque) { - if (with_func(index, i)) out.push_back(index); - ++index; - } - return out; - } - template VlQueue find_first(Func with_func) const { - // Can't use std::find_if as need index number - IData index = 0; - for (const auto& i : m_deque) { - if (with_func(index, i)) return VlQueue::cons(i); - ++index; - } - return VlQueue{}; - } - template VlQueue find_first_index(Func with_func) const { - IData index = 0; - for (const auto& i : m_deque) { - if (with_func(index, i)) return VlQueue::cons(index); - ++index; - } - return VlQueue{}; - } - template VlQueue find_last(Func with_func) const { - IData index = m_deque.size() - 1; - for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) { - if (with_func(index, *it)) return VlQueue::cons(*it); - --index; - } - return VlQueue{}; - } - template VlQueue find_last_index(Func with_func) const { - IData index = m_deque.size() - 1; - for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) { - if (with_func(index, *it)) return VlQueue::cons(index); - --index; - } - return VlQueue{}; - } - - // Reduction operators - VlQueue min() const { - if (m_deque.empty()) return VlQueue(); - const auto it = std::min_element(m_deque.begin(), m_deque.end()); - return VlQueue::cons(*it); - } - VlQueue max() const { - if (m_deque.empty()) return VlQueue(); - const auto it = std::max_element(m_deque.begin(), m_deque.end()); - return VlQueue::cons(*it); - } - - T_Value r_sum() const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_deque) out += i; - return out; - } - template T_Value r_sum(Func with_func) const { - T_Value out(0); // Type must have assignment operator - IData index = 0; - for (const auto& i : m_deque) out += with_func(index++, i); - return out; - } - T_Value r_product() const { - if (m_deque.empty()) return T_Value(0); - auto it = m_deque.begin(); - T_Value out{*it}; - ++it; - for (; it != m_deque.end(); ++it) out *= *it; - return out; - } - template T_Value r_product(Func with_func) const { - if (m_deque.empty()) return T_Value(0); - auto it = m_deque.begin(); - IData index = 0; - T_Value out{with_func(index, *it)}; - ++it; - ++index; - for (; it != m_deque.end(); ++it) out *= with_func(index++, *it); - return out; - } - T_Value r_and() const { - if (m_deque.empty()) return T_Value(0); - auto it = m_deque.begin(); - T_Value out{*it}; - ++it; - for (; it != m_deque.end(); ++it) out &= *it; - return out; - } - template T_Value r_and(Func with_func) const { - if (m_deque.empty()) return T_Value(0); - auto it = m_deque.begin(); - IData index = 0; - T_Value out{with_func(index, *it)}; - ++it; - ++index; - for (; it != m_deque.end(); ++it) out &= with_func(index, *it); - return out; - } - T_Value r_or() const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_deque) out |= i; - return out; - } - template T_Value r_or(Func with_func) const { - T_Value out(0); // Type must have assignment operator - IData index = 0; - for (const auto& i : m_deque) out |= with_func(index++, i); - return out; - } - T_Value r_xor() const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_deque) out ^= i; - return out; - } - template T_Value r_xor(Func with_func) const { - T_Value out(0); // Type must have assignment operator - IData index = 0; - for (const auto& i : m_deque) out ^= with_func(index++, i); - return out; - } - - // Dumping. Verilog: str = $sformatf("%p", assoc) - std::string to_string() const { - if (m_deque.empty()) return "'{}"; // No trailing space - std::string out = "'{"; - std::string comma; - for (const auto& i : m_deque) { - out += comma + VL_TO_STRING(i); - comma = ", "; - } - return out + "} "; - } -}; - -template std::string VL_TO_STRING(const VlQueue& obj) { - return obj.to_string(); -} - -//=================================================================== -// Verilog associative array container -// There are no multithreaded locks on this; the base variable must -// be protected by other means -// -template class VlAssocArray final { -private: - // TYPES - using Map = std::map; - -public: - using const_iterator = typename Map::const_iterator; - -private: - // MEMBERS - Map m_map; // State of the assoc array - T_Value m_defaultValue; // Default value - -public: - // CONSTRUCTORS - // m_defaultValue isn't defaulted. Caller's constructor must do it. - VlAssocArray() = default; - ~VlAssocArray() = default; - VlAssocArray(const VlAssocArray&) = default; - VlAssocArray(VlAssocArray&&) = default; - VlAssocArray& operator=(const VlAssocArray&) = default; - VlAssocArray& operator=(VlAssocArray&&) = default; - - // METHODS - T_Value& atDefault() { return m_defaultValue; } - const T_Value& atDefault() const { return m_defaultValue; } - - // Size of array. Verilog: function int size(), or int num() - int size() const { return m_map.size(); } - // Clear array. Verilog: function void delete([input index]) - void clear() { m_map.clear(); } - void erase(const T_Key& index) { m_map.erase(index); } - // Return 0/1 if element exists. Verilog: function int exists(input index) - int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); } - // Return first element. Verilog: function int first(ref index); - int first(T_Key& indexr) const { - const auto it = m_map.cbegin(); - if (it == m_map.end()) return 0; - indexr = it->first; - return 1; - } - // Return last element. Verilog: function int last(ref index) - int last(T_Key& indexr) const { - const auto it = m_map.crbegin(); - if (it == m_map.rend()) return 0; - indexr = it->first; - return 1; - } - // Return next element. Verilog: function int next(ref index) - int next(T_Key& indexr) const { - auto it = m_map.find(indexr); - if (VL_UNLIKELY(it == m_map.end())) return 0; - ++it; - if (VL_UNLIKELY(it == m_map.end())) return 0; - indexr = it->first; - return 1; - } - // Return prev element. Verilog: function int prev(ref index) - int prev(T_Key& indexr) const { - auto it = m_map.find(indexr); - if (VL_UNLIKELY(it == m_map.end())) return 0; - if (VL_UNLIKELY(it == m_map.begin())) return 0; - --it; - indexr = it->first; - return 1; - } - // Setting. Verilog: assoc[index] = v - // Can't just overload operator[] or provide a "at" reference to set, - // because we need to be able to insert only when the value is set - T_Value& at(const T_Key& index) { - const auto it = m_map.find(index); - if (it == m_map.end()) { - std::pair pit = m_map.emplace(index, m_defaultValue); - return pit.first->second; - } - return it->second; - } - // Accessing. Verilog: v = assoc[index] - const T_Value& at(const T_Key& index) const { - const auto it = m_map.find(index); - if (it == m_map.end()) { - return m_defaultValue; - } else { - return it->second; - } - } - // Setting as a chained operation - VlAssocArray& set(const T_Key& index, const T_Value& value) { - at(index) = value; - return *this; - } - VlAssocArray& setDefault(const T_Value& value) { - atDefault() = value; - return *this; - } - - // For save/restore - const_iterator begin() const { return m_map.begin(); } - const_iterator end() const { return m_map.end(); } - - // Methods - VlQueue unique() const { - VlQueue out; - std::set saw; - for (const auto& i : m_map) { - auto it = saw.find(i.second); - if (it == saw.end()) { - saw.insert(it, i.second); - out.push_back(i.second); - } - } - return out; - } - VlQueue unique_index() const { - VlQueue out; - std::set saw; - for (const auto& i : m_map) { - auto it = saw.find(i.second); - if (it == saw.end()) { - saw.insert(it, i.second); - out.push_back(i.first); - } - } - return out; - } - template VlQueue find(Func with_func) const { - VlQueue out; - for (const auto& i : m_map) - if (with_func(i.first, i.second)) out.push_back(i.second); - return out; - } - template VlQueue find_index(Func with_func) const { - VlQueue out; - for (const auto& i : m_map) - if (with_func(i.first, i.second)) out.push_back(i.first); - return out; - } - template VlQueue find_first(Func with_func) const { - const auto it - = std::find_if(m_map.begin(), m_map.end(), [=](const std::pair& i) { - return with_func(i.first, i.second); - }); - if (it == m_map.end()) return VlQueue{}; - return VlQueue::cons(it->second); - } - template VlQueue find_first_index(Func with_func) const { - const auto it - = std::find_if(m_map.begin(), m_map.end(), [=](const std::pair& i) { - return with_func(i.first, i.second); - }); - if (it == m_map.end()) return VlQueue{}; - return VlQueue::cons(it->first); - } - template VlQueue find_last(Func with_func) const { - const auto it - = std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair& i) { - return with_func(i.first, i.second); - }); - if (it == m_map.rend()) return VlQueue{}; - return VlQueue::cons(it->second); - } - template VlQueue find_last_index(Func with_func) const { - const auto it - = std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair& i) { - return with_func(i.first, i.second); - }); - if (it == m_map.rend()) return VlQueue{}; - return VlQueue::cons(it->first); - } - - // Reduction operators - VlQueue min() const { - if (m_map.empty()) return VlQueue(); - const auto it = std::min_element( - m_map.begin(), m_map.end(), - [](const std::pair& a, const std::pair& b) { - return a.second < b.second; - }); - return VlQueue::cons(it->second); - } - VlQueue max() const { - if (m_map.empty()) return VlQueue(); - const auto it = std::max_element( - m_map.begin(), m_map.end(), - [](const std::pair& a, const std::pair& b) { - return a.second < b.second; - }); - return VlQueue::cons(it->second); - } - - T_Value r_sum() const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_map) out += i.second; - return out; - } - template T_Value r_sum(Func with_func) const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_map) out += with_func(i.first, i.second); - return out; - } - T_Value r_product() const { - if (m_map.empty()) return T_Value(0); - auto it = m_map.begin(); - T_Value out{it->second}; - ++it; - for (; it != m_map.end(); ++it) out *= it->second; - return out; - } - template T_Value r_product(Func with_func) const { - if (m_map.empty()) return T_Value(0); - auto it = m_map.begin(); - T_Value out{with_func(it->first, it->second)}; - ++it; - for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second); - return out; - } - T_Value r_and() const { - if (m_map.empty()) return T_Value(0); - auto it = m_map.begin(); - T_Value out{it->second}; - ++it; - for (; it != m_map.end(); ++it) out &= it->second; - return out; - } - template T_Value r_and(Func with_func) const { - if (m_map.empty()) return T_Value(0); - auto it = m_map.begin(); - T_Value out{with_func(it->first, it->second)}; - ++it; - for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second); - return out; - } - T_Value r_or() const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_map) out |= i.second; - return out; - } - template T_Value r_or(Func with_func) const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_map) out |= with_func(i.first, i.second); - return out; - } - T_Value r_xor() const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_map) out ^= i.second; - return out; - } - template T_Value r_xor(Func with_func) const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_map) out ^= with_func(i.first, i.second); - return out; - } - - // Dumping. Verilog: str = $sformatf("%p", assoc) - std::string to_string() const { - if (m_map.empty()) return "'{}"; // No trailing space - std::string out = "'{"; - std::string comma; - for (const auto& i : m_map) { - out += comma + VL_TO_STRING(i.first) + ":" + VL_TO_STRING(i.second); - comma = ", "; - } - // Default not printed - maybe random init data - return out + "} "; - } -}; - -template -std::string VL_TO_STRING(const VlAssocArray& obj) { - return obj.to_string(); -} - -template -void VL_READMEM_N(bool hex, int bits, const std::string& filename, - VlAssocArray& obj, QData start, QData end) VL_MT_SAFE { - VlReadMem rmem(hex, bits, filename, start, end); - if (VL_UNLIKELY(!rmem.isOpen())) return; - while (true) { - QData addr; - std::string data; - if (rmem.get(addr /*ref*/, data /*ref*/)) { - rmem.setData(&(obj.at(addr)), data); - } else { - break; - } - } -} - -template -void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename, - const VlAssocArray& obj, QData start, QData end) VL_MT_SAFE { - VlWriteMem wmem(hex, bits, filename, start, end); - if (VL_UNLIKELY(!wmem.isOpen())) return; - for (const auto& i : obj) { - const QData addr = i.first; - if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second)); - } -} - -//=================================================================== -/// Verilog unpacked array container -/// For when a standard C++[] array is not sufficient, e.g. an -/// array under a queue, or methods operating on the array. -/// -/// A 'struct' as we want this to be an aggregate type that allows -/// static aggregate initialization. Consider data members private. -/// -/// This class may get exposed to a Verilated Model's top I/O, if the top -/// IO has an unpacked array. - -template struct VlUnpacked final { - // MEMBERS - // This should be the only data member, otherwise generated static initializers need updating - T_Value m_storage[T_Depth]; // Contents of the unpacked array - - // CONSTRUCTORS - VlUnpacked() = default; - ~VlUnpacked() = default; - VlUnpacked(const VlUnpacked&) = default; - VlUnpacked(VlUnpacked&&) = default; - VlUnpacked& operator=(const VlUnpacked&) = default; - VlUnpacked& operator=(VlUnpacked&&) = default; - - // METHODS - // Raw access - WData* data() { return &m_storage[0]; } - const WData* data() const { return &m_storage[0]; } - - T_Value& operator[](size_t index) { return m_storage[index]; }; - const T_Value& operator[](size_t index) const { return m_storage[index]; }; - - // Dumping. Verilog: str = $sformatf("%p", assoc) - std::string to_string() const { - std::string out = "'{"; - std::string comma; - for (int i = 0; i < T_Depth; ++i) { - out += comma + VL_TO_STRING(m_storage[i]); - comma = ", "; - } - return out + "} "; - } -}; - -template -std::string VL_TO_STRING(const VlUnpacked& obj) { - return obj.to_string(); -} - -//=================================================================== -// Verilog class reference container -// There are no multithreaded locks on this; the base variable must -// be protected by other means - -#define VlClassRef std::shared_ptr - -template // T typically of type VlClassRef -inline T VL_NULL_CHECK(T t, const char* filename, int linenum) { - if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum); - return t; -} - -template -static inline bool VL_CAST_DYNAMIC(VlClassRef in, VlClassRef& outr) { - VlClassRef casted = std::dynamic_pointer_cast(in); - if (VL_LIKELY(casted)) { - outr = casted; - return true; - } else { - return false; - } -} - -//====================================================================== -// Conversion functions - -extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE; -inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE { - VlWide lw; - VL_SET_WQ(lw, lhs); - return VL_CVT_PACK_STR_NW(VL_WQ_WORDS_E, lw); -} -inline std::string VL_CVT_PACK_STR_NN(const std::string& lhs) VL_PURE { return lhs; } -inline std::string& VL_CVT_PACK_STR_NN(std::string& lhs) VL_PURE { return lhs; } -inline std::string VL_CVT_PACK_STR_NI(IData lhs) VL_PURE { - VlWide lw; - VL_SET_WI(lw, lhs); - return VL_CVT_PACK_STR_NW(1, lw); -} -inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs) VL_PURE { - return lhs + rhs; -} -inline std::string VL_REPLICATEN_NNQ(int, int, int, const std::string& lhs, IData rep) VL_PURE { - std::string out; - out.reserve(lhs.length() * rep); - for (unsigned times = 0; times < rep; ++times) out += lhs; - return out; -} -inline std::string VL_REPLICATEN_NNI(int obits, int lbits, int rbits, const std::string& lhs, - IData rep) VL_PURE { - return VL_REPLICATEN_NNQ(obits, lbits, rbits, lhs, rep); -} - -inline IData VL_LEN_IN(const std::string& ld) { return ld.length(); } -extern std::string VL_TOLOWER_NN(const std::string& ld); -extern std::string VL_TOUPPER_NN(const std::string& ld); - -extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE; -extern IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) VL_MT_SAFE; -extern IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE; -extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb, - const std::string& filename, void* memp, QData start, - QData end) VL_MT_SAFE; -extern void VL_WRITEMEM_N(bool hex, int bits, QData depth, int array_lsb, - const std::string& filename, const void* memp, QData start, - QData end) VL_MT_SAFE; -extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp, ...) VL_MT_SAFE; -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, - 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 { - VlWide<2> rwp; // WData must always be at least 2 - IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); - if (got) rdr = rwp[0]; - return got; -} -inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, SData& rdr) VL_MT_SAFE { - VlWide<2> rwp; // WData must always be at least 2 - IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); - if (got) rdr = rwp[0]; - return got; -} -inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE { - VlWide<2> rwp; - IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); - if (got) rdr = rwp[0]; - return got; -} -inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, QData& rdr) VL_MT_SAFE { - VlWide<2> rwp; - IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); - if (got) rdr = VL_SET_QW(rwp); - return got; -} -inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, double& rdr) VL_MT_SAFE { - VlWide<2> rwp; - IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); - if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp)); - return got; -} -extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE; - -//====================================================================== -// Strings - -extern std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE; -extern CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE; -extern std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE; - -inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool ignoreCase) VL_PURE { - // SystemVerilog does not allow a string variable to contain '\0'. - // So C functions such as strcmp() can correctly compare strings. - if (ignoreCase) { - return VL_STRCASECMP(lhs.c_str(), rhs.c_str()); - } else { - return std::strcmp(lhs.c_str(), rhs.c_str()); - } -} - -extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE; - -extern IData VL_FGETS_NI(std::string& dest, IData fpi); - #endif // Guard diff --git a/include/verilated_imp.h b/include/verilated_imp.h index b4d13e0e0..4c30cbafc 100644 --- a/include/verilated_imp.h +++ b/include/verilated_imp.h @@ -30,7 +30,6 @@ #include "verilatedos.h" #include "verilated.h" -#include "verilated_heavy.h" #include "verilated_syms.h" #include @@ -104,7 +103,7 @@ public: // METHODS // Add message to queue (called by producer) void post(const VerilatedMsg& msg) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_queue.insert(msg); // Pass by value to copy the message into queue ++m_depth; } @@ -124,7 +123,7 @@ public: const VerilatedMsg msg = *(it); m_queue.erase(it); m_mutex.unlock(); - m_depth--; // Ok if outside critical section as only this code checks the value + --m_depth; // Ok if outside critical section as only this code checks the value { VL_DEBUG_IF(VL_DBG_MSGF("Executing callback from mtaskId=%d\n", msg.mtaskId());); msg.run(); @@ -179,7 +178,7 @@ public: // FILE* list constructed from a file-descriptor class VerilatedFpList final { - FILE* m_fp[31]; + FILE* m_fp[31] = {}; std::size_t m_sz = 0; public: @@ -256,11 +255,11 @@ public: // But only for verilated*.cpp 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() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { - const VerilatedLockGuard lock(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); + const VerilatedLockGuard lock{m_timeDumpMutex}; m_timeFormatSuffix = value; } @@ -276,7 +275,7 @@ public: // But only for verilated*.cpp 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); + const VerilatedLockGuard lock{m_fdMutex}; if (m_fdFreeMct.empty()) return 0; IData idx = m_fdFreeMct.back(); m_fdFreeMct.pop_back(); @@ -285,10 +284,10 @@ public: // But only for verilated*.cpp return (1 << idx); } IData fdNew(const char* filenamep, const char* modep) VL_MT_SAFE_EXCLUDES(m_fdMutex) { - FILE* fp = std::fopen(filenamep, modep); + FILE* const fp = std::fopen(filenamep, modep); if (VL_UNLIKELY(!fp)) return 0; // Bit 31 indicates it's a descriptor not a MCD - const VerilatedLockGuard lock(m_fdMutex); + 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()); @@ -306,25 +305,25 @@ public: // But only for verilated*.cpp 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 VerilatedLockGuard lock{m_fdMutex}; const VerilatedFpList fdlist = fdToFpList(fdi); for (const auto& i : fdlist) std::fflush(i); } IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE_EXCLUDES(m_fdMutex) { - const VerilatedLockGuard lock(m_fdMutex); + const VerilatedLockGuard lock{m_fdMutex}; const VerilatedFpList fdlist = fdToFpList(fdi); if (VL_UNLIKELY(fdlist.size() != 1)) return 0; return static_cast( std::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 VerilatedLockGuard lock{m_fdMutex}; const VerilatedFpList fdlist = fdToFpList(fdi); if (VL_UNLIKELY(fdlist.size() != 1)) return 0; return static_cast(std::ftell(*fdlist.begin())); } void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE_EXCLUDES(m_fdMutex) { - const VerilatedLockGuard lock(m_fdMutex); + const VerilatedLockGuard lock{m_fdMutex}; const VerilatedFpList fdlist = fdToFpList(fdi); for (const auto& i : fdlist) { if (VL_UNLIKELY(!i)) continue; @@ -332,7 +331,7 @@ public: // But only for verilated*.cpp } } void fdClose(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) { - const VerilatedLockGuard lock(m_fdMutex); + const VerilatedLockGuard lock{m_fdMutex}; if (VL_BITISSET_I(fdi, 31)) { // Non-MCD case IData idx = VL_MASK_I(31) & fdi; @@ -356,7 +355,7 @@ public: // But only for verilated*.cpp } } inline FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) { - const VerilatedLockGuard lock(m_fdMutex); + const VerilatedLockGuard lock{m_fdMutex}; const VerilatedFpList fdlist = fdToFpList(fdi); if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr; return *fdlist.begin(); @@ -463,7 +462,7 @@ public: // 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().m_userMapMutex); + 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; @@ -472,7 +471,7 @@ public: } } static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE { - const VerilatedLockGuard lock(s().m_userMapMutex); + 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; @@ -482,7 +481,7 @@ 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().m_userMapMutex); + const VerilatedLockGuard lock{s().m_userMapMutex}; for (auto it = s().m_userMap.begin(); it != s().m_userMap.end();) { if (it->first.first == scopep) { s().m_userMap.erase(it++); @@ -492,7 +491,7 @@ public: // But only for verilated.cpp } } static void userDump() VL_MT_SAFE { - const VerilatedLockGuard lock(s().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().m_userMap) { if (first) { @@ -508,13 +507,13 @@ 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().m_hierMapMutex); + 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().m_hierMapMutex); + const VerilatedLockGuard lock{s().m_hierMapMutex}; VerilatedHierarchyMap& map = s().m_hierMap; if (map.find(fromp) == map.end()) return; auto& scopes = map[fromp]; @@ -537,7 +536,7 @@ 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().m_exportMutex); + 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++); @@ -547,24 +546,24 @@ public: // But only for verilated*.cpp } } static int exportFind(const char* namep) VL_MT_SAFE { - const VerilatedLockGuard lock(s().m_exportMutex); + 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 + 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()); return -1; } 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().m_exportMutex); + 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().m_exportMutex); + const VerilatedLockGuard lock{s().m_exportMutex}; bool first = true; for (const auto& i : s().m_exportMap) { if (first) { diff --git a/include/verilated_save.cpp b/include/verilated_save.cpp index 0c07022ea..2fec9808d 100644 --- a/include/verilated_save.cpp +++ b/include/verilated_save.cpp @@ -93,8 +93,8 @@ void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE { if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, std::strlen(VLTSAVE_HEADER_STR)))) { const std::string fn = filename(); const std::string msg - = std::string( - "Can't deserialize; file has wrong header signature, or file not found: ") + = std:: + string{"Can't deserialize; file has wrong header signature, or file not found: "} + filename(); VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str()); // Die before we close() as close would infinite loop @@ -112,7 +112,7 @@ void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE { if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, std::strlen(VLTSAVE_TRAILER_STR)))) { const std::string fn = filename(); const std::string msg - = std::string("Can't deserialize; file has wrong end-of-file signature: ") + = std::string{"Can't deserialize; file has wrong end-of-file signature: "} + filename(); VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str()); // Die before we close() as close would infinite loop @@ -204,7 +204,7 @@ void VerilatedSave::flush() VL_MT_UNSAFE_ONE { if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) { // LCOV_EXCL_START // write failed, presume error (perhaps out of disk space) - std::string msg = std::string(__FUNCTION__) + ": " + std::strerror(errno); + std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno); VL_FATAL_MT("", 0, "", msg.c_str()); close(); break; @@ -235,7 +235,7 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE { if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) { // LCOV_EXCL_START // write failed, presume error (perhaps out of disk space) - const std::string msg = std::string(__FUNCTION__) + ": " + std::strerror(errno); + const std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno); VL_FATAL_MT("", 0, "", msg.c_str()); close(); break; diff --git a/include/verilated_save.h b/include/verilated_save.h index 1a6c14e13..f49c15f91 100644 --- a/include/verilated_save.h +++ b/include/verilated_save.h @@ -23,7 +23,7 @@ #define VERILATOR_VERILATED_SAVE_C_H_ #include "verilatedos.h" -#include "verilated_heavy.h" +#include "verilated.h" #include diff --git a/include/verilated_syms.h b/include/verilated_syms.h index 1dc633e6e..160bd31c9 100644 --- a/include/verilated_syms.h +++ b/include/verilated_syms.h @@ -30,7 +30,7 @@ #define VERILATOR_VERILATED_SYMS_H_ #include "verilatedos.h" -#include "verilated_heavy.h" +#include "verilated.h" #include "verilated_sym_props.h" #include diff --git a/include/verilated_threads.cpp b/include/verilated_threads.cpp index 8d3480540..606985494 100644 --- a/include/verilated_threads.cpp +++ b/include/verilated_threads.cpp @@ -48,7 +48,8 @@ VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount) // VlWorkerThread VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling) - : m_poolp{poolp} + : m_ready_size{0} + , 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} @@ -104,7 +105,7 @@ VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profil } // Create'em for (int i = 0; i < nThreads; ++i) { - m_workers.push_back(new VlWorkerThread(this, contextp, 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 @@ -131,13 +132,13 @@ void VlThreadPool::setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex) { // try not to malloc while collecting profiling. t_profilep->reserve(4096); { - const VerilatedLockGuard lk(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_allProfiles.insert(t_profilep); } } void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lk(m_mutex); + const VerilatedLockGuard lock{m_mutex}; for (const auto& profilep : m_allProfiles) { // Every thread's profile trace gets a copy of rec. profilep->emplace_back(rec); @@ -146,7 +147,7 @@ void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lk(m_mutex); + const VerilatedLockGuard lock{m_mutex}; VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep);); FILE* const fp = std::fopen(filenamep, "w"); diff --git a/include/verilated_threads.h b/include/verilated_threads.h index b6163477b..150168f80 100644 --- a/include/verilated_threads.h +++ b/include/verilated_threads.h @@ -225,10 +225,10 @@ public: } VL_CPU_RELAX(); } - VerilatedLockGuard lk(m_mutex); + VerilatedLockGuard lock{m_mutex}; while (m_ready.empty()) { m_waiting = true; - m_cv.wait(lk); + m_cv.wait(lock); } m_waiting = false; // As noted above this is inefficient if our ready list is ever @@ -242,7 +242,7 @@ public: VL_MT_SAFE_EXCLUDES(m_mutex) { bool notify; { - const VerilatedLockGuard lk(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_ready.emplace_back(fnp, selfp, evenCycle); m_ready_size.fetch_add(1, std::memory_order_relaxed); notify = m_waiting; diff --git a/include/verilated_trace.h b/include/verilated_trace.h index db3b71efc..6e7d6c59e 100644 --- a/include/verilated_trace.h +++ b/include/verilated_trace.h @@ -52,21 +52,21 @@ private: public: // Put an element at the back of the queue void put(T value) VL_MT_SAFE_EXCLUDES(m_mutex) { - VerilatedLockGuard lock(m_mutex); + const 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) VL_MT_SAFE_EXCLUDES(m_mutex) { - VerilatedLockGuard lock(m_mutex); + const 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() VL_MT_SAFE_EXCLUDES(m_mutex) { - VerilatedLockGuard lock(m_mutex); + VerilatedLockGuard lock{m_mutex}; m_cv.wait(lock, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); }); assert(!m_queue.empty()); T value = m_queue.front(); @@ -76,7 +76,7 @@ public: // Non blocking get bool tryGet(T& result) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lockGuard(m_mutex); + const VerilatedLockGuard lockGuard{m_mutex}; if (m_queue.empty()) return false; result = m_queue.front(); m_queue.pop_front(); diff --git a/include/verilated_trace_imp.cpp b/include/verilated_trace_imp.cpp index 6a31b6cd0..07e60f7a3 100644 --- a/include/verilated_trace_imp.cpp +++ b/include/verilated_trace_imp.cpp @@ -246,7 +246,7 @@ template <> void VerilatedTrace::closeBase() { shutdownWorker(); while (m_numTraceBuffers) { delete[] m_buffersFromWorker.get(); - m_numTraceBuffers--; + --m_numTraceBuffers; } #endif } @@ -351,7 +351,7 @@ template <> void VerilatedTrace::traceInit() VL_MT_UNSAFE { m_traceBufferSize = nextCode() + numSignals() * 2 + 4; // Start the worker thread - m_workerThread.reset(new std::thread(&VerilatedTrace::workerThreadMain, this)); + m_workerThread.reset(new std::thread{&VerilatedTrace::workerThreadMain, this}); #endif } @@ -398,7 +398,7 @@ void VerilatedTrace::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m // 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); + 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", @@ -479,9 +479,9 @@ template <> void VerilatedTrace::addCallbackRecord(std::vector& cbVec, CallbackRecord& cbRec) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START - std::string msg = (std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__ + std::string msg = (std::string{"Internal: "} + __FILE__ + "::" + __FUNCTION__ + " called with already open file"); VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); } // LCOV_EXCL_STOP @@ -489,19 +489,19 @@ void VerilatedTrace::addCallbackRecord(std::vector } template <> void VerilatedTrace::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE { - CallbackRecord cbr(cb, userp); + CallbackRecord cbr{cb, userp}; addCallbackRecord(m_initCbs, cbr); } template <> void VerilatedTrace::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE { - CallbackRecord cbr(cb, userp); + CallbackRecord cbr{cb, userp}; addCallbackRecord(m_fullCbs, cbr); } template <> void VerilatedTrace::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE { - CallbackRecord cbr(cb, userp); + CallbackRecord cbr{cb, userp}; addCallbackRecord(m_chgCbs, cbr); } template <> void VerilatedTrace::addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE { - CallbackRecord cbr(cb, userp); + CallbackRecord cbr{cb, userp}; addCallbackRecord(m_cleanupCbs, cbr); } template <> void VerilatedTrace::module(const std::string& name) VL_MT_UNSAFE { diff --git a/include/verilated_types.h b/include/verilated_types.h new file mode 100644 index 000000000..8a0dde96d --- /dev/null +++ b/include/verilated_types.h @@ -0,0 +1,897 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// Copyright 2003-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 +// +//************************************************************************* +/// +/// \file +/// \brief Verilated common data type containers +/// +/// verilated.h should be included instead of this file. +/// +/// Those macro/function/variable starting or ending in _ are internal, +/// however many of the other function/macros here are also internal. +/// +//************************************************************************* + +#ifndef VERILATOR_VERILATED_TYPES_H_ +#define VERILATOR_VERILATED_TYPES_H_ + +#ifndef VERILATOR_VERILATED_H_INTERNAL_ +#error "verilated_types.h should only be included by verilated.h" +#endif + +//=================================================================== +// String formatters (required by below containers) + +extern std::string VL_TO_STRING(CData lhs); +extern std::string VL_TO_STRING(SData lhs); +extern std::string VL_TO_STRING(IData lhs); +extern std::string VL_TO_STRING(QData lhs); +inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; } +extern std::string VL_TO_STRING_W(int words, const WDataInP obj); + +//========================================================================= +// Declare net data types + +#define VL_SIG8(name, msb, lsb) CData name ///< Declare signal, 1-8 bits +#define VL_SIG16(name, msb, lsb) SData name ///< Declare signal, 9-16 bits +#define VL_SIG64(name, msb, lsb) QData name ///< Declare signal, 33-64 bits +#define VL_SIG(name, msb, lsb) IData name ///< Declare signal, 17-32 bits +#define VL_SIGW(name, msb, lsb, words) WData name[words] ///< Declare signal, 65+ bits +#define VL_IN8(name, msb, lsb) CData name ///< Declare input signal, 1-8 bits +#define VL_IN16(name, msb, lsb) SData name ///< Declare input signal, 9-16 bits +#define VL_IN64(name, msb, lsb) QData name ///< Declare input signal, 33-64 bits +#define VL_IN(name, msb, lsb) IData name ///< Declare input signal, 17-32 bits +#define VL_INW(name, msb, lsb, words) WData name[words] ///< Declare input signal, 65+ bits +#define VL_INOUT8(name, msb, lsb) CData name ///< Declare bidir signal, 1-8 bits +#define VL_INOUT16(name, msb, lsb) SData name ///< Declare bidir signal, 9-16 bits +#define VL_INOUT64(name, msb, lsb) QData name ///< Declare bidir signal, 33-64 bits +#define VL_INOUT(name, msb, lsb) IData name ///< Declare bidir signal, 17-32 bits +#define VL_INOUTW(name, msb, lsb, words) WData name[words] ///< Declare bidir signal, 65+ bits +#define VL_OUT8(name, msb, lsb) CData name ///< Declare output signal, 1-8 bits +#define VL_OUT16(name, msb, lsb) SData name ///< Declare output signal, 9-16 bits +#define VL_OUT64(name, msb, lsb) QData name ///< Declare output signal, 33-64bits +#define VL_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits +#define VL_OUTW(name, msb, lsb, words) WData name[words] ///< Declare output signal, 65+ bits + +//=================================================================== +// Shuffle RNG + +extern vluint64_t vl_rand64() VL_MT_SAFE; + +class VlURNG final { +public: + using result_type = size_t; + static constexpr size_t min() { return 0; } + static constexpr size_t max() { return 1ULL << 31; } + size_t operator()() { return VL_MASK_I(31) & vl_rand64(); } +}; + +//=================================================================== +// Readmem/Writemem operation classes + +class VlReadMem final { + bool m_hex; // Hex format + int m_bits; // Bit width of values + const std::string& m_filename; // Filename + QData m_end; // End address (as specified by user) + FILE* m_fp; // File handle for filename + QData m_addr; // Next address to read + int m_linenum; // Line number last read from file +public: + VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end); + ~VlReadMem(); + bool isOpen() const { return m_fp != nullptr; } + int linenum() const { return m_linenum; } + bool get(QData& addrr, std::string& valuer); + void setData(void* valuep, const std::string& rhs); +}; + +class VlWriteMem final { + bool m_hex; // Hex format + int m_bits; // Bit width of values + FILE* m_fp; // File handle for filename + QData m_addr; // Next address to write +public: + VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end); + ~VlWriteMem(); + bool isOpen() const { return m_fp != nullptr; } + void print(QData addr, bool addrstamp, const void* valuep); +}; + +//=================================================================== +/// Verilog wide packed bit container. +/// Similar to std::array, but lighter weight, only methods needed +/// by Verilator, to help compile time. +/// +/// A 'struct' as we want this to be an aggregate type that allows +/// static aggregate initialization. Consider data members private. +/// +/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32 +/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be +/// zero in memory, but during intermediate operations in the Verilated +/// internals is unpredictable. + +static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE; + +template struct VlWide final { + // MEMBERS + // This should be the only data member, otherwise generated static initializers need updating + EData m_storage[T_Words]; // Contents of the packed array + + // CONSTRUCTORS + // Default constructors and destructor are used. Note however that C++20 requires that + // aggregate types do not have a user declared constructor, not even an explicitly defaulted + // one. + + // OPERATOR METHODS + // Default copy assignment operators are used. + operator WDataOutP() { return &m_storage[0]; } // This also allows [] + operator WDataInP() const { return &m_storage[0]; } // This also allows [] + + // METHODS + const EData& at(size_t index) const { return m_storage[index]; } + EData& at(size_t index) { return m_storage[index]; } + WData* data() { return &m_storage[0]; } + const WData* data() const { return &m_storage[0]; } + bool operator<(const VlWide& rhs) const { + return _vl_cmp_w(T_Words, data(), rhs.data()) < 0; + } +}; + +// Convert a C array to std::array reference by pointer magic, without copy. +// Data type (second argument) is so the function template can automatically generate. +template +VlWide& VL_CVT_W_A(const WDataInP inp, const VlWide&) { + return *((VlWide*)inp); +} + +template std::string VL_TO_STRING(const VlWide& obj) { + return VL_TO_STRING_W(T_Words, obj.data()); +} + +//=================================================================== +// Verilog queue and dynamic array container +// There are no multithreaded locks on this; the base variable must +// be protected by other means +// +// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound +// For dynamic arrays it is always zero +template class VlQueue final { +private: + // TYPES + using Deque = std::deque; + +public: + using const_iterator = typename Deque::const_iterator; + +private: + // MEMBERS + Deque m_deque; // State of the assoc array + T_Value m_defaultValue; // Default value + +public: + // CONSTRUCTORS + // m_defaultValue isn't defaulted. Caller's constructor must do it. + VlQueue() = default; + ~VlQueue() = default; + VlQueue(const VlQueue&) = default; + VlQueue(VlQueue&&) = default; + VlQueue& operator=(const VlQueue&) = default; + VlQueue& operator=(VlQueue&&) = default; + + // Standard copy constructor works. Verilog: assoca = assocb + // Also must allow conversion from a different T_MaxSize queue + template VlQueue operator=(const VlQueue& rhs) { + m_deque = rhs.privateDeque(); + if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1); + return *this; + } + + static VlQueue cons(const T_Value& lhs) { + VlQueue out; + out.push_back(lhs); + return out; + } + static VlQueue cons(const T_Value& lhs, const T_Value& rhs) { + VlQueue out; + out.push_back(rhs); + out.push_back(lhs); + return out; + } + static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) { + VlQueue out = lhs; + out.push_front(rhs); + return out; + } + static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) { + VlQueue out = rhs; + out.push_back(lhs); + return out; + } + static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) { + VlQueue out = rhs; + for (const auto& i : lhs.m_deque) out.push_back(i); + return out; + } + + // METHODS + T_Value& atDefault() { return m_defaultValue; } + const T_Value& atDefault() const { return m_defaultValue; } + const Deque& privateDeque() const { return m_deque; } + + // Size. Verilog: function int size(), or int num() + int size() const { return m_deque.size(); } + // Clear array. Verilog: function void delete([input index]) + void clear() { m_deque.clear(); } + void erase(vlsint32_t index) { + if (VL_LIKELY(index >= 0 && index < m_deque.size())) + m_deque.erase(m_deque.begin() + index); + } + + // Dynamic array new[] becomes a renew() + void renew(size_t size) { + clear(); + m_deque.resize(size, atDefault()); + } + // Dynamic array new[]() becomes a renew_copy() + void renew_copy(size_t size, const VlQueue& rhs) { + if (size == 0) { + clear(); + } else { + *this = rhs; + m_deque.resize(size, atDefault()); + } + } + + // function void q.push_front(value) + void push_front(const T_Value& value) { + m_deque.push_front(value); + if (VL_UNLIKELY(T_MaxSize != 0 && m_deque.size() > T_MaxSize)) m_deque.pop_back(); + } + // function void q.push_back(value) + void push_back(const T_Value& value) { + if (VL_LIKELY(T_MaxSize == 0 || m_deque.size() < T_MaxSize)) m_deque.push_back(value); + } + // function value_t q.pop_front(); + T_Value pop_front() { + if (m_deque.empty()) return m_defaultValue; + T_Value v = m_deque.front(); + m_deque.pop_front(); + return v; + } + // function value_t q.pop_back(); + T_Value pop_back() { + if (m_deque.empty()) return m_defaultValue; + T_Value v = m_deque.back(); + m_deque.pop_back(); + return v; + } + + // Setting. Verilog: assoc[index] = v + // Can't just overload operator[] or provide a "at" reference to set, + // because we need to be able to insert only when the value is set + T_Value& at(vlsint32_t index) { + static T_Value s_throwAway; + // Needs to work for dynamic arrays, so does not use T_MaxSize + if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) { + s_throwAway = atDefault(); + return s_throwAway; + } else { + return m_deque[index]; + } + } + // Accessing. Verilog: v = assoc[index] + const T_Value& at(vlsint32_t index) const { + static T_Value s_throwAway; + // Needs to work for dynamic arrays, so does not use T_MaxSize + if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) { + return atDefault(); + } else { + return m_deque[index]; + } + } + // function void q.insert(index, value); + void insert(vlsint32_t index, const T_Value& value) { + if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return; + m_deque.insert(m_deque.begin() + index, value); + } + + // Return slice q[lsb:msb] + VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const { + VlQueue out; + if (VL_UNLIKELY(lsb < 0)) lsb = 0; + if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1; + if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1; + for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]); + return out; + } + + // For save/restore + const_iterator begin() const { return m_deque.begin(); } + const_iterator end() const { return m_deque.end(); } + + // Methods + void sort() { std::sort(m_deque.begin(), m_deque.end()); } + template void sort(Func with_func) { + // with_func returns arbitrary type to use for the sort comparison + std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) { + // index number is meaninless with sort, as it changes + return with_func(0, a) < with_func(0, b); + }); + } + void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); } + template void rsort(Func with_func) { + // with_func returns arbitrary type to use for the sort comparison + std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) { + // index number is meaninless with sort, as it changes + return with_func(0, a) < with_func(0, b); + }); + } + void reverse() { std::reverse(m_deque.begin(), m_deque.end()); } + void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG{}); } + VlQueue unique() const { + VlQueue out; + std::unordered_set saw; + for (const auto& i : m_deque) { + auto it = saw.find(i); + if (it == saw.end()) { + saw.insert(it, i); + out.push_back(i); + } + } + return out; + } + VlQueue unique_index() const { + VlQueue out; + IData index = 0; + std::unordered_set saw; + for (const auto& i : m_deque) { + auto it = saw.find(i); + if (it == saw.end()) { + saw.insert(it, i); + out.push_back(index); + } + ++index; + } + return out; + } + template VlQueue find(Func with_func) const { + VlQueue out; + IData index = 0; + for (const auto& i : m_deque) { + if (with_func(index, i)) out.push_back(i); + ++index; + } + return out; + } + template VlQueue find_index(Func with_func) const { + VlQueue out; + IData index = 0; + for (const auto& i : m_deque) { + if (with_func(index, i)) out.push_back(index); + ++index; + } + return out; + } + template VlQueue find_first(Func with_func) const { + // Can't use std::find_if as need index number + IData index = 0; + for (const auto& i : m_deque) { + if (with_func(index, i)) return VlQueue::cons(i); + ++index; + } + return VlQueue{}; + } + template VlQueue find_first_index(Func with_func) const { + IData index = 0; + for (const auto& i : m_deque) { + if (with_func(index, i)) return VlQueue::cons(index); + ++index; + } + return VlQueue{}; + } + template VlQueue find_last(Func with_func) const { + IData index = m_deque.size() - 1; + for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) { + if (with_func(index, *it)) return VlQueue::cons(*it); + --index; + } + return VlQueue{}; + } + template VlQueue find_last_index(Func with_func) const { + IData index = m_deque.size() - 1; + for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) { + if (with_func(index, *it)) return VlQueue::cons(index); + --index; + } + return VlQueue{}; + } + + // Reduction operators + VlQueue min() const { + if (m_deque.empty()) return VlQueue{}; + const auto it = std::min_element(m_deque.begin(), m_deque.end()); + return VlQueue::cons(*it); + } + VlQueue max() const { + if (m_deque.empty()) return VlQueue{}; + const auto it = std::max_element(m_deque.begin(), m_deque.end()); + return VlQueue::cons(*it); + } + + T_Value r_sum() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_deque) out += i; + return out; + } + template T_Value r_sum(Func with_func) const { + T_Value out(0); // Type must have assignment operator + IData index = 0; + for (const auto& i : m_deque) out += with_func(index++, i); + return out; + } + T_Value r_product() const { + if (m_deque.empty()) return T_Value(0); + auto it = m_deque.begin(); + T_Value out{*it}; + ++it; + for (; it != m_deque.end(); ++it) out *= *it; + return out; + } + template T_Value r_product(Func with_func) const { + if (m_deque.empty()) return T_Value(0); + auto it = m_deque.begin(); + IData index = 0; + T_Value out{with_func(index, *it)}; + ++it; + ++index; + for (; it != m_deque.end(); ++it) out *= with_func(index++, *it); + return out; + } + T_Value r_and() const { + if (m_deque.empty()) return T_Value(0); + auto it = m_deque.begin(); + T_Value out{*it}; + ++it; + for (; it != m_deque.end(); ++it) out &= *it; + return out; + } + template T_Value r_and(Func with_func) const { + if (m_deque.empty()) return T_Value(0); + auto it = m_deque.begin(); + IData index = 0; + T_Value out{with_func(index, *it)}; + ++it; + ++index; + for (; it != m_deque.end(); ++it) out &= with_func(index, *it); + return out; + } + T_Value r_or() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_deque) out |= i; + return out; + } + template T_Value r_or(Func with_func) const { + T_Value out(0); // Type must have assignment operator + IData index = 0; + for (const auto& i : m_deque) out |= with_func(index++, i); + return out; + } + T_Value r_xor() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_deque) out ^= i; + return out; + } + template T_Value r_xor(Func with_func) const { + T_Value out(0); // Type must have assignment operator + IData index = 0; + for (const auto& i : m_deque) out ^= with_func(index++, i); + return out; + } + + // Dumping. Verilog: str = $sformatf("%p", assoc) + std::string to_string() const { + if (m_deque.empty()) return "'{}"; // No trailing space + std::string out = "'{"; + std::string comma; + for (const auto& i : m_deque) { + out += comma + VL_TO_STRING(i); + comma = ", "; + } + return out + "} "; + } +}; + +template std::string VL_TO_STRING(const VlQueue& obj) { + return obj.to_string(); +} + +//=================================================================== +// Verilog associative array container +// There are no multithreaded locks on this; the base variable must +// be protected by other means +// +template class VlAssocArray final { +private: + // TYPES + using Map = std::map; + +public: + using const_iterator = typename Map::const_iterator; + +private: + // MEMBERS + Map m_map; // State of the assoc array + T_Value m_defaultValue; // Default value + +public: + // CONSTRUCTORS + // m_defaultValue isn't defaulted. Caller's constructor must do it. + VlAssocArray() = default; + ~VlAssocArray() = default; + VlAssocArray(const VlAssocArray&) = default; + VlAssocArray(VlAssocArray&&) = default; + VlAssocArray& operator=(const VlAssocArray&) = default; + VlAssocArray& operator=(VlAssocArray&&) = default; + + // METHODS + T_Value& atDefault() { return m_defaultValue; } + const T_Value& atDefault() const { return m_defaultValue; } + + // Size of array. Verilog: function int size(), or int num() + int size() const { return m_map.size(); } + // Clear array. Verilog: function void delete([input index]) + void clear() { m_map.clear(); } + void erase(const T_Key& index) { m_map.erase(index); } + // Return 0/1 if element exists. Verilog: function int exists(input index) + int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); } + // Return first element. Verilog: function int first(ref index); + int first(T_Key& indexr) const { + const auto it = m_map.cbegin(); + if (it == m_map.end()) return 0; + indexr = it->first; + return 1; + } + // Return last element. Verilog: function int last(ref index) + int last(T_Key& indexr) const { + const auto it = m_map.crbegin(); + if (it == m_map.rend()) return 0; + indexr = it->first; + return 1; + } + // Return next element. Verilog: function int next(ref index) + int next(T_Key& indexr) const { + auto it = m_map.find(indexr); + if (VL_UNLIKELY(it == m_map.end())) return 0; + ++it; + if (VL_UNLIKELY(it == m_map.end())) return 0; + indexr = it->first; + return 1; + } + // Return prev element. Verilog: function int prev(ref index) + int prev(T_Key& indexr) const { + auto it = m_map.find(indexr); + if (VL_UNLIKELY(it == m_map.end())) return 0; + if (VL_UNLIKELY(it == m_map.begin())) return 0; + --it; + indexr = it->first; + return 1; + } + // Setting. Verilog: assoc[index] = v + // Can't just overload operator[] or provide a "at" reference to set, + // because we need to be able to insert only when the value is set + T_Value& at(const T_Key& index) { + const auto it = m_map.find(index); + if (it == m_map.end()) { + std::pair pit = m_map.emplace(index, m_defaultValue); + return pit.first->second; + } + return it->second; + } + // Accessing. Verilog: v = assoc[index] + const T_Value& at(const T_Key& index) const { + const auto it = m_map.find(index); + if (it == m_map.end()) { + return m_defaultValue; + } else { + return it->second; + } + } + // Setting as a chained operation + VlAssocArray& set(const T_Key& index, const T_Value& value) { + at(index) = value; + return *this; + } + VlAssocArray& setDefault(const T_Value& value) { + atDefault() = value; + return *this; + } + + // For save/restore + const_iterator begin() const { return m_map.begin(); } + const_iterator end() const { return m_map.end(); } + + // Methods + VlQueue unique() const { + VlQueue out; + std::set saw; + for (const auto& i : m_map) { + auto it = saw.find(i.second); + if (it == saw.end()) { + saw.insert(it, i.second); + out.push_back(i.second); + } + } + return out; + } + VlQueue unique_index() const { + VlQueue out; + std::set saw; + for (const auto& i : m_map) { + auto it = saw.find(i.second); + if (it == saw.end()) { + saw.insert(it, i.second); + out.push_back(i.first); + } + } + return out; + } + template VlQueue find(Func with_func) const { + VlQueue out; + for (const auto& i : m_map) + if (with_func(i.first, i.second)) out.push_back(i.second); + return out; + } + template VlQueue find_index(Func with_func) const { + VlQueue out; + for (const auto& i : m_map) + if (with_func(i.first, i.second)) out.push_back(i.first); + return out; + } + template VlQueue find_first(Func with_func) const { + const auto it + = std::find_if(m_map.begin(), m_map.end(), [=](const std::pair& i) { + return with_func(i.first, i.second); + }); + if (it == m_map.end()) return VlQueue{}; + return VlQueue::cons(it->second); + } + template VlQueue find_first_index(Func with_func) const { + const auto it + = std::find_if(m_map.begin(), m_map.end(), [=](const std::pair& i) { + return with_func(i.first, i.second); + }); + if (it == m_map.end()) return VlQueue{}; + return VlQueue::cons(it->first); + } + template VlQueue find_last(Func with_func) const { + const auto it + = std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair& i) { + return with_func(i.first, i.second); + }); + if (it == m_map.rend()) return VlQueue{}; + return VlQueue::cons(it->second); + } + template VlQueue find_last_index(Func with_func) const { + const auto it + = std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair& i) { + return with_func(i.first, i.second); + }); + if (it == m_map.rend()) return VlQueue{}; + return VlQueue::cons(it->first); + } + + // Reduction operators + VlQueue min() const { + if (m_map.empty()) return VlQueue(); + const auto it = std::min_element( + m_map.begin(), m_map.end(), + [](const std::pair& a, const std::pair& b) { + return a.second < b.second; + }); + return VlQueue::cons(it->second); + } + VlQueue max() const { + if (m_map.empty()) return VlQueue(); + const auto it = std::max_element( + m_map.begin(), m_map.end(), + [](const std::pair& a, const std::pair& b) { + return a.second < b.second; + }); + return VlQueue::cons(it->second); + } + + T_Value r_sum() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out += i.second; + return out; + } + template T_Value r_sum(Func with_func) const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out += with_func(i.first, i.second); + return out; + } + T_Value r_product() const { + if (m_map.empty()) return T_Value(0); + auto it = m_map.begin(); + T_Value out{it->second}; + ++it; + for (; it != m_map.end(); ++it) out *= it->second; + return out; + } + template T_Value r_product(Func with_func) const { + if (m_map.empty()) return T_Value(0); + auto it = m_map.begin(); + T_Value out{with_func(it->first, it->second)}; + ++it; + for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second); + return out; + } + T_Value r_and() const { + if (m_map.empty()) return T_Value(0); + auto it = m_map.begin(); + T_Value out{it->second}; + ++it; + for (; it != m_map.end(); ++it) out &= it->second; + return out; + } + template T_Value r_and(Func with_func) const { + if (m_map.empty()) return T_Value(0); + auto it = m_map.begin(); + T_Value out{with_func(it->first, it->second)}; + ++it; + for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second); + return out; + } + T_Value r_or() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out |= i.second; + return out; + } + template T_Value r_or(Func with_func) const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out |= with_func(i.first, i.second); + return out; + } + T_Value r_xor() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out ^= i.second; + return out; + } + template T_Value r_xor(Func with_func) const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out ^= with_func(i.first, i.second); + return out; + } + + // Dumping. Verilog: str = $sformatf("%p", assoc) + std::string to_string() const { + if (m_map.empty()) return "'{}"; // No trailing space + std::string out = "'{"; + std::string comma; + for (const auto& i : m_map) { + out += comma + VL_TO_STRING(i.first) + ":" + VL_TO_STRING(i.second); + comma = ", "; + } + // Default not printed - maybe random init data + return out + "} "; + } +}; + +template +std::string VL_TO_STRING(const VlAssocArray& obj) { + return obj.to_string(); +} + +template +void VL_READMEM_N(bool hex, int bits, const std::string& filename, + VlAssocArray& obj, QData start, QData end) VL_MT_SAFE { + VlReadMem rmem{hex, bits, filename, start, end}; + if (VL_UNLIKELY(!rmem.isOpen())) return; + while (true) { + QData addr; + std::string data; + if (rmem.get(addr /*ref*/, data /*ref*/)) { + rmem.setData(&(obj.at(addr)), data); + } else { + break; + } + } +} + +template +void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename, + const VlAssocArray& obj, QData start, QData end) VL_MT_SAFE { + VlWriteMem wmem{hex, bits, filename, start, end}; + if (VL_UNLIKELY(!wmem.isOpen())) return; + for (const auto& i : obj) { + const QData addr = i.first; + if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second)); + } +} + +//=================================================================== +/// Verilog unpacked array container +/// For when a standard C++[] array is not sufficient, e.g. an +/// array under a queue, or methods operating on the array. +/// +/// A 'struct' as we want this to be an aggregate type that allows +/// static aggregate initialization. Consider data members private. +/// +/// This class may get exposed to a Verilated Model's top I/O, if the top +/// IO has an unpacked array. + +template struct VlUnpacked final { + // MEMBERS + // This should be the only data member, otherwise generated static initializers need updating + T_Value m_storage[T_Depth]; // Contents of the unpacked array + + // CONSTRUCTORS + // Default constructors and destructor are used. Note however that C++20 requires that + // aggregate types do not have a user declared constructor, not even an explicitly defaulted + // one. + + // OPERATOR METHODS + // Default copy assignment operators are used. + + // METHODS + // Raw access + WData* data() { return &m_storage[0]; } + const WData* data() const { return &m_storage[0]; } + + T_Value& operator[](size_t index) { return m_storage[index]; }; + const T_Value& operator[](size_t index) const { return m_storage[index]; }; + + // Dumping. Verilog: str = $sformatf("%p", assoc) + std::string to_string() const { + std::string out = "'{"; + std::string comma; + for (int i = 0; i < T_Depth; ++i) { + out += comma + VL_TO_STRING(m_storage[i]); + comma = ", "; + } + return out + "} "; + } +}; + +template +std::string VL_TO_STRING(const VlUnpacked& obj) { + return obj.to_string(); +} + +//=================================================================== +// Verilog class reference container +// There are no multithreaded locks on this; the base variable must +// be protected by other means + +#define VlClassRef std::shared_ptr + +template // T typically of type VlClassRef +inline T VL_NULL_CHECK(T t, const char* filename, int linenum) { + if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum); + return t; +} + +template +static inline bool VL_CAST_DYNAMIC(VlClassRef in, VlClassRef& outr) { + VlClassRef casted = std::dynamic_pointer_cast(in); + if (VL_LIKELY(casted)) { + outr = casted; + return true; + } else { + return false; + } +} + +//====================================================================== + +#endif // Guard diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index 798618643..1a21bdbf2 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -102,7 +102,7 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep) { } void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; if (isOpen()) return; // Set member variables @@ -120,7 +120,7 @@ void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) { void VerilatedVcd::openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex) { // Open next filename in concat sequence, mangle filename if // incFilename is true. - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; openNextImp(incFilename); } @@ -195,11 +195,11 @@ void VerilatedVcd::makeNameMap() { if (!hiername.empty() && hiername[0] == '\t') nullScope = true; } if (nullScope) { - NameMap* newmapp = new NameMap; + NameMap* const newmapp = new NameMap; for (const auto& i : *m_namemapp) { const std::string& hiername = i.first; const std::string& decl = i.second; - std::string newname = std::string("top"); + std::string newname{"top"}; if (hiername[0] != '\t') newname += ' '; newname += hiername; newmapp->emplace(newname, decl); @@ -243,7 +243,7 @@ void VerilatedVcd::closeErr() { void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) { // This function is on the flush() call path - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; if (!isOpen()) return; if (m_evcd) { printStr("$vcdclose "); @@ -257,7 +257,7 @@ void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) { } void VerilatedVcd::flush() VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; VerilatedTrace::flushBase(); bufferFlush(); } @@ -271,8 +271,9 @@ void VerilatedVcd::printStr(const char* str) { } void VerilatedVcd::printQuad(vluint64_t n) { - char buf[100]; - VL_SNPRINTF(buf, 100, "%" VL_PRI64 "u", n); + constexpr size_t LEN_STR_QUAD = 40; + char buf[LEN_STR_QUAD]; + VL_SNPRINTF(buf, LEN_STR_QUAD, "%" VL_PRI64 "u", n); printStr(buf); } @@ -311,7 +312,7 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE { // LCOV_EXCL_START // write failed, presume error (perhaps out of disk space) std::string msg - = std::string("VerilatedVcd::bufferFlush: ") + std::strerror(errno); + = std::string{"VerilatedVcd::bufferFlush: "} + std::strerror(errno); VL_FATAL_MT("", 0, "", msg.c_str()); closeErr(); break; @@ -331,7 +332,7 @@ char* VerilatedVcd::writeCode(char* writep, vluint32_t code) { *writep++ = static_cast('!' + code % 94); code /= 94; while (code) { - code--; + --code; *writep++ = static_cast('!' + code % 94); code /= 94; } @@ -355,9 +356,9 @@ void VerilatedVcd::dumpHeader() { const time_t tick = time(nullptr); tm ticktm; VL_LOCALTIME_R(&tick, &ticktm); - constexpr int bufsize = 50; - char buf[bufsize]; - std::strftime(buf, bufsize, "%c", &ticktm); + constexpr size_t LEN_BUF = 50; + char buf[LEN_BUF]; + std::strftime(buf, LEN_BUF, "%c", &ticktm); printStr(buf); } printStr(" $end\n"); @@ -393,8 +394,8 @@ void VerilatedVcd::dumpHeader() { // Skip common prefix, it must break at a space or tab for (; *np && (*np == *lp); np++, lp++) {} while (np != hiername && *np && *np != ' ' && *np != '\t') { - np--; - lp--; + --np; + --lp; } // printf("hier %s\n lp=%s\n np=%s\n",hiername,lp,np); @@ -430,9 +431,9 @@ void VerilatedVcd::dumpHeader() { for (; *np && *np != ' ' && *np != '\t'; np++) { if (*np == '[') { - printStr("("); + printStr("["); } else if (*np == ']') { - printStr(")"); + printStr("]"); } else if (!(*np & '\x80')) { *m_writep++ = *np; } @@ -531,7 +532,7 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep, decl += " "; decl += basename; if (array) { - VL_SNPRINTF(buf, bufsize, "(%d)", arraynum); + VL_SNPRINTF(buf, bufsize, "[%d]", arraynum); decl += buf; hiername += buf; } @@ -896,9 +897,9 @@ 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_unit(std::string{"1ms"}); vcdp->set_time_resolution("1ns"); - vcdp->set_time_resolution(std::string("1ns")); + vcdp->set_time_resolution(std::string{"1ns"}); vcdp->spTrace()->addInitCb(&vcdInit, 0); vcdp->spTrace()->addFullCb(&vcdFull, 0); vcdp->spTrace()->addChgCb(&vcdChange, 0); @@ -927,7 +928,7 @@ void vcdTestMain(const char* filenamep) { vcdp->dump(++timestamp); vcdp->dump(++timestamp); # ifdef VERILATED_VCD_TEST_64BIT - vluint64_t bytesPerDump = 15ULL; + const vluint64_t bytesPerDump = 15ULL; for (vluint64_t i = 0; i < ((1ULL << 32) / bytesPerDump); i++) { v1 = i; vcdp->dump(++timestamp); diff --git a/include/verilated_vcd_sc.h b/include/verilated_vcd_sc.h index de6e6d943..a79a9c61a 100644 --- a/include/verilated_vcd_sc.h +++ b/include/verilated_vcd_sc.h @@ -44,9 +44,9 @@ public: // We want to avoid a depreciated warning, but still be back compatible. // Turning off the message just for this still results in an // annoying "to turn off" message. - const sc_time t1sec(1, SC_SEC); + const sc_time t1sec{1, SC_SEC}; if (t1sec.to_default_time_units() != 0) { - const sc_time tunits(1.0 / t1sec.to_default_time_units(), SC_SEC); + const sc_time tunits{1.0 / t1sec.to_default_time_units(), SC_SEC}; spTrace()->set_time_unit(tunits.to_string()); } spTrace()->set_time_resolution(sc_get_time_resolution().to_string()); diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index eb9e254a3..5b7bbfe5c 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -80,7 +80,7 @@ public: static const size_t chunk = 96; if (VL_UNCOVERABLE(size > chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk"); if (VL_LIKELY(t_freeHead)) { - vluint8_t* newp = t_freeHead; + vluint8_t* const newp = t_freeHead; t_freeHead = *(reinterpret_cast(newp)); *(reinterpret_cast(newp)) = activeMagic(); return newp + 8; @@ -91,7 +91,7 @@ public: return newp + 8; } static void operator delete(void* obj, size_t /*size*/)VL_MT_SAFE { - vluint8_t* oldp = (static_cast(obj)) - 8; + vluint8_t* const oldp = (static_cast(obj)) - 8; if (VL_UNLIKELY(*(reinterpret_cast(oldp)) != activeMagic())) { VL_FATAL_MT(__FILE__, __LINE__, "", "vpi_release_handle() called on same object twice, or on non-Verilator " @@ -128,7 +128,7 @@ class VerilatedVpioTimedCb final : public VerilatedVpio { public: VerilatedVpioTimedCb(vluint64_t id, QData time) - : m_id(id) + : m_id{id} , m_time{time} {} virtual ~VerilatedVpioTimedCb() override = default; static VerilatedVpioTimedCb* castp(vpiHandle h) { @@ -147,7 +147,7 @@ class VerilatedVpioReasonCb final : public VerilatedVpio { public: // cppcheck-suppress uninitVar // m_value VerilatedVpioReasonCb(vluint64_t id, PLI_INT32 reason) - : m_id(id) + : m_id{id} , m_reason{reason} {} virtual ~VerilatedVpioReasonCb() override = default; static VerilatedVpioReasonCb* castp(vpiHandle h) { @@ -200,7 +200,7 @@ public: virtual const char* name() const override { return m_varp->name(); } virtual const char* fullname() const override { static VL_THREAD_LOCAL std::string t_out; - t_out = std::string(m_scopep->name()) + "." + name(); + t_out = std::string{m_scopep->name()} + "." + name(); return t_out.c_str(); } }; @@ -208,7 +208,7 @@ public: class VerilatedVpioParam final : public VerilatedVpioVarBase { public: VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep) - : VerilatedVpioVarBase(varp, scopep) {} + : VerilatedVpioVarBase{varp, scopep} {} virtual ~VerilatedVpioParam() override = default; static VerilatedVpioParam* castp(vpiHandle h) { @@ -252,7 +252,7 @@ public: return nullptr; } m_done = true; - return ((new VerilatedVpioRange(m_range))->castVpiHandle()); + return ((new VerilatedVpioRange{m_range})->castVpiHandle()); } }; @@ -286,13 +286,13 @@ protected: public: VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep) - : VerilatedVpioVarBase(varp, scopep) { + : VerilatedVpioVarBase{varp, scopep} { m_mask.u32 = VL_MASK_I(varp->packed().elements()); m_entSize = varp->entSize(); m_varDatap = varp->datap(); } explicit VerilatedVpioVar(const VerilatedVpioVar* varp) - : VerilatedVpioVarBase(varp) { + : VerilatedVpioVarBase{varp} { if (varp) { m_mask.u32 = varp->m_mask.u32; m_entSize = varp->m_entSize; @@ -343,9 +343,10 @@ 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[25]; - VL_SNPRINTF(num, 25, "%d", m_index); - t_out = std::string(scopep()->name()) + "." + name() + "[" + num + "]"; + constexpr size_t LEN_MAX_INDEX = 25; + char num[LEN_MAX_INDEX]; + VL_SNPRINTF(num, LEN_MAX_INDEX, "%d", m_index); + t_out = std::string{scopep()->name()} + "." + name() + "[" + num + "]"; return t_out.c_str(); } }; @@ -379,7 +380,7 @@ public: delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle return nullptr; } - return ((new VerilatedVpioVar(&(m_it->second), m_scopep))->castVpiHandle()); + return ((new VerilatedVpioVar{&(m_it->second), m_scopep})->castVpiHandle()); } delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle return nullptr; // End of list - only one deep @@ -457,7 +458,7 @@ public: return nullptr; } const VerilatedScope* modp = *m_it++; - return (new VerilatedVpioModule(modp))->castVpiHandle(); + return (new VerilatedVpioModule{modp})->castVpiHandle(); } }; @@ -475,16 +476,16 @@ class VerilatedVpiCbHolder final { public: // cppcheck-suppress uninitVar // m_value VerilatedVpiCbHolder(vluint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop) - : m_id(id) - , m_cbData(*cbDatap) - , m_varo(varop) { + : m_id{id} + , m_cbData{*cbDatap} + , m_varo{varop} { m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal; m_cbData.value = &m_value; if (varop) { m_cbData.obj = m_varo.castVpiHandle(); m_varo.createPrevDatap(); } else { - m_cbData.obj = NULL; + m_cbData.obj = nullptr; } } ~VerilatedVpiCbHolder() = default; @@ -626,16 +627,18 @@ public: continue; } VerilatedVpiCbHolder& ho = *it++; - if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) { - void* newDatap = varop->varDatap(); - void* prevDatap = varop->prevDatap(); // Was malloced when we added the callback + if (VerilatedVpioVar* const varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) { + void* const newDatap = varop->varDatap(); + void* const prevDatap + = varop->prevDatap(); // Was malloced when we added the callback VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n", - varop->fullname(), *((CData*)newDatap), - *((CData*)prevDatap), newDatap, prevDatap);); + varop->fullname(), *(static_cast(newDatap)), + *(static_cast(prevDatap)), newDatap, + prevDatap);); if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) { - VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" VL_PRI64 - "d %s v[0]=%d\n", - ho.id(), varop->fullname(), *((CData*)newDatap));); + VL_DEBUG_IF_PLI( + VL_DBG_MSGF("- vpi: value_callback %" VL_PRI64 "d %s v[0]=%d\n", ho.id(), + varop->fullname(), *(static_cast(newDatap)));); update.insert(varop); vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value); (ho.cb_rtnp())(ho.cb_datap()); @@ -706,7 +709,8 @@ public: va_end(args); m_errorInfo.state = vpiPLI; t_filehold = file; - setError((PLI_BYTE8*)m_buff, nullptr, const_cast(t_filehold.c_str()), line); + setError(static_cast(m_buff), nullptr, + const_cast(t_filehold.c_str()), line); } p_vpi_error_info getError() { if (m_flag) return &m_errorInfo; @@ -758,7 +762,7 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() { VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE { VerilatedVpiImp::assertOneCheck(); - if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError(); + if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError; return s().m_errorInfop; } @@ -1108,13 +1112,13 @@ const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) VL_MT_SAFE { #define SELF_CHECK_RESULT_CSTR(got, exp) \ if (0 != std::strcmp((got), (exp))) { \ std::string msg \ - = std::string("%Error: ") + "GOT = '" + got + "'" + " EXP = '" + exp + "'"; \ + = std::string{"%Error: "} + "GOT = '" + (got) + "'" + " EXP = '" + (exp) + "'"; \ VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); \ } #define SELF_CHECK_ENUM_STR(fn, enumn) \ do { \ - const char* strVal = VerilatedVpiError::fn(enumn); \ + const char* const strVal = VerilatedVpiError::fn(enumn); \ SELF_CHECK_RESULT_CSTR(strVal, #enumn); \ } while (0) @@ -1358,7 +1362,7 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope); std::string scopeAndName = namep; if (voScopep) { - scopeAndName = std::string(voScopep->fullname()) + "." + namep; + scopeAndName = std::string{voScopep->fullname()} + "." + namep; namep = const_cast(scopeAndName.c_str()); } { @@ -1366,9 +1370,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { scopep = Verilated::threadContextp()->scopeFind(namep); if (scopep) { // Whole thing found as a scope if (scopep->type() == VerilatedScope::SCOPE_MODULE) { - return (new VerilatedVpioModule(scopep))->castVpiHandle(); + return (new VerilatedVpioModule{scopep})->castVpiHandle(); } else { - return (new VerilatedVpioScope(scopep))->castVpiHandle(); + return (new VerilatedVpioScope{scopep})->castVpiHandle(); } } const char* baseNamep = scopeAndName.c_str(); @@ -1376,7 +1380,8 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { const char* const dotp = std::strrchr(namep, '.'); if (VL_LIKELY(dotp)) { baseNamep = dotp + 1; - scopename = std::string(namep, dotp - namep); + const size_t len = dotp - namep; + scopename = std::string{namep, len}; } if (scopename.find('.') == std::string::npos) { @@ -1393,9 +1398,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { if (!varp) return nullptr; if (varp->isParam()) { - return (new VerilatedVpioParam(varp, scopep))->castVpiHandle(); + return (new VerilatedVpioParam{varp, scopep})->castVpiHandle(); } else { - return (new VerilatedVpioVar(varp, scopep))->castVpiHandle(); + return (new VerilatedVpioVar{varp, scopep})->castVpiHandle(); } } @@ -1405,7 +1410,7 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) { VerilatedVpiImp::assertOneCheck(); VL_VPI_ERROR_RESET_(); // Memory words are not indexable - VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object); + VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object); if (VL_UNLIKELY(vop)) return nullptr; const VerilatedVpioVar* const varop = VerilatedVpioVar::castp(object); if (VL_LIKELY(varop)) { @@ -1414,15 +1419,15 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) { if (VL_UNLIKELY(indx > varop->varp()->unpacked().left() || indx < varop->varp()->unpacked().right())) return nullptr; - return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx, - indx - varop->varp()->unpacked().right())) + return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx, + indx - varop->varp()->unpacked().right()}) ->castVpiHandle(); } if (VL_UNLIKELY(indx < varop->varp()->unpacked().left() || indx > varop->varp()->unpacked().right())) return nullptr; - return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx, - indx - varop->varp()->unpacked().left())) + return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx, + indx - varop->varp()->unpacked().left()}) ->castVpiHandle(); } VL_VPI_INTERNAL_(__FILE__, __LINE__, "%s : can't resolve handle", __func__); @@ -1437,12 +1442,12 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { VL_VPI_ERROR_RESET_(); switch (type) { case vpiLeftRange: { - if (VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object)) { + if (VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) { if (VL_UNLIKELY(!vop->rangep())) return nullptr; - return (new VerilatedVpioConst(vop->rangep()->left()))->castVpiHandle(); - } else if (VerilatedVpioRange* vop = VerilatedVpioRange::castp(object)) { + return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle(); + } else if (VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) { if (VL_UNLIKELY(!vop->rangep())) return nullptr; - return (new VerilatedVpioConst(vop->rangep()->left()))->castVpiHandle(); + return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle(); } VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned", @@ -1450,12 +1455,12 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { return nullptr; } case vpiRightRange: { - if (VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object)) { + if (VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) { if (VL_UNLIKELY(!vop->rangep())) return nullptr; - return (new VerilatedVpioConst(vop->rangep()->right()))->castVpiHandle(); - } else if (VerilatedVpioRange* vop = VerilatedVpioRange::castp(object)) { + return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle(); + } else if (VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) { if (VL_UNLIKELY(!vop->rangep())) return nullptr; - return (new VerilatedVpioConst(vop->rangep()->right()))->castVpiHandle(); + return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle(); } VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned", @@ -1463,19 +1468,20 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { return nullptr; } case vpiIndex: { - VerilatedVpioVar* vop = VerilatedVpioVar::castp(object); + VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; - return (new VerilatedVpioConst(vop->index()))->castVpiHandle(); + const vlsint32_t val = vop->index(); + return (new VerilatedVpioConst{val})->castVpiHandle(); } case vpiScope: { - VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object); + VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; - return (new VerilatedVpioScope(vop->scopep()))->castVpiHandle(); + return (new VerilatedVpioScope{vop->scopep()})->castVpiHandle(); } case vpiParent: { - VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object); + VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; - return (new VerilatedVpioVar(vop->varp(), vop->scopep()))->castVpiHandle(); + return (new VerilatedVpioVar{vop->varp(), vop->scopep()})->castVpiHandle(); } default: VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned", @@ -1505,7 +1511,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { VerilatedVpiError::strFromVpiMethod(type), vop->fullname(), vop->varp()->dims()); } - return (new VerilatedVpioMemoryWordIter(object, vop->varp()))->castVpiHandle(); + return (new VerilatedVpioMemoryWordIter{object, vop->varp()})->castVpiHandle(); } case vpiRange: { const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); @@ -1518,12 +1524,12 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { VerilatedVpiError::strFromVpiMethod(type), vop->fullname(), vop->varp()->dims()); } - return ((new VerilatedVpioRangeIter(vop->rangep()))->castVpiHandle()); + return ((new VerilatedVpioRangeIter{vop->rangep()})->castVpiHandle()); } case vpiReg: { const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; - return ((new VerilatedVpioVarIter(vop->scopep()))->castVpiHandle()); + return ((new VerilatedVpioVarIter{vop->scopep()})->castVpiHandle()); } case vpiModule: { const VerilatedVpioModule* const vop = VerilatedVpioModule::castp(object); @@ -1531,7 +1537,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { const VerilatedScope* const modp = vop ? vop->scopep() : nullptr; const auto it = vlstd::as_const(map)->find(const_cast(modp)); if (it == map->end()) return nullptr; - return ((new VerilatedVpioModuleIter(it->second))->castVpiHandle()); + return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle()); } default: VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned", @@ -1543,7 +1549,7 @@ vpiHandle vpi_scan(vpiHandle object) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_scan %p\n", object);); VerilatedVpiImp::assertOneCheck(); VL_VPI_ERROR_RESET_(); - VerilatedVpio* vop = VerilatedVpio::castp(object); + VerilatedVpio* const vop = VerilatedVpio::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; return vop->dovpi_scan(); } @@ -1689,7 +1695,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, const char* fullname) { if (!vl_check_format(varp, valuep, fullname, true)) return; // Maximum required size is for binary string, one byte per bit plus null termination - static VL_THREAD_LOCAL char t_outStr[1 + VL_MULS_MAX_WORDS * 32]; + static VL_THREAD_LOCAL char t_outStr[VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE + 1]; // cppcheck-suppress variableScope static VL_THREAD_LOCAL int t_outStrSz = sizeof(t_outStr) - 1; // We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal @@ -1697,7 +1703,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, if (valuep->format == vpiVectorVal) { // Vector pointer must come from our memory pool // It only needs to persist until the next vpi_get_value - static VL_THREAD_LOCAL t_vpi_vecval t_out[VL_MULS_MAX_WORDS * 2]; + static VL_THREAD_LOCAL t_vpi_vecval t_out[VL_VALUE_STRING_MAX_WORDS * 2]; valuep->value.vector = t_out; if (varp->vltype() == VLVT_UINT8) { t_out[0].aval = *(reinterpret_cast(varDatap)); @@ -1720,10 +1726,10 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, return; } else if (varp->vltype() == VLVT_WDATA) { int words = VL_WORDS_I(varp->packed().elements()); - if (VL_UNCOVERABLE(words >= VL_MULS_MAX_WORDS)) { - VL_FATAL_MT( - __FILE__, __LINE__, "", - "vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile"); + if (VL_UNCOVERABLE(words >= VL_VALUE_STRING_MAX_WORDS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value with more than VL_VALUE_STRING_MAX_WORDS; increase and " + "recompile"); } const WDataInP datap = (reinterpret_cast(varDatap)); for (int i = 0; i < words; ++i) { @@ -1743,9 +1749,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, 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)", + " as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)", __func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz, - VL_MULS_MAX_WORDS, bits); + VL_VALUE_STRING_MAX_WORDS, bits); } for (i = 0; i < bits; ++i) { char val = (datap[i >> 3] >> (i & 7)) & 1; @@ -1764,9 +1770,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, 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)", + " as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)", __func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz, - VL_MULS_MAX_WORDS, chars); + VL_VALUE_STRING_MAX_WORDS, chars); chars = t_outStrSz; } for (i = 0; i < chars; ++i) { @@ -1822,9 +1828,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, 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)", + " as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)", __func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz, - VL_MULS_MAX_WORDS, chars); + VL_VALUE_STRING_MAX_WORDS, chars); chars = t_outStrSz; } for (i = 0; i < chars; ++i) { @@ -1853,12 +1859,12 @@ 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_( - __FILE__, __LINE__, - "%s: Truncating string value of %s for %s" - " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", - __func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, - t_outStrSz, VL_MULS_MAX_WORDS, bytes); + VL_VPI_WARNING_(__FILE__, __LINE__, + "%s: Truncating string value of %s for %s" + " as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than " + "required (%d)", + __func__, VerilatedVpiError::strFromVpiVal(valuep->format), + fullname, t_outStrSz, VL_VALUE_STRING_MAX_WORDS, bytes); bytes = t_outStrSz; } for (i = 0; i < bytes; ++i) { @@ -1893,13 +1899,13 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) { VL_VPI_ERROR_RESET_(); if (VL_UNLIKELY(!valuep)) return; - if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) { + if (VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) { vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname()); return; } else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) { vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname()); return; - } else if (VerilatedVpioConst* vop = VerilatedVpioConst::castp(object)) { + } else if (VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) { if (valuep->format == vpiIntVal) { valuep->value.integer = vop->num(); return; @@ -2161,7 +2167,7 @@ void vpi_get_time(vpiHandle object, p_vpi_time time_p) { return; } else if (time_p->type == vpiScaledRealTime) { double dtime = VL_TIME_D(); - if (VerilatedVpioScope* vop = VerilatedVpioScope::castp(object)) { + if (VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object)) { const int scalePow10 = Verilated::threadContextp()->timeprecision() - vop->scopep()->timeunit(); const double scale = vl_time_multiplier(scalePow10); // e.g. 0.0001 @@ -2271,7 +2277,7 @@ PLI_INT32 vpi_free_object(vpiHandle object) { 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); + VerilatedVpio* const vop = VerilatedVpio::castp(object); VL_VPI_ERROR_RESET_(); if (VL_UNLIKELY(!vop)) return 0; VL_DO_DANGLING(delete vop, vop); diff --git a/include/verilatedos.h b/include/verilatedos.h index 06d07d3a6..1f64bf63c 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -421,7 +421,8 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type // Verilated function size macros #define VL_MULS_MAX_WORDS 16 ///< Max size in words of MULS operation -#define VL_TO_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation +#define VL_VALUE_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation +#define VL_VALUE_STRING_MAX_CHARS (VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE / VL_BYTESIZE) //========================================================================= // Base macros diff --git a/nodist/dot_importer b/nodist/dot_importer index fb6d6661a..cc9520a27 100755 --- a/nodist/dot_importer +++ b/nodist/dot_importer @@ -66,11 +66,11 @@ def cwrite(filename): fh.write(" auto* gp = &m_graph;\n") for ver in sorted(Vertexes, key=lambda ver: ver['num']): fh.write( - " auto* %s = new V3GraphTestVertex(gp, \"%s\"); if (%s) {}\n" + " auto* %s = new V3GraphTestVertex{gp, \"%s\"}; if (%s) {}\n" % (ver['name'], ver['name'], ver['name'])) fh.write("\n") for edge in Edges: - fh.write(" new V3GraphEdge(gp, %s, %s, %s, %s);\n" % + fh.write(" new V3GraphEdge{gp, %s, %s, %s, %s};\n" % (edge['from'], edge['to'], edge['weight'], "true" if edge['cutable'] else "false")) fh.write("}\n") diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 47288dec6..abb5f1a36 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -169,6 +169,7 @@ RAW_OBJS = \ V3Clean.o \ V3Clock.o \ V3Combine.o \ + V3Common.o \ V3Config.o \ V3Const__gen.o \ V3Coverage.o \ @@ -179,10 +180,11 @@ RAW_OBJS = \ V3DepthBlock.o \ V3Descope.o \ V3DupFinder.o \ - V3EmitC.o \ V3EmitCBase.o \ V3EmitCConstPool.o \ V3EmitCFunc.o \ + V3EmitCHeaders.o \ + V3EmitCImp.o \ V3EmitCInlines.o \ V3EmitCMain.o \ V3EmitCMake.o \ @@ -254,6 +256,7 @@ RAW_OBJS = \ V3Undriven.o \ V3Unknown.o \ V3Unroll.o \ + V3VariableOrder.o \ V3Waiver.o \ V3Width.o \ V3WidthSel.o \ diff --git a/src/V3Active.cpp b/src/V3Active.cpp index ee7debb29..76adf0e25 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -64,11 +64,11 @@ private: public: LatchDetectGraphVertex(V3Graph* graphp, const string& name, VertexType type = VT_BLOCK) - : V3GraphVertex(graphp) - , m_name(name) - , m_type(type) {} - virtual string name() const { return m_name + " " + typestr(); } - virtual string dotColor() const { return user() ? "green" : "black"; } + : V3GraphVertex{graphp} + , m_name{name} + , m_type{type} {} + virtual string name() const override { return m_name + " " + typestr(); } + virtual string dotColor() const override { return user() ? "green" : "black"; } virtual int type() const { return m_type; } }; @@ -120,7 +120,7 @@ protected: public: LatchDetectGraph() { clear(); } - ~LatchDetectGraph() { clear(); } + virtual ~LatchDetectGraph() override { clear(); } // ACCESSORS LatchDetectGraphVertex* currentp() { return m_curVertexp; } void currentp(LatchDetectGraphVertex* vertex) { m_curVertexp = vertex; } @@ -142,17 +142,17 @@ public: // Add a new control path and connect it to its parent LatchDetectGraphVertex* addPathVertex(LatchDetectGraphVertex* parent, const string& name, bool branch = false) { - m_curVertexp = new LatchDetectGraphVertex(this, name, + m_curVertexp = new LatchDetectGraphVertex{this, name, branch ? LatchDetectGraphVertex::VT_BRANCH - : LatchDetectGraphVertex::VT_BLOCK); - new V3GraphEdge(this, parent, m_curVertexp, 1); + : LatchDetectGraphVertex::VT_BLOCK}; + new V3GraphEdge{this, parent, m_curVertexp, 1}; return m_curVertexp; } // Add a new output variable vertex and store a pointer to it in the user1 field of the // variables AstNode LatchDetectGraphVertex* addOutputVertex(AstVarRef* nodep) { LatchDetectGraphVertex* outVertexp - = new LatchDetectGraphVertex(this, nodep->name(), LatchDetectGraphVertex::VT_OUTPUT); + = new LatchDetectGraphVertex{this, nodep->name(), LatchDetectGraphVertex::VT_OUTPUT}; nodep->varp()->user1p(outVertexp); m_outputs.push_back(nodep); return outVertexp; @@ -437,7 +437,7 @@ private: virtual void visit(AstInitial* nodep) override { // Relink to IACTIVE, unless already under it UINFO(4, " INITIAL " << nodep << endl); - ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_INITIAL); + ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL}; AstActive* wantactivep = m_namer.getIActive(nodep->fileline()); nodep->unlinkFrBack(); wantactivep->addStmtsp(nodep); @@ -470,7 +470,7 @@ private: VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); return; } - ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_INITIAL); + ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL}; if (!m_scopeFinalp) { m_scopeFinalp = new AstCFunc( nodep->fileline(), "_final_" + m_namer.scopep()->nameDotless(), m_namer.scopep()); @@ -540,14 +540,14 @@ private: // Warn and/or convert any delayed assignments if (combo && !sequent) { - ActiveLatchCheckVisitor latchvisitor(nodep, kwd); + ActiveLatchCheckVisitor latchvisitor{nodep, kwd}; if (kwd == VAlwaysKwd::ALWAYS_LATCH) { - ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_LATCH); + ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_LATCH}; } else { - ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_COMBO); + ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_COMBO}; } } else if (!combo && sequent) { - ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_SEQ); + ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_SEQ}; } } virtual void visit(AstAlways* nodep) override { @@ -620,6 +620,6 @@ public: void V3Active::activeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { ActiveVisitor visitor(nodep); } // Destruct before checking + { ActiveVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("active", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3ActiveTop.cpp b/src/V3ActiveTop.cpp index 0745c8ed7..6706dc357 100644 --- a/src/V3ActiveTop.cpp +++ b/src/V3ActiveTop.cpp @@ -136,6 +136,6 @@ public: void V3ActiveTop::activeTopAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { ActiveTopVisitor visitor(nodep); } // Destruct before checking + { ActiveTopVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("activetop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 22ae310ba..40aa060da 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -474,6 +474,6 @@ public: void V3Assert::assertAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { AssertVisitor visitor(nodep); } // Destruct before checking + { AssertVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("assert", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index 9049d6146..12bd7fddc 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -205,6 +205,6 @@ public: void V3AssertPre::assertPreAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { AssertPreVisitor visitor(nodep); } // Destruct before checking + { AssertPreVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("assertpre", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index e4d36cb60..356c8b201 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -74,9 +74,10 @@ AstNode::AstNode(AstType t, FileLine* fl) m_clonep = nullptr; m_cloneCnt = 0; // Attributes - m_didWidth = false; - m_doingWidth = false; - m_protect = true; + m_flags.didWidth = false; + m_flags.doingWidth = false; + m_flags.protect = true; + m_flags.unused = 0; // Initializing this avoids a read-modify-write on construction m_user1u = VNUser(0); m_user1Cnt = 0; m_user2u = VNUser(0); @@ -1123,7 +1124,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo if (doDump) { { // Write log & close UINFO(2, "Dumping " << filename << endl); - const std::unique_ptr logsp(V3File::new_ofstream(filename, append)); + const std::unique_ptr logsp{V3File::new_ofstream(filename, append)}; if (logsp->fail()) v3fatal("Can't write " << filename); *logsp << "Verilator Tree Dump (format 0x3900) from to \n"; @@ -1263,12 +1264,15 @@ AstNodeDType* AstNode::findBitRangeDType(const VNumRange& range, int widthMin, AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) { return v3Global.rootp()->typeTablep()->findInsertSameDType(nodep); } -AstNodeDType* AstNode::findVoidDType() const { - return v3Global.rootp()->typeTablep()->findVoidDType(fileline()); +AstNodeDType* AstNode::findEmptyQueueDType() const { + return v3Global.rootp()->typeTablep()->findEmptyQueueDType(fileline()); } AstNodeDType* AstNode::findQueueIndexDType() const { return v3Global.rootp()->typeTablep()->findQueueIndexDType(fileline()); } +AstNodeDType* AstNode::findVoidDType() const { + return v3Global.rootp()->typeTablep()->findVoidDType(fileline()); +} //###################################################################### // AstNVisitor diff --git a/src/V3Ast.h b/src/V3Ast.h index afa26f75d..3351a1b54 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -24,6 +24,7 @@ #include "V3FileLine.h" #include "V3Number.h" #include "V3Global.h" +#include "V3Broken.h" #include #include @@ -50,7 +51,7 @@ using MTaskIdSet = std::set; // Set of mtaskIds for Var sorting // For broken() function, return error string if have a match #define BROKEN_RTN(test) \ do { \ - if (VL_UNCOVERABLE(test)) return #test; \ + if (VL_UNCOVERABLE(test)) return "'" #test "' @ " __FILE__ ":" VL_STRINGIFY(__LINE__); \ } while (false) // For broken() function, return error string if a base of this class has a match #define BROKEN_BASE_RTN(test) \ @@ -238,39 +239,6 @@ inline bool operator==(AstPragmaType::en lhs, const AstPragmaType& rhs) { return //###################################################################### -class AstCFuncType final { -public: - enum en : uint8_t { - FT_NORMAL, - TRACE_REGISTER, - TRACE_INIT, - TRACE_INIT_SUB, - TRACE_FULL, - TRACE_FULL_SUB, - TRACE_CHANGE, - TRACE_CHANGE_SUB, - TRACE_CLEANUP - }; - enum en m_e; - inline AstCFuncType() - : m_e{FT_NORMAL} {} - // cppcheck-suppress noExplicitConstructor - inline AstCFuncType(en _e) - : m_e{_e} {} - explicit inline AstCFuncType(int _e) - : m_e(static_cast(_e)) {} // Need () or GCC 4.8 false warning - operator en() const { return m_e; } - // METHODS - bool isTrace() const { return m_e != FT_NORMAL; } -}; -inline bool operator==(const AstCFuncType& lhs, const AstCFuncType& rhs) { - return lhs.m_e == rhs.m_e; -} -inline bool operator==(const AstCFuncType& lhs, AstCFuncType::en rhs) { return lhs.m_e == rhs; } -inline bool operator==(AstCFuncType::en lhs, const AstCFuncType& rhs) { return lhs == rhs.m_e; } - -//###################################################################### - class VEdgeType final { public: // REMEMBER to edit the strings below too @@ -561,6 +529,25 @@ public: bool isEventValue() const { return m_e == EVENTVALUE; } bool isString() const { return m_e == STRING; } bool isMTaskState() const { return m_e == MTASKSTATE; } + // Does this represent a C++ LiteralType? (can be constexpr) + bool isLiteralType() const { + switch (m_e) { + case BIT: + case BYTE: + case CHANDLE: + case INT: + case INTEGER: + case LOGIC: + case LONGINT: + case DOUBLE: + case SHORTINT: + case SCOPEPTR: + case CHARPTR: + case UINT32: + case UINT64: return true; + default: return false; + } + } }; inline bool operator==(const AstBasicDTypeKwd& lhs, const AstBasicDTypeKwd& rhs) { return lhs.m_e == rhs.m_e; @@ -1367,8 +1354,32 @@ class AstNode VL_NOT_FINAL { const AstType m_type; // Node sub-type identifier // ^ ASTNODE_PREFETCH depends on above ordering of members - // padding - 2 extra bytes here after m_type - int m_cloneCnt; // Mark of when userp was set + // AstType is 2 bytes, so we can stick another 6 bytes after it to utilize what would + // otherwise be padding (on a 64-bit system). We stick the attribute flags, broken state, + // and the clone count here. + + struct { + bool didWidth : 1; // Did V3Width computation + bool doingWidth : 1; // Inside V3Width + bool protect : 1; // Protect name if protection is on + // Space for more flags here (there must be 8 bits in total) + uint8_t unused : 5; + } m_flags; // Attribute flags + + // State variable used by V3Broken for consistency checking. The top bit of this is byte is a + // flag, representing V3Broken is currently proceeding under this node. The bottom 7 bits are + // a generation number. This is hot when --debug-checks so we access as a whole to avoid bit + // field masking resulting in unnecessary read-modify-write ops. + uint8_t m_brokenState = 0; + + int m_cloneCnt; // Sequence number for when last clone was made + +#if defined(__x86_64__) && defined(__gnu_linux__) + // Only assert this on known platforms, as it only affects performance, not correctness + static_assert(sizeof(m_type) + sizeof(m_flags) + sizeof(m_brokenState) + sizeof(m_cloneCnt) + <= sizeof(void*), + "packing requires padding"); +#endif AstNodeDType* m_dtypep; // Data type of output or assignment (etc) AstNode* m_headtailp; // When at begin/end of list, the opposite end of the list @@ -1381,12 +1392,6 @@ class AstNode VL_NOT_FINAL { AstNode* m_clonep; // Pointer to clone of/ source of this module (for *LAST* cloneTree() ONLY) static int s_cloneCntGbl; // Count of which userp is set - // Attributes - bool m_didWidth : 1; // Did V3Width computation - bool m_doingWidth : 1; // Inside V3Width - bool m_protect : 1; // Protect name if protection is on - // // Space for more bools here - // This member ordering both allows 64 bit alignment and puts associated data together VNUser m_user1u; // Contains any information the user iteration routine wants uint32_t m_user1Cnt; // Mark of when userp was set @@ -1489,9 +1494,14 @@ public: AstNode* firstAbovep() const { // Returns nullptr when second or later in list return ((backp() && backp()->nextp() != this) ? backp() : nullptr); } - bool brokeExists() const; - bool brokeExistsAbove() const; - bool brokeExistsBelow() const; + uint8_t brokenState() const { return m_brokenState; } + void brokenState(uint8_t value) { m_brokenState = value; } + + // Used by AstNode::broken() + bool brokeExists() const { return V3Broken::isLinkable(this); } + bool brokeExistsAbove() const { return brokeExists() && (m_brokenState >> 7); } + bool brokeExistsBelow() const { return brokeExists() && !(m_brokenState >> 7); } + // Note: brokeExistsBelow is not quite precise, as it is true for sibling nodes as well // CONSTRUCTORS virtual ~AstNode() = default; @@ -1500,21 +1510,20 @@ public: static void operator delete(void* obj, size_t size); #endif - // CONSTANT ACCESSORS - static int instrCountBranch() { return 4; } ///< Instruction cycles to branch - static int instrCountDiv() { return 10; } ///< Instruction cycles to divide - static int instrCountDpi() { return 1000; } ///< Instruction cycles to call user function - static int instrCountLd() { return 2; } ///< Instruction cycles to load memory - static int instrCountMul() { return 3; } ///< Instruction cycles to multiply integers - static int instrCountPli() { return 20; } ///< Instruction cycles to call pli routines - static int instrCountDouble() { return 8; } ///< Instruction cycles to convert or do floats - static int instrCountDoubleDiv() { return 40; } ///< Instruction cycles to divide floats - static int instrCountDoubleTrig() { return 200; } ///< Instruction cycles to do trigonomics - static int instrCountString() { return 100; } ///< Instruction cycles to do string ops - /// Instruction cycles to call subroutine - static int instrCountCall() { return instrCountBranch() + 10; } - /// Instruction cycles to determine simulation time - static int instrCountTime() { return instrCountCall() + 5; } + // CONSTANTS + // The following are relative dynamic costs (~ execution cycle count) of various operations. + // They are used by V3InstCount to estimate the relative execution time of code fragments. + static constexpr int INSTR_COUNT_BRANCH = 4; // Branch + static constexpr int INSTR_COUNT_CALL = INSTR_COUNT_BRANCH + 10; // Subroutine call + static constexpr int INSTR_COUNT_LD = 2; // Load memory + static constexpr int INSTR_COUNT_INT_MUL = 3; // Integer multiply + static constexpr int INSTR_COUNT_INT_DIV = 10; // Integer divide + static constexpr int INSTR_COUNT_DBL = 8; // Convert or do float ops + static constexpr int INSTR_COUNT_DBL_DIV = 40; // Double divide + static constexpr int INSTR_COUNT_DBL_TRIG = 200; // Double trigonometric ops + static constexpr int INSTR_COUNT_STR = 100; // String ops + static constexpr int INSTR_COUNT_TIME = INSTR_COUNT_CALL + 5; // Determine simulation time + static constexpr int INSTR_COUNT_PLI = 20; // PLI routines // ACCESSORS virtual string name() const { return ""; } @@ -1545,17 +1554,17 @@ public: void fileline(FileLine* fl) { m_fileline = fl; } bool width1() const; int widthInstrs() const; - void didWidth(bool flag) { m_didWidth = flag; } - bool didWidth() const { return m_didWidth; } + void didWidth(bool flag) { m_flags.didWidth = flag; } + bool didWidth() const { return m_flags.didWidth; } bool didWidthAndSet() { if (didWidth()) return true; didWidth(true); return false; } - bool doingWidth() const { return m_doingWidth; } - void doingWidth(bool flag) { m_doingWidth = flag; } - bool protect() const { return m_protect; } - void protect(bool flag) { m_protect = flag; } + bool doingWidth() const { return m_flags.doingWidth; } + void doingWidth(bool flag) { m_flags.doingWidth = flag; } + bool protect() const { return m_flags.protect; } + void protect(bool flag) { m_flags.protect = flag; } // TODO stomp these width functions out, and call via dtypep() instead int width() const; @@ -1689,6 +1698,7 @@ public: void dtypeSetSigned32() { dtypep(findSigned32DType()); } void dtypeSetUInt32() { dtypep(findUInt32DType()); } // Twostate void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate + void dtypeSetEmptyQueue() { dtypep(findEmptyQueueDType()); } void dtypeSetVoid() { dtypep(findVoidDType()); } // Data type locators @@ -1699,6 +1709,7 @@ public: AstNodeDType* findUInt32DType() { return findBasicDType(AstBasicDTypeKwd::UINT32); } AstNodeDType* findUInt64DType() { return findBasicDType(AstBasicDTypeKwd::UINT64); } AstNodeDType* findCHandleDType() { return findBasicDType(AstBasicDTypeKwd::CHANDLE); } + AstNodeDType* findEmptyQueueDType() const; AstNodeDType* findVoidDType() const; AstNodeDType* findQueueIndexDType() const; AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const; @@ -1707,7 +1718,7 @@ public: VSigning numeric) const; AstNodeDType* findBitRangeDType(const VNumRange& range, int widthMin, VSigning numeric) const; AstNodeDType* findBasicDType(AstBasicDTypeKwd kwd) const; - AstBasicDType* findInsertSameDType(AstBasicDType* nodep); + static AstBasicDType* findInsertSameDType(AstBasicDType* nodep); // METHODS - dump and error void v3errorEnd(std::ostringstream& str) const; @@ -1782,8 +1793,6 @@ public: virtual bool isGateOptimizable() const { return true; } // GateDedupable is a slightly larger superset of GateOptimzable (eg, AstNodeIf) virtual bool isGateDedupable() const { return isGateOptimizable(); } - // Needs verilated_heavy.h (uses std::string or some others) - virtual bool isHeavy() const { return false; } // Else creates output or exits, etc, not unconsumed virtual bool isOutputter() const { return false; } // Else a AstTime etc which output can't be predicted from input @@ -2086,7 +2095,7 @@ public: virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } virtual bool sizeMattersThs() const override { return false; } - virtual int instrCount() const override { return instrCountBranch(); } + virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } virtual AstNode* cloneType(AstNode* condp, AstNode* expr1p, AstNode* expr2p) = 0; }; @@ -2221,7 +2230,7 @@ public: AstNode* incsp() const { return op3p(); } // op3 = increment statements AstNode* bodysp() const { return op4p(); } // op4 = body of loop virtual bool isGateOptimizable() const override { return false; } - virtual int instrCount() const override { return instrCountBranch(); } + virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } virtual bool same(const AstNode* samep) const override { return true; } }; @@ -2248,7 +2257,7 @@ public: void addElsesp(AstNode* newp) { addOp3p(newp); } virtual bool isGateOptimizable() const override { return false; } virtual bool isGateDedupable() const override { return true; } - virtual int instrCount() const override { return instrCountBranch(); } + virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } virtual bool same(const AstNode* samep) const override { return true; } void branchPred(VBranchPred flag) { m_branchPred = flag; } VBranchPred branchPred() const { return m_branchPred; } @@ -2266,7 +2275,7 @@ protected: public: ASTNODE_BASE_FUNCS(NodeCase) - virtual int instrCount() const override { return instrCountBranch(); } + virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } AstNode* exprp() const { return op1p(); } // op1 = case condition AstCaseItem* itemsp() const { return VN_CAST(op2p(), CaseItem); @@ -2445,6 +2454,7 @@ public: return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I"); } string cType(const string& name, bool forFunc, bool isRef) const; + bool isLiteralType() const; // Does this represent a C++ LiteralType? (can be constexpr) private: class CTypeRecursed; @@ -2621,7 +2631,6 @@ class AstNodeCCall VL_NOT_FINAL : public AstNodeStmt { // A call of a C++ function, perhaps a AstCFunc or perhaps globally named // Functions are not statements, while tasks are. AstNodeStmt needs isStatement() to deal. AstCFunc* m_funcp; - string m_selfPointer; // Output code object pointer (e.g.: 'this') string m_argTypes; protected: @@ -2636,7 +2645,7 @@ public: virtual void dump(std::ostream& str = std::cout) const override; virtual void cloneRelink() override; virtual const char* broken() const override; - virtual int instrCount() const override { return instrCountCall(); } + virtual int instrCount() const override { return INSTR_COUNT_CALL; } virtual bool same(const AstNode* samep) const override { const AstNodeCCall* asamep = static_cast(samep); return (funcp() == asamep->funcp() && argTypes() == asamep->argTypes()); @@ -2647,9 +2656,6 @@ public: virtual bool isPure() const override; virtual bool isOutputter() const override { return !isPure(); } AstCFunc* funcp() const { return m_funcp; } - string selfPointer() const { return m_selfPointer; } - void selfPointer(const string& value) { m_selfPointer = value; } - string selfPointerProtect(bool useSelfForThis) const; void argTypes(const string& str) { m_argTypes = str; } string argTypes() const { return m_argTypes; } // op1p reserved for AstCMethodCall @@ -2678,6 +2684,7 @@ private: bool m_isHideProtected : 1; // Verilog protected bool m_pure : 1; // DPI import pure (vs. virtual pure) bool m_pureVirtual : 1; // Pure virtual + bool m_underGenerate : 1; // Under generate (for warning) bool m_virtual : 1; // Virtual method in class VLifetime m_lifetime; // Lifetime protected: @@ -2700,6 +2707,7 @@ protected: , m_isHideProtected{false} , m_pure{false} , m_pureVirtual{false} + , m_underGenerate{false} , m_virtual{false} { addNOp3p(stmtsp); cname(name); // Might be overridden by dpi import/export @@ -2766,6 +2774,8 @@ public: bool pure() const { return m_pure; } void pureVirtual(bool flag) { m_pureVirtual = flag; } bool pureVirtual() const { return m_pureVirtual; } + void underGenerate(bool flag) { m_underGenerate = flag; } + bool underGenerate() const { return m_underGenerate; } void isVirtual(bool flag) { m_virtual = flag; } bool isVirtual() const { return m_virtual; } void lifetime(const VLifetime& flag) { m_lifetime = flag; } @@ -2845,7 +2855,6 @@ private: bool m_recursive : 1; // Recursive module bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr int m_level = 0; // 1=top module, 2=cell off top module, ... - int m_varNum = 0; // Incrementing variable number int m_typeNum = 0; // Incrementing implicit type number VLifetime m_lifetime; // Lifetime VTimescale m_timeunit; // Global time unit @@ -2886,7 +2895,6 @@ public: void level(int level) { m_level = level; } int level() const { return m_level; } bool isTop() const { return level() == 1; } - int varNumGetInc() { return ++m_varNum; } int typeNumGetInc() { return ++m_typeNum; } void modPublic(bool flag) { m_modPublic = flag; } bool modPublic() const { return m_modPublic; } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index d878aa615..ea3ecdf8e 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -121,7 +121,8 @@ const char* AstNodeCCall::broken() const { return nullptr; } bool AstNodeCCall::isPure() const { return funcp()->pure(); } -string AstNodeCCall::selfPointerProtect(bool useSelfForThis) const { + +string AstCCall::selfPointerProtect(bool useSelfForThis) const { const string& sp = useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer(); return VIdProtect::protectWordsIf(sp, protect()); @@ -267,7 +268,7 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) { char* endp; int v = strtol(literal.c_str(), &endp, 0); if ((v != 0) && (endp[0] == 0)) { // C literal - return new AstConst(fl, AstConst::WidthedValue(), 32, v); + return new AstConst(fl, AstConst::Signed32(), v); } else { // Try a Verilog literal (fatals if not) return new AstConst(fl, AstConst::StringToParse(), literal.c_str()); } @@ -775,6 +776,20 @@ int AstNodeDType::widthPow2() const { return 1; } +bool AstNodeDType::isLiteralType() const { + if (auto* const dtypep = VN_CAST_CONST(skipRefp(), BasicDType)) { + return dtypep->keyword().isLiteralType(); + } else if (auto* const dtypep = VN_CAST_CONST(skipRefp(), UnpackArrayDType)) { + return dtypep->basicp()->isLiteralType(); + } else if (auto* const dtypep = VN_CAST_CONST(skipRefp(), StructDType)) { + // Currently all structs are packed, later this can be expanded to + // 'forall members _.isLiteralType()' + return dtypep->packed(); + } else { + return false; + } +} + /// What is the base variable (or const) this dereferences? AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) { // Else AstArraySel etc; search for the base @@ -917,9 +932,18 @@ void AstTypeTable::repairCache() { } } +AstEmptyQueueDType* AstTypeTable::findEmptyQueueDType(FileLine* fl) { + if (VL_UNLIKELY(!m_emptyQueuep)) { + AstEmptyQueueDType* newp = new AstEmptyQueueDType{fl}; + addTypesp(newp); + m_emptyQueuep = newp; + } + return m_emptyQueuep; +} + AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) { if (VL_UNLIKELY(!m_voidp)) { - AstVoidDType* newp = new AstVoidDType(fl); + AstVoidDType* newp = new AstVoidDType{fl}; addTypesp(newp); m_voidp = newp; } @@ -1050,8 +1074,8 @@ AstVarScope* AstConstPool::findTable(AstInitArray* initp) { UASSERT_OBJ(VN_IS(valuep, Const), valuep, "Const pool table entry must be Const"); } // Try to find an existing table with the same content - const uint32_t hash = V3Hasher::uncachedHash(initp).value(); - const auto& er = m_tables.equal_range(hash); + const V3Hash hash = V3Hasher::uncachedHash(initp); + const auto& er = m_tables.equal_range(hash.value()); for (auto it = er.first; it != er.second; ++it) { AstVarScope* const varScopep = it->second; const AstInitArray* const init2p = VN_CAST(varScopep->varp()->valuep(), InitArray); @@ -1061,11 +1085,11 @@ AstVarScope* AstConstPool::findTable(AstInitArray* initp) { } // No such table yet, create it. string name = "TABLE_"; - name += cvtToHex(hash); + name += hash.toString(); name += "_"; name += cvtToStr(std::distance(er.first, er.second)); AstVarScope* const varScopep = createNewEntry(name, initp); - m_tables.emplace(hash, varScopep); + m_tables.emplace(hash.value(), varScopep); return varScopep; } @@ -1079,8 +1103,8 @@ static bool sameInit(const AstConst* ap, const AstConst* bp) { AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) { // Try to find an existing constant with the same value - const uint32_t hash = initp->num().toHash().value(); - const auto& er = m_consts.equal_range(hash); + const V3Hash hash = initp->num().toHash(); + const auto& er = m_consts.equal_range(hash.value()); for (auto it = er.first; it != er.second; ++it) { AstVarScope* const varScopep = it->second; const AstConst* const init2p = VN_CAST(varScopep->varp()->valuep(), Const); @@ -1091,11 +1115,11 @@ AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) { } // No such constant yet, create it. string name = "CONST_"; - name += cvtToHex(hash); + name += hash.toString(); name += "_"; name += cvtToStr(std::distance(er.first, er.second)); AstVarScope* const varScopep = createNewEntry(name, initp); - m_consts.emplace(hash, varScopep); + m_consts.emplace(hash.value(), varScopep); return varScopep; } @@ -1627,6 +1651,10 @@ void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str << "[]"; } +void AstEmptyQueueDType::dumpSmall(std::ostream& str) const { + this->AstNodeDType::dumpSmall(str); + str << "emptyq"; +} void AstVoidDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str << "void"; @@ -1827,6 +1855,7 @@ void AstCFunc::dump(std::ostream& str) const { if (dpiExportImpl()) str << " [DPIEI]"; if (dpiImportPrototype()) str << " [DPIIP]"; if (dpiImportWrapper()) str << " [DPIIW]"; + if (dpiContext()) str << " [DPICTX]"; if (isConstructor()) str << " [CTOR]"; if (isDestructor()) str << " [DTOR]"; if (isVirtual()) str << " [VIRT]"; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 731bcc1dc..da6da2a01 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -183,6 +183,17 @@ public: static AstConst* parseParamLiteral(FileLine* fl, const string& literal); }; +class AstEmptyQueue final : public AstNodeMath { +public: + AstEmptyQueue(FileLine* fl) + : ASTGEN_SUPER_EmptyQueue(fl) {} + ASTNODE_NODE_FUNCS(EmptyQueue) + virtual string emitC() override { V3ERROR_NA_RETURN(""); } + virtual string emitVerilog() override { return "{}"; } + virtual bool same(const AstNode* samep) const override { return true; } + virtual bool cleanOut() const override { return true; } +}; + class AstRange final : public AstNodeRange { // Range specification, for use under variables and cells public: @@ -307,7 +318,6 @@ public: : ASTGEN_SUPER_Class(fl, name) {} ASTNODE_NODE_FUNCS(Class) virtual string verilogKwd() const override { return "class"; } - virtual bool isHeavy() const override { return true; } virtual bool maybePointedTo() const override { return true; } virtual void dump(std::ostream& str) const override; virtual const char* broken() const override { @@ -556,7 +566,6 @@ public: virtual void dumpSmall(std::ostream& str) const override; virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); } virtual AstNodeDType* getChild2DTypep() const override { return keyChildDTypep(); } - virtual bool isHeavy() const override { return true; } // op1 = Range of variable AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } @@ -653,7 +662,6 @@ public: } virtual string prettyDTypeName() const override; virtual void dumpSmall(std::ostream& str) const override; - virtual bool isHeavy() const override { return true; } virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); } // op1 = Range of variable AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } @@ -881,7 +889,6 @@ public: BROKEN_RTN(dtypep() != this); return nullptr; } - virtual bool isHeavy() const override { return keyword() == AstBasicDTypeKwd::STRING; } AstRange* rangep() const { return VN_CAST(op1p(), Range); } // op1 = Range of variable void rangep(AstRange* nodep) { setNOp1p(nodep); } void setSignedState(const VSigning& signst) { @@ -1141,7 +1148,6 @@ public: } virtual void dumpSmall(std::ostream& str) const override; virtual string prettyDTypeName() const override; - virtual bool isHeavy() const override { return true; } virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); } // op1 = Range of variable AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } @@ -1359,6 +1365,33 @@ public: } }; +class AstEmptyQueueDType final : public AstNodeDType { + // For EmptyQueue +public: + explicit AstEmptyQueueDType(FileLine* fl) + : ASTGEN_SUPER_EmptyQueueDType(fl) { + dtypep(this); + } + ASTNODE_NODE_FUNCS(EmptyQueueDType) + virtual void dumpSmall(std::ostream& str) const override; + virtual bool hasDType() const override { return true; } + virtual bool maybePointedTo() const override { return true; } + virtual AstNodeDType* subDTypep() const override { return nullptr; } + virtual AstNodeDType* virtRefDTypep() const override { return nullptr; } + virtual void virtRefDTypep(AstNodeDType* nodep) override {} + virtual bool similarDType(AstNodeDType* samep) const override { return this == samep; } + virtual AstBasicDType* basicp() const override { return nullptr; } + // cppcheck-suppress csyleCast + virtual AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; } + // cppcheck-suppress csyleCast + virtual AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } + // cppcheck-suppress csyleCast + virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } + virtual int widthAlignBytes() const override { return 1; } + virtual int widthTotalBytes() const override { return 1; } + virtual bool isCompound() const override { return false; } +}; + class AstVoidDType final : public AstNodeDType { // For e.g. a function returning void public: @@ -2405,7 +2438,7 @@ public: } } virtual int instrCount() const override { - return widthInstrs() * (access().isReadOrRW() ? instrCountLd() : 1); + return widthInstrs() * (access().isReadOrRW() ? INSTR_COUNT_LD : 1); } virtual string emitVerilog() override { V3ERROR_NA_RETURN(""); } virtual string emitC() override { V3ERROR_NA_RETURN(""); } @@ -3484,6 +3517,17 @@ public: virtual bool brokeLhsMustBeLvalue() const override { return true; } }; +class AstDpiExportUpdated final : public AstNodeStmt { + // Denotes that the referenced variable may have been updated via a DPI Export +public: + AstDpiExportUpdated(FileLine* fl, AstVarScope* varScopep) + : ASTGEN_SUPER_DpiExportUpdated(fl) { + addOp1p(new AstVarRef{fl, varScopep, VAccess::WRITE}); + } + ASTNODE_NODE_FUNCS(DpiExportUpdated) + AstVarScope* varScopep() const { return VN_CAST(op1p(), VarRef)->varScopep(); } +}; + class AstExprStmt final : public AstNodeMath { // Perform a statement, often assignment inside an expression/math node, // the parent gets passed the 'resultp()'. @@ -3589,7 +3633,7 @@ public: if (m_dataDeclp && m_dataDeclp->clonep()) m_dataDeclp = m_dataDeclp->clonep(); } virtual void dump(std::ostream& str) const override; - virtual int instrCount() const override { return 1 + 2 * instrCountLd(); } + virtual int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; } virtual bool maybePointedTo() const override { return true; } void binNum(int flag) { m_binNum = flag; } int binNum() const { return m_binNum; } @@ -3632,7 +3676,7 @@ public: if (m_declp->clonep()) m_declp = m_declp->clonep(); } virtual void dump(std::ostream& str) const override; - virtual int instrCount() const override { return 1 + 2 * instrCountLd(); } + virtual int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; } virtual bool same(const AstNode* samep) const override { return declp() == static_cast(samep)->declp(); } @@ -3655,7 +3699,7 @@ public: setOp3p(changep); } ASTNODE_NODE_FUNCS(CoverToggle) - virtual int instrCount() const override { return 3 + instrCountBranch() + instrCountLd(); } + virtual int instrCount() const override { return 3 + INSTR_COUNT_BRANCH + INSTR_COUNT_LD; } virtual bool same(const AstNode* samep) const override { return true; } virtual bool isGateOptimizable() const override { return false; } virtual bool isPredictOptimizable() const override { return true; } @@ -3748,7 +3792,7 @@ public: addNOp2p(bodysp); } ASTNODE_NODE_FUNCS(CaseItem) - virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); } + virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; } AstNode* condsp() const { return op1p(); } // op1 = list of possible matching expressions AstNode* bodysp() const { return op2p(); } // op2 = what to do void condsp(AstNode* nodep) { setOp1p(nodep); } @@ -3792,7 +3836,7 @@ public: } ASTNODE_NODE_FUNCS(SFormatF) virtual string name() const override { return m_text; } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } virtual bool hasDType() const override { return true; } virtual bool same(const AstNode* samep) const override { return text() == static_cast(samep)->text(); @@ -3857,7 +3901,7 @@ public: virtual bool same(const AstNode* samep) const override { return displayType() == static_cast(samep)->displayType(); } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } AstDisplayType displayType() const { return m_displayType; } void displayType(AstDisplayType type) { m_displayType = type; } // * = Add a newline for $display @@ -3882,7 +3926,6 @@ public: ASTNODE_NODE_FUNCS(DumpCtl) virtual string verilogKwd() const override { return ctlType().ascii(); } virtual bool isGateOptimizable() const override { return false; } - virtual bool isHeavy() const override { return true; } virtual bool isPredictOptimizable() const override { return false; } virtual bool isOutputter() const override { return true; } virtual bool cleanOut() const { return true; } @@ -3922,7 +3965,7 @@ public: virtual bool same(const AstNode* samep) const override { return displayType() == static_cast(samep)->displayType(); } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } AstDisplayType displayType() const { return m_displayType; } void displayType(AstDisplayType type) { m_displayType = type; } void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter @@ -3956,7 +3999,7 @@ public: virtual bool isPure() const override { return true; } virtual bool isOutputter() const override { return false; } virtual bool cleanOut() const { return false; } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } virtual bool same(const AstNode* samep) const override { return true; } void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter AstSFormatF* fmtp() const { return VN_CAST(op1p(), SFormatF); } @@ -4001,7 +4044,7 @@ public: } // Though deleted before opt virtual bool isPure() const override { return false; } // Though deleted before opt virtual bool isOutputter() const override { return true; } // Though deleted before opt - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output void exprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output }; @@ -4038,7 +4081,6 @@ public: ASTNODE_NODE_FUNCS(FOpen) virtual string verilogKwd() const override { return "$fopen"; } virtual bool isGateOptimizable() const override { return false; } - virtual bool isHeavy() const override { return true; } virtual bool isPredictOptimizable() const override { return false; } virtual bool isPure() const override { return false; } virtual bool isOutputter() const override { return true; } @@ -4060,7 +4102,6 @@ public: ASTNODE_NODE_FUNCS(FOpenMcd) virtual string verilogKwd() const override { return "$fopen"; } virtual bool isGateOptimizable() const override { return false; } - virtual bool isHeavy() const override { return true; } virtual bool isPredictOptimizable() const override { return false; } virtual bool isPure() const override { return false; } virtual bool isOutputter() const override { return true; } @@ -4284,7 +4325,6 @@ public: setNOp4p(msbp); } virtual bool isGateOptimizable() const override { return false; } - virtual bool isHeavy() const override { return true; } virtual bool isPredictOptimizable() const override { return false; } virtual bool isPure() const override { return false; } virtual bool isOutputter() const override { return true; } @@ -4335,7 +4375,7 @@ public: } // Though deleted before opt virtual bool isPure() const override { return false; } // Though deleted before opt virtual bool isOutputter() const override { return true; } // Though deleted before opt - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } virtual bool same(const AstNode* samep) const override { return m_off == static_cast(samep)->m_off; } @@ -4395,7 +4435,6 @@ public: virtual string emitVerilog() override { return "%f$value$plusargs(%l, %k%r)"; } virtual string emitC() override { V3ERROR_NA_RETURN(""); } virtual bool isGateOptimizable() const override { return false; } - virtual bool isHeavy() const override { return true; } virtual bool isPredictOptimizable() const override { return false; } virtual bool cleanOut() const override { return true; } virtual bool same(const AstNode* samep) const override { return true; } @@ -4448,7 +4487,7 @@ public: AstNode* arrayp() const { return op1p(); } // op1 = array and index vars AstNode* bodysp() const { return op4p(); } // op4 = body of loop virtual bool isGateOptimizable() const override { return false; } - virtual int instrCount() const override { return instrCountBranch(); } + virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } virtual bool same(const AstNode* samep) const override { return true; } }; @@ -4465,7 +4504,7 @@ public: virtual bool isGateOptimizable() const override { return false; } // Not relevant - converted to FOR - virtual int instrCount() const override { return instrCountBranch(); } + virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } virtual bool same(const AstNode* samep) const override { return true; } }; @@ -4498,7 +4537,7 @@ public: void addBodysp(AstNode* newp) { addOp3p(newp); } void addIncsp(AstNode* newp) { addOp4p(newp); } virtual bool isGateOptimizable() const override { return false; } - virtual int instrCount() const override { return instrCountBranch(); } + virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } virtual bool same(const AstNode* samep) const override { return true; } // Stop statement searchback here virtual void addBeforeStmt(AstNode* newp, AstNode* belowp) override; @@ -4679,7 +4718,7 @@ public: if (m_labelp->clonep()) m_labelp = m_labelp->clonep(); } virtual void dump(std::ostream& str) const override; - virtual int instrCount() const override { return instrCountBranch(); } + virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } virtual bool same(const AstNode* samep) const override { return labelp() == static_cast(samep)->labelp(); } @@ -4736,7 +4775,7 @@ public: bool isClockReq() const { return m_clockReq; } virtual bool isGateOptimizable() const override { return false; } virtual bool isPredictOptimizable() const override { return false; } - virtual int instrCount() const override { return widthInstrs(); } + virtual int instrCount() const override { return widthInstrs() * 2; } // xor, or/logor virtual bool same(const AstNode* samep) const override { return true; } }; @@ -5075,7 +5114,7 @@ public: virtual bool isPredictOptimizable() const override { return false; } virtual bool isPure() const override { return false; } virtual bool isOutputter() const override { return true; } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } void timeunit(const VTimescale& flag) { m_timeunit = flag; } VTimescale timeunit() const { return m_timeunit; } }; @@ -5174,7 +5213,7 @@ public: virtual bool isPredictOptimizable() const override { return false; } virtual bool isPure() const override { return false; } virtual bool isOutputter() const override { return true; } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } AstNode* unitsp() const { return op1p(); } AstNode* precisionp() const { return op2p(); } AstNode* suffixp() const { return op3p(); } @@ -5244,11 +5283,14 @@ class AstTraceInc final : public AstNodeStmt { private: AstTraceDecl* m_declp; // Pointer to declaration const bool m_full; // Is this a full vs incremental dump + const uint32_t m_baseCode; // Trace code base value in function containing this AstTraceInc + public: - AstTraceInc(FileLine* fl, AstTraceDecl* declp, bool full) + AstTraceInc(FileLine* fl, AstTraceDecl* declp, bool full, uint32_t baseCode = 0) : ASTGEN_SUPER_TraceInc(fl) , m_declp{declp} - , m_full{full} { + , m_full{full} + , m_baseCode{baseCode} { dtypeFrom(declp); addOp2p(declp->valuep()->cloneTree(true)); } @@ -5261,7 +5303,7 @@ public: if (m_declp->clonep()) m_declp = m_declp->clonep(); } virtual void dump(std::ostream& str) const override; - virtual int instrCount() const override { return 10 + 2 * instrCountLd(); } + virtual int instrCount() const override { return 10 + 2 * INSTR_COUNT_LD; } virtual bool hasDType() const override { return true; } virtual bool same(const AstNode* samep) const override { return declp() == static_cast(samep)->declp(); @@ -5276,6 +5318,7 @@ public: AstNode* valuep() const { return op2p(); } AstTraceDecl* declp() const { return m_declp; } bool full() const { return m_full; } + uint32_t baseCode() const { return m_baseCode; } }; class AstActive final : public AstNode { @@ -5438,7 +5481,7 @@ public: virtual bool cleanOut() const override { return true; } virtual bool isGateOptimizable() const override { return false; } virtual bool isPredictOptimizable() const override { return false; } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } virtual bool same(const AstNode* samep) const override { return true; } AstNode* seedp() const { return op1p(); } bool reset() const { return m_reset; } @@ -5468,7 +5511,7 @@ public: virtual bool sizeMattersRhs() const override { return false; } virtual bool isGateOptimizable() const override { return false; } virtual bool isPredictOptimizable() const override { return false; } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } }; class AstTime final : public AstNodeTermop { @@ -5485,7 +5528,7 @@ public: virtual bool cleanOut() const override { return true; } virtual bool isGateOptimizable() const override { return false; } virtual bool isPredictOptimizable() const override { return false; } - virtual int instrCount() const override { return instrCountTime(); } + virtual int instrCount() const override { return INSTR_COUNT_TIME; } virtual bool same(const AstNode* samep) const override { return true; } virtual void dump(std::ostream& str = std::cout) const override; void timeunit(const VTimescale& flag) { m_timeunit = flag; } @@ -5506,7 +5549,7 @@ public: virtual bool cleanOut() const override { return true; } virtual bool isGateOptimizable() const override { return false; } virtual bool isPredictOptimizable() const override { return false; } - virtual int instrCount() const override { return instrCountTime(); } + virtual int instrCount() const override { return INSTR_COUNT_TIME; } virtual bool same(const AstNode* samep) const override { return true; } virtual void dump(std::ostream& str = std::cout) const override; void timeunit(const VTimescale& flag) { m_timeunit = flag; } @@ -5531,7 +5574,7 @@ public: virtual bool isGateOptimizable() const override { return false; } virtual bool isSubstOptimizable() const override { return false; } virtual bool isPredictOptimizable() const override { return false; } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } virtual bool same(const AstNode* samep) const override { return true; } }; @@ -5567,7 +5610,7 @@ public: virtual bool cleanOut() const override { return true; } virtual bool cleanLhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstRedAnd final : public AstNodeUniop { @@ -5760,7 +5803,7 @@ public: virtual bool cleanOut() const override { return false; } virtual bool cleanLhs() const override { return false; } // Eliminated before matters virtual bool sizeMattersLhs() const override { return false; } // Eliminated before matters - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } }; class AstRToIRoundS final : public AstNodeUniop { // Convert real to integer, with arbitrary sized output (not just "integer" format) @@ -5778,7 +5821,7 @@ public: virtual bool cleanOut() const override { return false; } virtual bool cleanLhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } }; class AstIToRD final : public AstNodeUniop { // $itor where lhs is unsigned @@ -5794,7 +5837,7 @@ public: virtual bool cleanOut() const override { return false; } virtual bool cleanLhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } }; class AstISToRD final : public AstNodeUniop { // $itor where lhs is signed @@ -5811,7 +5854,7 @@ public: virtual bool cleanOut() const override { return false; } virtual bool cleanLhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } }; class AstRealToBits final : public AstNodeUniop { public: @@ -5828,7 +5871,7 @@ public: virtual bool cleanOut() const override { return false; } virtual bool cleanLhs() const override { return false; } // Eliminated before matters virtual bool sizeMattersLhs() const override { return false; } // Eliminated before matters - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } }; class AstBitsToRealD final : public AstNodeUniop { public: @@ -5845,7 +5888,7 @@ public: virtual bool cleanOut() const override { return false; } virtual bool cleanLhs() const override { return false; } // Eliminated before matters virtual bool sizeMattersLhs() const override { return false; } // Eliminated before matters - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } }; class AstCLog2 final : public AstNodeUniop { @@ -6219,7 +6262,7 @@ public: virtual bool cleanOut() const override { return true; } virtual bool cleanLhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } - virtual int instrCount() const override { return instrCountDoubleTrig(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL_TRIG; } virtual bool doubleFlavor() const override { return true; } }; @@ -6530,7 +6573,6 @@ public: virtual bool cleanOut() const override { return true; } virtual bool cleanLhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } - virtual bool isHeavy() const override { return true; } FmtType format() const { return m_fmt; } }; @@ -6558,7 +6600,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); } + virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; } }; class AstLogAnd final : public AstNodeBiop { public: @@ -6581,7 +6623,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); } + virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; } }; class AstLogEq final : public AstNodeBiCom { public: @@ -6604,7 +6646,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); } + virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; } }; class AstLogIf final : public AstNodeBiop { public: @@ -6627,7 +6669,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); } + virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; } }; class AstOr final : public AstNodeBiComAsv { public: @@ -6740,7 +6782,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstEqN final : public AstNodeBiCom { @@ -6764,7 +6806,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountString(); } + virtual int instrCount() const override { return INSTR_COUNT_STR; } virtual bool stringFlavor() const override { return true; } }; class AstNeq final : public AstNodeBiCom { @@ -6810,7 +6852,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstNeqN final : public AstNodeBiCom { @@ -6834,7 +6876,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountString(); } + virtual int instrCount() const override { return INSTR_COUNT_STR; } virtual bool stringFlavor() const override { return true; } }; class AstLt final : public AstNodeBiop { @@ -6880,7 +6922,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstLtS final : public AstNodeBiop { @@ -6927,7 +6969,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountString(); } + virtual int instrCount() const override { return INSTR_COUNT_STR; } virtual bool stringFlavor() const override { return true; } }; class AstGt final : public AstNodeBiop { @@ -6973,7 +7015,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstGtS final : public AstNodeBiop { @@ -7020,7 +7062,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountString(); } + virtual int instrCount() const override { return INSTR_COUNT_STR; } virtual bool stringFlavor() const override { return true; } }; class AstGte final : public AstNodeBiop { @@ -7068,7 +7110,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstGteS final : public AstNodeBiop { @@ -7115,7 +7157,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountString(); } + virtual int instrCount() const override { return INSTR_COUNT_STR; } virtual bool stringFlavor() const override { return true; } }; class AstLte final : public AstNodeBiop { @@ -7163,7 +7205,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstLteS final : public AstNodeBiop { @@ -7210,7 +7252,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountString(); } + virtual int instrCount() const override { return INSTR_COUNT_STR; } virtual bool stringFlavor() const override { return true; } }; class AstShiftL final : public AstNodeBiop { @@ -7331,7 +7373,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstSub final : public AstNodeBiop { @@ -7377,7 +7419,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstMul final : public AstNodeBiComAsv { @@ -7401,7 +7443,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return true; } - virtual int instrCount() const override { return widthInstrs() * instrCountMul(); } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL; } }; class AstMulD final : public AstNodeBiComAsv { public: @@ -7424,7 +7466,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return true; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstMulS final : public AstNodeBiComAsv { @@ -7449,7 +7491,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return true; } - virtual int instrCount() const override { return widthInstrs() * instrCountMul(); } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL; } virtual bool signedFlavor() const override { return true; } }; class AstDiv final : public AstNodeBiop { @@ -7472,7 +7514,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return true; } - virtual int instrCount() const override { return widthInstrs() * instrCountDiv(); } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; } }; class AstDivD final : public AstNodeBiop { public: @@ -7495,7 +7537,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDoubleDiv(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL_DIV; } virtual bool doubleFlavor() const override { return true; } }; class AstDivS final : public AstNodeBiop { @@ -7518,7 +7560,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return true; } - virtual int instrCount() const override { return widthInstrs() * instrCountDiv(); } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; } virtual bool signedFlavor() const override { return true; } }; class AstModDiv final : public AstNodeBiop { @@ -7541,7 +7583,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return true; } - virtual int instrCount() const override { return widthInstrs() * instrCountDiv(); } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; } }; class AstModDivS final : public AstNodeBiop { public: @@ -7563,7 +7605,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return true; } - virtual int instrCount() const override { return widthInstrs() * instrCountDiv(); } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; } virtual bool signedFlavor() const override { return true; } }; class AstPow final : public AstNodeBiop { @@ -7587,7 +7629,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return widthInstrs() * instrCountMul() * 10; } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; } }; class AstPowD final : public AstNodeBiop { public: @@ -7609,7 +7651,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDoubleDiv() * 5; } + virtual int instrCount() const override { return INSTR_COUNT_DBL_DIV * 5; } virtual bool doubleFlavor() const override { return true; } }; class AstPowSU final : public AstNodeBiop { @@ -7635,7 +7677,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return widthInstrs() * instrCountMul() * 10; } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; } virtual bool signedFlavor() const override { return true; } }; class AstPowSS final : public AstNodeBiop { @@ -7661,7 +7703,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return widthInstrs() * instrCountMul() * 10; } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; } virtual bool signedFlavor() const override { return true; } }; class AstPowUS final : public AstNodeBiop { @@ -7687,7 +7729,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return widthInstrs() * instrCountMul() * 10; } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; } virtual bool signedFlavor() const override { return true; } }; class AstPreAdd final : public AstNodeTriop { @@ -7928,7 +7970,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountString(); } + virtual int instrCount() const override { return INSTR_COUNT_STR; } virtual bool stringFlavor() const override { return true; } }; class AstReplicate final : public AstNodeBiop { @@ -8091,7 +8133,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDoubleTrig(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL_TRIG; } virtual bool doubleFlavor() const override { return true; } }; @@ -8148,7 +8190,6 @@ public: virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } virtual bool sizeMattersThs() const override { return false; } - virtual bool isHeavy() const override { return true; } }; class AstGetcN final : public AstNodeBiop { @@ -8174,7 +8215,6 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual bool isHeavy() const override { return true; } }; class AstGetcRefN final : public AstNodeBiop { @@ -8200,7 +8240,6 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual bool isHeavy() const override { return true; } }; class AstSubstrN final : public AstNodeTriop { @@ -8226,7 +8265,6 @@ public: virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } virtual bool sizeMattersThs() const override { return false; } - virtual bool isHeavy() const override { return true; } }; class AstCompareNN final : public AstNodeBiop { @@ -8259,7 +8297,6 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual bool isHeavy() const override { return true; } }; class AstFell final : public AstNodeMath { @@ -8493,9 +8530,9 @@ private: public: AstNodeCoverOrAssert(AstType t, FileLine* fl, AstNode* propp, AstNode* passsp, bool immediate, const string& name = "") - : AstNodeStmt(t, fl) - , m_immediate(immediate) - , m_name(name) { + : AstNodeStmt{t, fl} + , m_immediate{immediate} + , m_name{name} { addOp1p(propp); addNOp4p(passsp); } @@ -8728,7 +8765,6 @@ class AstCFunc final : public AstNode { // Parents: MODULE/SCOPE // Children: VAR/statements private: - AstCFuncType m_funcType; AstScope* m_scopep; string m_name; string m_cname; // C name, for dpiExports @@ -8738,6 +8774,7 @@ private: string m_ifdef; // #ifdef symbol around this function VBoolOrUnknown m_isConst; // Function is declared const (*this not changed) bool m_isStatic : 1; // Function is static (no need for a 'this' pointer) + bool m_isTrace : 1; // Function is related to tracing bool m_dontCombine : 1; // V3Combine shouldn't compare this func tree, it's special bool m_declPrivate : 1; // Declare it private bool m_formCallTree : 1; // Make a global function to call entire tree of functions @@ -8756,15 +8793,16 @@ private: bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup) bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user) bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code + bool m_dpiContext : 1; // Declared as 'context' DPI import/export function public: AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "") : ASTGEN_SUPER_CFunc(fl) { - m_funcType = AstCFuncType::FT_NORMAL; m_isConst = VBoolOrUnknown::BU_UNKNOWN; // Unknown until analyzed m_scopep = scopep; m_name = name; m_rtnType = rtnType; m_isStatic = false; + m_isTrace = false; m_dontCombine = false; m_declPrivate = false; m_formCallTree = false; @@ -8782,6 +8820,7 @@ public: m_dpiExportImpl = false; m_dpiImportPrototype = false; m_dpiImportWrapper = false; + m_dpiContext = false; } ASTNODE_NODE_FUNCS(CFunc) virtual string name() const override { return m_name; } @@ -8793,25 +8832,29 @@ public: virtual void dump(std::ostream& str = std::cout) const override; virtual bool same(const AstNode* samep) const override { const AstCFunc* asamep = static_cast(samep); - return ((funcType() == asamep->funcType()) && (rtnTypeVoid() == asamep->rtnTypeVoid()) + return ((isTrace() == asamep->isTrace()) && (rtnTypeVoid() == asamep->rtnTypeVoid()) && (argTypes() == asamep->argTypes()) && (ctorInits() == asamep->ctorInits()) && isLoose() == asamep->isLoose() && (!(dpiImportPrototype() || dpiExportImpl()) || name() == asamep->name())); } // virtual void name(const string& name) override { m_name = name; } - virtual int instrCount() const override { return dpiImportPrototype() ? instrCountDpi() : 0; } + virtual int instrCount() const override { + return dpiImportPrototype() ? v3Global.opt.instrCountDpi() : 0; + } VBoolOrUnknown isConst() const { return m_isConst; } void isConst(bool flag) { m_isConst.setTrueOrFalse(flag); } void isConst(VBoolOrUnknown flag) { m_isConst = flag; } bool isStatic() const { return m_isStatic; } void isStatic(bool flag) { m_isStatic = flag; } + bool isTrace() const { return m_isTrace; } + void isTrace(bool flag) { m_isTrace = flag; } void cname(const string& name) { m_cname = name; } string cname() const { return m_cname; } AstScope* scopep() const { return m_scopep; } void scopep(AstScope* nodep) { m_scopep = nodep; } string rtnTypeVoid() const { return ((m_rtnType == "") ? "void" : m_rtnType); } - bool dontCombine() const { return m_dontCombine || funcType() != AstCFuncType::FT_NORMAL; } + bool dontCombine() const { return m_dontCombine || isTrace(); } void dontCombine(bool flag) { m_dontCombine = flag; } bool dontInline() const { return dontCombine() || slow() || funcPublic(); } bool declPrivate() const { return m_declPrivate; } @@ -8828,8 +8871,6 @@ public: string ctorInits() const { return m_ctorInits; } void ifdef(const string& str) { m_ifdef = str; } string ifdef() const { return m_ifdef; } - void funcType(AstCFuncType flag) { m_funcType = flag; } - AstCFuncType funcType() const { return m_funcType; } bool isConstructor() const { return m_isConstructor; } void isConstructor(bool flag) { m_isConstructor = flag; } bool isDestructor() const { return m_isDestructor; } @@ -8855,6 +8896,8 @@ public: void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; } bool dpiImportWrapper() const { return m_dpiImportWrapper; } void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; } + bool dpiContext() const { return m_dpiContext; } + void dpiContext(bool flag) { m_dpiContext = flag; } // // If adding node accessors, see below emptyBody AstNode* argsp() const { return op1p(); } @@ -8876,10 +8919,17 @@ class AstCCall final : public AstNodeCCall { // C++ function call // Parents: Anything above a statement // Children: Args to the function + + string m_selfPointer; // Output code object pointer (e.g.: 'this') + public: AstCCall(FileLine* fl, AstCFunc* funcp, AstNode* argsp = nullptr) : ASTGEN_SUPER_CCall(fl, funcp, argsp) {} ASTNODE_NODE_FUNCS(CCall) + + string selfPointer() const { return m_selfPointer; } + void selfPointer(const string& value) { m_selfPointer = value; } + string selfPointerProtect(bool useSelfForThis) const; }; class AstCMethodCall final : public AstNodeCCall { @@ -9002,8 +9052,8 @@ class AstCUse final : public AstNode { // C++ use of a class or #include; indicates need of forward declaration // Parents: NODEMODULE private: - VUseType m_useType; // What sort of use this is - string m_name; + const VUseType m_useType; // What sort of use this is + const string m_name; public: AstCUse(FileLine* fl, VUseType useType, const string& name) @@ -9011,10 +9061,9 @@ public: , m_useType{useType} , m_name{name} {} ASTNODE_NODE_FUNCS(CUse) - virtual string name() const override { return m_name; } virtual void dump(std::ostream& str = std::cout) const override; + virtual string name() const override { return m_name; } VUseType useType() const { return m_useType; } - void useType(VUseType useType) { m_useType = useType; } }; class AstMTaskBody final : public AstNode { @@ -9077,8 +9126,9 @@ public: class AstTypeTable final : public AstNode { // Container for hash of standard data types // Children: NODEDTYPEs - AstVoidDType* m_voidp = nullptr; + AstEmptyQueueDType* m_emptyQueuep = nullptr; AstQueueDType* m_queueIndexp = nullptr; + AstVoidDType* m_voidp = nullptr; AstBasicDType* m_basicps[AstBasicDTypeKwd::_ENUM_MAX]; // using DetailedMap = std::map; @@ -9089,14 +9139,15 @@ public: ASTNODE_NODE_FUNCS(TypeTable) AstNodeDType* typesp() const { return VN_CAST(op1p(), NodeDType); } // op1 = List of dtypes void addTypesp(AstNodeDType* nodep) { addOp1p(nodep); } - AstVoidDType* findVoidDType(FileLine* fl); - AstQueueDType* findQueueIndexDType(FileLine* fl); AstBasicDType* findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd); AstBasicDType* findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, int width, int widthMin, VSigning numeric); AstBasicDType* findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, const VNumRange& range, int widthMin, VSigning numeric); AstBasicDType* findInsertSameDType(AstBasicDType* nodep); + AstEmptyQueueDType* findEmptyQueueDType(FileLine* fl); + AstQueueDType* findQueueIndexDType(FileLine* fl); + AstVoidDType* findVoidDType(FileLine* fl); void clearCache(); void repairCache(); virtual void dump(std::ostream& str = std::cout) const override; @@ -9143,8 +9194,10 @@ private: AstPackage* m_dollarUnitPkgp = nullptr; // $unit AstCFunc* m_evalp = nullptr; // The '_eval' function AstExecGraph* m_execGraphp = nullptr; // Execution MTask graph for threads>1 mode + AstVarScope* m_dpiExportTriggerp = nullptr; // The DPI export trigger variable VTimescale m_timeunit; // Global time unit VTimescale m_timeprecision; // Global time precision + bool m_changeRequest = false; // Have _change_request method bool m_timescaleSpecified = false; // Input HDL specified timescale public: AstNetlist(); @@ -9152,6 +9205,7 @@ public: virtual const char* broken() const override { BROKEN_RTN(m_dollarUnitPkgp && !m_dollarUnitPkgp->brokeExists()); BROKEN_RTN(m_evalp && !m_evalp->brokeExists()); + BROKEN_RTN(m_dpiExportTriggerp && !m_dpiExportTriggerp->brokeExists()); return nullptr; } virtual string name() const override { return "$root"; } @@ -9168,6 +9222,8 @@ public: AstNode* miscsp() const { return op3p(); } // op3 = List of dtypes etc void addMiscsp(AstNode* nodep) { addOp3p(nodep); } AstTypeTable* typeTablep() { return m_typeTablep; } + void changeRequest(bool specified) { m_changeRequest = specified; } + bool changeRequest() const { return m_changeRequest; } AstConstPool* constPoolp() { return m_constPoolp; } AstPackage* dollarUnitPkgp() const { return m_dollarUnitPkgp; } AstPackage* dollarUnitPkgAddp() { @@ -9185,6 +9241,8 @@ public: void evalp(AstCFunc* evalp) { m_evalp = evalp; } AstExecGraph* execGraphp() const { return m_execGraphp; } void execGraphp(AstExecGraph* graphp) { m_execGraphp = graphp; } + AstVarScope* dpiExportTriggerp() const { return m_dpiExportTriggerp; } + void dpiExportTriggerp(AstVarScope* varScopep) { m_dpiExportTriggerp = varScopep; } VTimescale timeunit() const { return m_timeunit; } void timeunit(const VTimescale& value) { m_timeunit = value; } VTimescale timeprecision() const { return m_timeprecision; } diff --git a/src/V3AstUserAllocator.h b/src/V3AstUserAllocator.h index f00e35550..637b6d2aa 100644 --- a/src/V3AstUserAllocator.h +++ b/src/V3AstUserAllocator.h @@ -33,7 +33,7 @@ template class AstUserAllocatorBase VL private: std::vector m_allocated; - inline T_Data* getUserp(T_Node* nodep) const { + inline T_Data* getUserp(const T_Node* nodep) const { // This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'. if (T_UserN == 1) { const VNUser user = nodep->user1u(); @@ -100,6 +100,13 @@ public: } return *userp; } + + // Get a reference to the user data + T_Data& operator()(const T_Node* nodep) { + T_Data* userp = getUserp(nodep); + UASSERT_OBJ(userp, nodep, "Missing User data on const AstNode"); + return *userp; + } }; // User pointer allocator classes. T_Node is the type of node the allocator should be applied to diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 9c1e3674a..404b96803 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -62,6 +62,7 @@ private: BeginState* m_statep; // Current global state AstNodeModule* m_modp = nullptr; // Current module AstNodeFTask* m_ftaskp = nullptr; // Current function/task + AstNode* m_liftedp = nullptr; // Local nodes we are lifting into m_ftaskp string m_namedScope; // Name of begin blocks above us string m_unnamedScope; // Name of begin blocks, including unnamed blocks int m_ifDepth = 0; // Current if depth @@ -74,6 +75,21 @@ private: return a + "__DOT__" + b; } + void liftNode(AstNode* nodep) { + nodep->unlinkFrBack(); + if (m_ftaskp) { + // AstBegin under ftask, just move into the ftask + if (!m_liftedp) { + m_liftedp = nodep; + } else { + m_liftedp->addNext(nodep); + } + } else { + // Move to module + m_modp->addStmtp(nodep); + } + } + // VISITORS virtual void visit(AstNodeModule* nodep) override { VL_RESTORER(m_modp); @@ -101,7 +117,17 @@ private: m_namedScope = ""; m_unnamedScope = ""; m_ftaskp = nodep; + m_liftedp = nullptr; iterateChildren(nodep); + if (m_liftedp) { + // Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced + if (AstNode* const stmtsp = nodep->stmtsp()) { + stmtsp->unlinkFrBackWithNext(); + m_liftedp->addNext(stmtsp); + } + nodep->addStmtsp(m_liftedp); + m_liftedp = nullptr; + } m_ftaskp = nullptr; } } @@ -157,13 +183,8 @@ private: // Rename it nodep->name(dot(m_unnamedScope, nodep->name())); m_statep->userMarkChanged(nodep); - // Move to module - nodep->unlinkFrBack(); - if (m_ftaskp) { - m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func - } else { - m_modp->addStmtp(nodep); - } + // Move it under enclosing tree + liftNode(nodep); } } virtual void visit(AstTypedef* nodep) override { @@ -171,14 +192,8 @@ private: // Rename it nodep->name(dot(m_unnamedScope, nodep->name())); m_statep->userMarkChanged(nodep); - // Move to module - nodep->unlinkFrBack(); - // Begins under funcs just move into the func - if (m_ftaskp) { - m_ftaskp->addStmtsp(nodep); - } else { - m_modp->addStmtp(nodep); - } + // Move it under enclosing tree + liftNode(nodep); } } virtual void visit(AstCell* nodep) override { @@ -293,7 +308,7 @@ void V3Begin::debeginAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { BeginState state; - { BeginVisitor bvisitor(nodep, &state); } + { BeginVisitor bvisitor{nodep, &state}; } if (state.anyFuncInBegin()) { BeginRelinkVisitor brvisitor(nodep, &state); } } // Destruct before checking V3Global::dumpCheckGlobalTree("begin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); diff --git a/src/V3Branch.cpp b/src/V3Branch.cpp index b1a5de551..ae8a6f1d8 100644 --- a/src/V3Branch.cpp +++ b/src/V3Branch.cpp @@ -43,8 +43,8 @@ private: AstUser1InUse m_inuser1; // STATE - int m_likely; // Excuses for branch likely taken - int m_unlikely; // Excuses for branch likely not taken + int m_likely = false; // Excuses for branch likely taken + int m_unlikely = false; // Excuses for branch likely not taken std::vector m_cfuncsp; // List of all tasks // METHODS @@ -123,5 +123,5 @@ public: void V3Branch::branchAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - BranchVisitor visitor(nodep); + BranchVisitor visitor{nodep}; } diff --git a/src/V3Broken.cpp b/src/V3Broken.cpp index 3205ce912..a1b081523 100644 --- a/src/V3Broken.cpp +++ b/src/V3Broken.cpp @@ -18,6 +18,7 @@ // Entire netlist // Mark all nodes // Check all links point to marked nodes +// Check local variables in CFuncs appear before they are referenced // //************************************************************************* @@ -31,201 +32,129 @@ // This visitor does not edit nodes, and is called at error-exit, so should use constant iterators #include "V3AstConstOnly.h" -#include #include +#include //###################################################################### +// Generation counter for AstNode::m_brokenState -class BrokenTable VL_NOT_FINAL : public AstNVisitor { - // Table of brokenExists node pointers +static class BrokenCntGlobal { + // This is a 7 bit generation counter, stored in the bottom 7 bits of AstNode::m_brokenState, + // used to mark a node as being present under the root AstNetlist in the current traversal. A + // value 0 is invalid, as the AstNode constructor uses that to initialize m_brokenState + static constexpr uint8_t MIN_VALUE = 1; + static constexpr uint8_t MAX_VALUE = 127; + + uint8_t m_count = MIN_VALUE; + +public: + uint8_t get() { + UASSERT(MIN_VALUE <= m_count && m_count <= MAX_VALUE, "Invalid generation number"); + return m_count; + } + + void inc() { + ++m_count; + if (m_count > MAX_VALUE) m_count = MIN_VALUE; + } +} s_brokenCntGlobal; + +//###################################################################### +// Table of allocated AstNode pointers + +static class AllocTable final { private: // MEMBERS - // For each node, we keep if it exists or not. - using NodeMap = std::unordered_map; // Performance matters (when --debug) - static NodeMap s_nodes; // Set of all nodes that exist - // BITMASK - enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed - enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree - enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to - enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked - enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node + std::unordered_set m_allocated; // Set of all nodes allocated but not freed public: // METHODS - static void deleted(const AstNode* nodep) { - // Called by operator delete on any node - only if VL_LEAK_CHECKS - if (debug() >= 9) cout << "-nodeDel: " << cvtToHex(nodep) << endl; - const auto iter = s_nodes.find(nodep); - UASSERT_OBJ(!(iter == s_nodes.end() || !(iter->second & FLAG_ALLOCATED)), - reinterpret_cast(nodep), - "Deleting AstNode object that was never tracked or already deleted"); - if (iter != s_nodes.end()) s_nodes.erase(iter); - } -#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4 - // GCC 4.4.* compiler warning bug, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39390 -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -#endif - static void addNewed(const AstNode* nodep) { + void addNewed(const AstNode* nodep) { // Called by operator new on any node - only if VL_LEAK_CHECKS - if (debug() >= 9) cout << "-nodeNew: " << cvtToHex(nodep) << endl; - const auto iter = s_nodes.find(nodep); - UASSERT_OBJ(!(iter != s_nodes.end() && (iter->second & FLAG_ALLOCATED)), nodep, - "Newing AstNode object that is already allocated"); - if (iter == s_nodes.end()) { - const int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2 - s_nodes.emplace(nodep, flags); + // LCOV_EXCL_START + if (VL_UNCOVERABLE(!m_allocated.emplace(nodep).second)) { + nodep->v3fatalSrc("Newing AstNode object that is already allocated"); } + // LCOV_EXCL_STOP } - static void setUnder(const AstNode* nodep, bool flag) { - // Called by BrokenCheckVisitor when each node entered/exited - if (!okIfLinkedTo(nodep)) return; - const auto iter = s_nodes.find(nodep); - if (iter != s_nodes.end()) { - iter->second &= ~FLAG_UNDER_NOW; - if (flag) iter->second |= FLAG_UNDER_NOW; + void deleted(const AstNode* nodep) { + // Called by operator delete on any node - only if VL_LEAK_CHECKS + // LCOV_EXCL_START + if (VL_UNCOVERABLE(m_allocated.erase(nodep) == 0)) { + nodep->v3fatalSrc("Deleting AstNode object that was not allocated or already freed"); } + // LCOV_EXCL_STOP } - static void addInTree(AstNode* nodep, bool linkable) { -#ifndef VL_LEAK_CHECKS - // cppcheck-suppress knownConditionTrueFalse - if (!linkable) return; // save some time, else the map will get huge! -#endif - const auto iter = s_nodes.find(nodep); - if (VL_UNCOVERABLE(iter == s_nodes.end())) { -#ifdef VL_LEAK_CHECKS - nodep->v3fatalSrc("AstNode is in tree, but not allocated"); -#endif - } else { -#ifdef VL_LEAK_CHECKS - UASSERT_OBJ(iter->second & FLAG_ALLOCATED, nodep, - "AstNode is in tree, but not allocated"); -#endif - UASSERT_OBJ(!(iter->second & FLAG_IN_TREE), nodep, - "AstNode is already in tree at another location"); - } - const int or_flags = FLAG_IN_TREE | (linkable ? FLAG_LINKABLE : 0); - if (iter == s_nodes.end()) { - s_nodes.emplace(nodep, or_flags); - } else { - iter->second |= or_flags; - } - } - static bool isAllocated(const AstNode* nodep) { - // Some generic node has a pointer to this node. Is it allocated? - // Use this when might not be in tree; otherwise use okIfLinkedTo(). -#ifdef VL_LEAK_CHECKS - const auto iter = s_nodes.find(nodep); - if (iter == s_nodes.end()) return false; - if (!(iter->second & FLAG_ALLOCATED)) return false; -#endif - return true; - } - static bool okIfLinkedTo(const AstNode* nodep) { - // Some node in tree has a pointer to this node. Is it kosher? - const auto iter = s_nodes.find(nodep); - if (iter == s_nodes.end()) return false; -#ifdef VL_LEAK_CHECKS - if (!(iter->second & FLAG_ALLOCATED)) return false; -#endif - if (!(iter->second & FLAG_IN_TREE)) return false; - if (!(iter->second & FLAG_LINKABLE)) return false; - return true; - } - static bool okIfAbove(const AstNode* nodep) { - // Must be linked to and below current node - if (!okIfLinkedTo(nodep)) return false; - const auto iter = s_nodes.find(nodep); - if (iter == s_nodes.end()) return false; - if ((iter->second & FLAG_UNDER_NOW)) return false; - return true; - } - static bool okIfBelow(const AstNode* nodep) { - // Must be linked to and below current node - if (!okIfLinkedTo(nodep)) return false; - const auto iter = s_nodes.find(nodep); - if (iter == s_nodes.end()) return false; - if (!(iter->second & FLAG_UNDER_NOW)) return false; - return true; - } - static void prepForTree() { -#ifndef VL_LEAK_CHECKS - s_nodes.clear(); -#else - for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) { - it->second &= ~FLAG_IN_TREE; - it->second &= ~FLAG_LINKABLE; - } -#endif - } - static void doneWithTree() { - for (int backs = 0; backs < 2; - backs++) { // Those with backp() are probably under one leaking without - for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) { - // LCOV_EXCL_START - if (VL_UNCOVERABLE((it->second & FLAG_ALLOCATED) && !(it->second & FLAG_IN_TREE) - && !(it->second & FLAG_LEAKED) - && (it->first->backp() ? backs == 1 : backs == 0))) { + bool isAllocated(const AstNode* nodep) const { return m_allocated.count(nodep) != 0; } + void checkForLeaks() { + if (!v3Global.opt.debugCheck()) return; + const uint8_t brokenCntCurrent = s_brokenCntGlobal.get(); + + // Those with backp() are probably under a parent that was leaked and has no backp() + for (const bool withBack : {false, true}) { + for (const AstNode* const nodep : m_allocated) { + // LCOV_EXCL_START + // Most likely not leaked, so check that first + if (VL_UNCOVERABLE(nodep->brokenState() != brokenCntCurrent)) { + const bool hasBack = nodep->backp() != nullptr; + if (hasBack != withBack) continue; // Use only AstNode::dump instead of the virtual one, as there // may be varp() and other cross links that are bad. - if (v3Global.opt.debugCheck()) { - // When get this message, find what forgot to delete the - // node by running GDB, where for node "" use: - // watch AstNode::s_editCntGbl==#### - // run - // bt - std::cerr << "%Error: LeakedNode" - << (it->first->backp() ? "Back: " : ": "); - AstNode* rawp - = const_cast(static_cast(it->first)); - rawp->AstNode::dump(std::cerr); - std::cerr << endl; - V3Error::incErrors(); - } - it->second |= FLAG_LEAKED; + // When get this message, find what forgot to delete the + // node by running GDB, where for node "" use: + // watch AstNode::s_editCntGbl==#### + // run + // bt + std::cerr << "%Error: LeakedNode" << (withBack ? "with back pointer: " : ": "); + nodep->AstNode::dump(std::cerr); + std::cerr << endl; + V3Error::incErrors(); } // LCOV_EXCL_STOP } } } +} s_allocTable; - // CONSTRUCTORS - BrokenTable() = default; - virtual ~BrokenTable() override = default; -}; - -BrokenTable::NodeMap BrokenTable::s_nodes; - -bool AstNode::brokeExists() const { - // Called by node->broken() routines to do table lookup - return BrokenTable::okIfLinkedTo(this); -} -bool AstNode::brokeExistsAbove() const { - // Called by node->broken() routines to do table lookup - return BrokenTable::okIfBelow(this); -} -bool AstNode::brokeExistsBelow() const { - // Called by node->broken() routines to do table lookup - return BrokenTable::okIfAbove(this); -} +void V3Broken::addNewed(const AstNode* nodep) { s_allocTable.addNewed(nodep); } +void V3Broken::deleted(const AstNode* nodep) { s_allocTable.deleted(nodep); } //###################################################################### +// Table of AstNode pointers that can be linked to via member pointers + +static class LinkableTable final { +private: + // MEMBERS + std::unordered_set m_linkable; // Set of all nodes allocated but not freed + +public: + // METHODS + void clear() { m_linkable.clear(); } + inline void addLinkable(const AstNode* nodep) { m_linkable.emplace(nodep); } + inline bool isLinkable(const AstNode* nodep) const { return m_linkable.count(nodep) != 0; } +} s_linkableTable; + +bool V3Broken::isLinkable(const AstNode* nodep) { return s_linkableTable.isLinkable(nodep); } + +//###################################################################### +// Mark every node in the tree class BrokenMarkVisitor final : public AstNVisitor { - // Mark every node in the tree private: - // NODE STATE - // Nothing! // This may be called deep inside other routines - // // so userp and friends may not be used - // METHODS - void processAndIterate(AstNode* nodep) { - BrokenTable::addInTree(nodep, nodep->maybePointedTo()); - iterateChildrenConst(nodep); - } + const uint8_t m_brokenCntCurrent = s_brokenCntGlobal.get(); + // VISITORS virtual void visit(AstNode* nodep) override { - // Process not just iterate - processAndIterate(nodep); +#ifdef VL_LEAK_CHECKS + UASSERT_OBJ(s_allocTable.isAllocated(nodep), nodep, + "AstNode is in tree, but not allocated"); +#endif + UASSERT_OBJ(nodep->brokenState() != m_brokenCntCurrent, nodep, + "AstNode is already in tree at another location"); + if (nodep->maybePointedTo()) s_linkableTable.addLinkable(nodep); + nodep->brokenState(m_brokenCntCurrent); + iterateChildrenConst(nodep); } public: @@ -235,19 +164,33 @@ public: }; //###################################################################### -// Broken state, as a visitor of each AstNode +// Check every node in tree class BrokenCheckVisitor final : public AstNVisitor { bool m_inScope = false; // Under AstScope + // Constants for marking we are under/not under a node + const uint8_t m_brokenCntCurrentNotUnder = s_brokenCntGlobal.get(); // Top bit is clear + const uint8_t m_brokenCntCurrentUnder = m_brokenCntCurrentNotUnder | 0x80; // Top bit is set + + // Current CFunc, if any + const AstCFunc* m_cfuncp = nullptr; + // All local variables declared in current function + std::unordered_set m_localVars; + // Variable references in current function that do not reference an in-scope local + std::unordered_map m_suspectRefs; + // Local variables declared in the scope of the current statement + std::vector> m_localsStack; + private: static void checkWidthMin(const AstNode* nodep) { UASSERT_OBJ(nodep->width() == nodep->widthMin() || v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH, nodep, "Width != WidthMin"); } - void processAndIterate(AstNode* nodep) { - BrokenTable::setUnder(nodep, true); + + void processEnter(AstNode* nodep) { + nodep->brokenState(m_brokenCntCurrentUnder); const char* whyp = nodep->broken(); UASSERT_OBJ(!whyp, nodep, "Broken link in node (or something without maybePointedTo): " << whyp); @@ -270,8 +213,30 @@ private: if (const AstNodeDType* dnodep = VN_CAST(nodep, NodeDType)) checkWidthMin(dnodep); } checkWidthMin(nodep); + } + void processExit(AstNode* nodep) { nodep->brokenState(m_brokenCntCurrentNotUnder); } + void processAndIterate(AstNode* nodep) { + processEnter(nodep); iterateChildrenConst(nodep); - BrokenTable::setUnder(nodep, false); + processExit(nodep); + } + void processAndIterateList(AstNode* nodep) { + while (nodep) { + processAndIterate(nodep); + nodep = nodep->nextp(); + }; + } + void pushLocalScope() { + if (m_cfuncp) m_localsStack.emplace_back(); + } + void popLocalScope() { + if (m_cfuncp) m_localsStack.pop_back(); + } + bool isInScopeLocal(const AstVar* varp) const { + for (const auto& set : m_localsStack) { + if (set.count(varp)) return true; + } + return false; } virtual void visit(AstNodeAssign* nodep) override { processAndIterate(nodep); @@ -295,6 +260,65 @@ private: UASSERT_OBJ( !(v3Global.assertScoped() && m_inScope && nodep->varp() && !nodep->varScopep()), nodep, "VarRef missing VarScope pointer"); + if (m_cfuncp) { + // Check if variable is an in-scope local, otherwise mark as suspect + if (const AstVar* const varp = nodep->varp()) { + if (!isInScopeLocal(varp)) { + // This only stores the first ref for each Var, which is what we want + m_suspectRefs.emplace(varp, nodep); + } + } + } + } + virtual void visit(AstCFunc* nodep) override { + UASSERT_OBJ(!m_cfuncp, nodep, "Nested AstCFunc"); + m_cfuncp = nodep; + m_localVars.clear(); + m_suspectRefs.clear(); + m_localsStack.clear(); + pushLocalScope(); + + processAndIterate(nodep); + + // Check suspect references are all to non-locals + for (const auto& pair : m_suspectRefs) { + UASSERT_OBJ(m_localVars.count(pair.first) == 0, pair.second, + "Local variable not in scope where referenced: " << pair.first); + } + + m_cfuncp = nullptr; + } + virtual void visit(AstNodeIf* nodep) override { + // Each branch is a separate local variable scope + pushLocalScope(); + processEnter(nodep); + processAndIterate(nodep->condp()); + if (AstNode* const ifsp = nodep->ifsp()) { + pushLocalScope(); + processAndIterateList(ifsp); + popLocalScope(); + } + if (AstNode* const elsesp = nodep->elsesp()) { + pushLocalScope(); + processAndIterateList(elsesp); + popLocalScope(); + } + processExit(nodep); + popLocalScope(); + } + virtual void visit(AstNodeStmt* nodep) override { + // For local variable checking act as if any statement introduces a new scope. + // This is aggressive but conservatively correct. + pushLocalScope(); + processAndIterate(nodep); + popLocalScope(); + } + virtual void visit(AstVar* nodep) override { + processAndIterate(nodep); + if (m_cfuncp) { + m_localVars.insert(nodep); + m_localsStack.back().insert(nodep); + } } virtual void visit(AstNode* nodep) override { // Process not just iterate @@ -308,7 +332,7 @@ public: }; //###################################################################### -// Broken class functions +// Broken check entry point void V3Broken::brokenAll(AstNetlist* nodep) { // UINFO(9, __FUNCTION__ << ": " << endl); @@ -318,24 +342,23 @@ void V3Broken::brokenAll(AstNetlist* nodep) { UINFO(1, "Broken called under broken, skipping recursion.\n"); // LCOV_EXCL_LINE } else { inBroken = true; - BrokenTable::prepForTree(); - BrokenMarkVisitor mvisitor(nodep); - BrokenCheckVisitor cvisitor(nodep); - BrokenTable::doneWithTree(); + BrokenMarkVisitor mvisitor{nodep}; + BrokenCheckVisitor cvisitor{nodep}; + s_allocTable.checkForLeaks(); + s_linkableTable.clear(); + s_brokenCntGlobal.inc(); inBroken = false; } } -void V3Broken::addNewed(AstNode* nodep) { BrokenTable::addNewed(nodep); } -void V3Broken::deleted(AstNode* nodep) { BrokenTable::deleted(nodep); } -bool V3Broken::isAllocated(AstNode* nodep) { return BrokenTable::isAllocated(nodep); } +//###################################################################### +// Self test + void V3Broken::selfTest() { - // Warmup addNewed and deleted for coverage, as otherwise only with VL_LEAK_CHECKS - FileLine* fl = new FileLine(FileLine::commandLineFilename()); - auto* newp = new AstBegin(fl, "[EditWrapper]", nullptr); -#ifndef VL_LEAK_CHECKS + // Exercise addNewed and deleted for coverage, as otherwise only used with VL_LEAK_CHECKS + FileLine* const fl = new FileLine(FileLine::commandLineFilename()); + const AstNode* const newp = new AstBegin(fl, "[EditWrapper]", nullptr); addNewed(newp); deleted(newp); -#endif VL_DO_DANGLING(delete newp, newp); } diff --git a/src/V3Broken.h b/src/V3Broken.h index 16103baa0..94387ef06 100644 --- a/src/V3Broken.h +++ b/src/V3Broken.h @@ -20,17 +20,18 @@ #include "config_build.h" #include "verilatedos.h" -#include "V3Error.h" -#include "V3Ast.h" - //============================================================================ +// Forward declare so we can include this in V3Ast.h +class AstNode; +class AstNetlist; + class V3Broken final { public: static void brokenAll(AstNetlist* nodep); - static void addNewed(AstNode* nodep); - static void deleted(AstNode* nodep); - static bool isAllocated(AstNode* nodep); + static bool isLinkable(const AstNode* nodep); + static void addNewed(const AstNode* nodep); + static void deleted(const AstNode* nodep); static void selfTest(); }; diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index 2becaa25d..40b328966 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -60,7 +60,7 @@ private: AstCFunc* makeNewFunc() { const int funcNum = m_newFunctions.size(); const string funcName = m_basename + "_" + cvtToStr(funcNum); - AstCFunc* const funcp = new AstCFunc(m_modp->fileline(), funcName, nullptr, "void"); + AstCFunc* const funcp = new AstCFunc{m_modp->fileline(), funcName, nullptr, "void"}; funcp->isStatic(false); funcp->isLoose(!m_type.isClass()); funcp->declPrivate(true); @@ -74,7 +74,7 @@ private: preventUnusedStmt = "if (false && first) {} // Prevent unused\n"; } if (!preventUnusedStmt.empty()) { - funcp->addStmtsp(new AstCStmt(m_modp->fileline(), preventUnusedStmt)); + funcp->addStmtsp(new AstCStmt{m_modp->fileline(), preventUnusedStmt}); } m_modp->addStmtp(funcp); m_numStmts = 0; @@ -91,9 +91,9 @@ public: } V3CCtorsBuilder(AstNodeModule* nodep, const string& basename, VCtorType type) - : m_modp(nodep) + : m_modp{nodep} , m_basename{basename} - , m_type(type) { + , m_type{type} { // Note: The constructor is always called, even if empty, so we must always create at least // one. m_newFunctions.push_back(makeNewFunc()); @@ -108,7 +108,7 @@ public: AstCFunc* const rootFuncp = makeNewFunc(); rootFuncp->name(m_basename); for (AstCFunc* const funcp : m_newFunctions) { - AstCCall* const callp = new AstCCall(m_modp->fileline(), funcp); + AstCCall* const callp = new AstCCall{m_modp->fileline(), funcp}; if (m_type.isClass()) { callp->argTypes("vlSymsp"); } else { @@ -127,8 +127,9 @@ private: //###################################################################### void V3CCtors::evalAsserts() { - AstNodeModule* modp = v3Global.rootp()->modulesp(); // Top module wrapper - AstCFunc* funcp = new AstCFunc(modp->fileline(), "_eval_debug_assertions", nullptr, "void"); + AstNodeModule* const modp = v3Global.rootp()->modulesp(); // Top module wrapper + AstCFunc* const funcp + = new AstCFunc{modp->fileline(), "_eval_debug_assertions", nullptr, "void"}; funcp->declPrivate(true); funcp->isStatic(false); funcp->isLoose(true); @@ -136,7 +137,7 @@ void V3CCtors::evalAsserts() { funcp->ifdef("VL_DEBUG"); modp->addStmtp(funcp); for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) { - if (AstVar* varp = VN_CAST(np, Var)) { + if (AstVar* const varp = VN_CAST(np, Var)) { if (varp->isPrimaryInish() && !varp->isSc()) { if (AstBasicDType* basicp = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) { const int storedWidth = basicp->widthAlignBytes() * 8; @@ -144,22 +145,22 @@ void V3CCtors::evalAsserts() { if (lastWordWidth != 0) { // if (signal & CONST(upper_non_clean_mask)) { fail; } AstVarRef* const vrefp - = new AstVarRef(varp->fileline(), varp, VAccess::READ); + = new AstVarRef{varp->fileline(), varp, VAccess::READ}; vrefp->selfPointer("this"); AstNode* newp = vrefp; if (varp->isWide()) { - newp = new AstWordSel( + newp = new AstWordSel{ varp->fileline(), newp, - new AstConst(varp->fileline(), varp->widthWords() - 1)); + new AstConst(varp->fileline(), varp->widthWords() - 1)}; } - uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth); - newp = new AstAnd(varp->fileline(), newp, + const uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth); + newp = new AstAnd{varp->fileline(), newp, new AstConst(varp->fileline(), AstConst::WidthedValue(), - storedWidth, value)); - AstNodeIf* ifp = new AstIf( + storedWidth, value)}; + AstNodeIf* const ifp = new AstIf{ varp->fileline(), newp, - new AstCStmt(varp->fileline(), "Verilated::overWidthError(\"" - + varp->prettyName() + "\");")); + new AstCStmt{varp->fileline(), "Verilated::overWidthError(\"" + + varp->prettyName() + "\");"}}; ifp->branchPred(VBranchPred::BP_UNLIKELY); newp = ifp; funcp->addStmtsp(newp); @@ -177,20 +178,21 @@ void V3CCtors::cctorsAll() { modp = VN_CAST(modp->nextp(), NodeModule)) { // Process each module in turn { - V3CCtorsBuilder var_reset(modp, "_ctor_var_reset", - VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE); + V3CCtorsBuilder var_reset{modp, "_ctor_var_reset", + VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE}; for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) { if (AstVar* const varp = VN_CAST(np, Var)) { - if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()) { - const auto vrefp = new AstVarRef(varp->fileline(), varp, VAccess::WRITE); - var_reset.add(new AstCReset(varp->fileline(), vrefp)); + if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset() + && !varp->isParam()) { + const auto vrefp = new AstVarRef{varp->fileline(), varp, VAccess::WRITE}; + var_reset.add(new AstCReset{varp->fileline(), vrefp}); } } } } if (v3Global.opt.coverage()) { - V3CCtorsBuilder configure_coverage(modp, "_configure_coverage", VCtorType::COVERAGE); + V3CCtorsBuilder configure_coverage{modp, "_configure_coverage", VCtorType::COVERAGE}; for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) { if (AstCoverDecl* const coverp = VN_CAST(np, CoverDecl)) { np = coverp->backp(); @@ -198,8 +200,8 @@ void V3CCtors::cctorsAll() { } } } - if (AstClass* classp = VN_CAST(modp, Class)) { - AstCFunc* funcp = new AstCFunc(modp->fileline(), "~", nullptr, ""); + if (AstClass* const classp = VN_CAST(modp, Class)) { + AstCFunc* const funcp = new AstCFunc{modp->fileline(), "~", nullptr, ""}; funcp->isDestructor(true); funcp->isStatic(false); // If can be referred to by base pointer, need virtual delete diff --git a/src/V3CUse.cpp b/src/V3CUse.cpp index 3eb65f13e..54c2a0566 100644 --- a/src/V3CUse.cpp +++ b/src/V3CUse.cpp @@ -13,14 +13,12 @@ // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // //************************************************************************* -// V3Class's Transformations: +// V3CUse's Transformations: // // Each module: // Each cell: // Create CUse for cell forward declaration -// Each class: -// Create string access functions -// Search for dtypes referencing class, and create CUse for forward declaraion +// Search for dtypes referencing class, and create CUse for forward declaration // //************************************************************************* @@ -30,61 +28,37 @@ #include "V3Global.h" #include "V3CUse.h" #include "V3Ast.h" -#include "V3EmitCBase.h" -#include +#include //###################################################################### -class CUseState final { -private: - // MEMBERS - AstNodeModule* m_modInsertp; // Current module to insert AstCUse under - using UseString = std::pair; - std::map m_didUse; // What we already used - - // NODE STATE - // Entire netlist: - // AstClass::user1() -> bool. True if class needs to_string dumper - AstUser1InUse m_inuser1; - // AstClass::user2() -> bool. True if iterated - AstUser2InUse m_inuser2; - - // METHODS - VL_DEBUG_FUNC; // Declare debug() - -public: - AstCUse* newUse(AstNode* nodep, VUseType useType, const string& name) { - UseString key(useType, name); - if (m_didUse.find(key) == m_didUse.end()) { - AstCUse* newp = new AstCUse(nodep->fileline(), useType, name); - m_modInsertp->addStmtp(newp); - UINFO(8, "Insert " << newp << endl); - m_didUse[key] = newp; - } - return m_didUse[key]; - } - - // CONSTRUCTORS - explicit CUseState(AstNodeModule* nodep) - : m_modInsertp{nodep} {} - virtual ~CUseState() = default; - VL_UNCOPYABLE(CUseState); -}; - // Visit within a module all nodes and data types they reference, finding // any classes so we can make sure they are defined when Verilated code // compiles -class CUseDTypeVisitor final : public AstNVisitor { +class CUseVisitor final : public AstNVisitor { + // NODE STATE + // AstNode::user1() -> bool. True if already visited + AstUser1InUse m_inuser1; + // MEMBERS - CUseState& m_stater; // State for inserter bool m_impOnly = false; // In details needed only for implementation + AstNodeModule* const m_modp; // Current module + std::set> m_didUse; // What we already used + // METHODS + void addNewUse(AstNode* nodep, VUseType useType, const string& name) { + if (m_didUse.emplace(useType, name).second) { + AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name}; + m_modp->addStmtp(newp); + UINFO(8, "Insert " << newp << endl); + } + } + + // VISITORS virtual void visit(AstClassRefDType* nodep) override { - if (nodep->user2SetOnce()) return; // Process once - if (!m_impOnly) m_stater.newUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name()); - // No class.h, it's inside the class package's h file - m_stater.newUse(nodep, VUseType::IMP_INCLUDE, nodep->classp()->classOrPackagep()->name()); + if (nodep->user1SetOnce()) return; // Process once + if (!m_impOnly) addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name()); // Need to include extends() when we implement, but no need for pointers to know VL_RESTORER(m_impOnly); { @@ -93,131 +67,27 @@ class CUseDTypeVisitor final : public AstNVisitor { } } virtual void visit(AstNodeDType* nodep) override { - if (nodep->user2SetOnce()) return; // Process once + if (nodep->user1SetOnce()) return; // Process once if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep()); if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p()); } virtual void visit(AstNode* nodep) override { - if (nodep->user2SetOnce()) return; // Process once - if (nodep->dtypep() && !nodep->dtypep()->user2()) iterate(nodep->dtypep()); + if (nodep->user1SetOnce()) return; // Process once + if (nodep->dtypep() && !nodep->dtypep()->user1()) iterate(nodep->dtypep()); + iterateChildren(nodep); + } + virtual void visit(AstCell* nodep) override { + if (nodep->user1SetOnce()) return; // Process once + // Currently no IMP_INCLUDE because we include __Syms which has them all + addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->modp()->name()); iterateChildren(nodep); } public: // CONSTRUCTORS - explicit CUseDTypeVisitor(AstNodeModule* nodep, CUseState& stater) - : m_stater(stater) { // Need () or GCC 4.8 false warning - iterate(nodep); - } - virtual ~CUseDTypeVisitor() override = default; - VL_UNCOPYABLE(CUseDTypeVisitor); -}; - -class CUseVisitor final : public AstNVisitor { - // MEMBERS - CUseState m_state; // Inserter state - - // METHODS - VL_DEBUG_FUNC; // Declare debug() - - // Module use builders - void makeUseCells(AstNodeModule* nodep) { - for (AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) { - if (AstCell* cellp = VN_CAST(itemp, Cell)) { - // Currently no include because we include __Syms which has them all - m_state.newUse(nodep, VUseType::INT_FWD_CLASS, cellp->modp()->name()); - } - } - } - void makeVlToString(AstClass* nodep) { - AstCFunc* funcp = new AstCFunc(nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"); - funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep) - + ">& obj"); - funcp->isMethod(false); - funcp->isConst(false); - funcp->isStatic(false); - funcp->protect(false); - AstNode* exprp = new AstCMath(nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0); - exprp->dtypeSetString(); - funcp->addStmtsp(new AstCReturn(nodep->fileline(), exprp)); - nodep->addStmtp(funcp); - } - void makeToString(AstClass* nodep) { - AstCFunc* funcp = new AstCFunc(nodep->fileline(), "to_string", nullptr, "std::string"); - funcp->isConst(true); - funcp->isStatic(false); - funcp->protect(false); - AstNode* exprp = new AstCMath(nodep->fileline(), - R"(std::string("'{") + to_string_middle() + "}")", 0); - exprp->dtypeSetString(); - funcp->addStmtsp(new AstCReturn(nodep->fileline(), exprp)); - nodep->addStmtp(funcp); - } - void makeToStringMiddle(AstClass* nodep) { - AstCFunc* funcp - = new AstCFunc(nodep->fileline(), "to_string_middle", nullptr, "std::string"); - funcp->isConst(true); - funcp->isStatic(false); - funcp->protect(false); - funcp->addStmtsp(new AstCStmt(nodep->fileline(), "std::string out;\n")); - std::string comma; - for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) { - if (auto* varp = VN_CAST(itemp, Var)) { - if (!varp->isParam()) { - string stmt = "out += \""; - stmt += comma; - comma = ", "; - stmt += itemp->origNameProtect(); - stmt += ":\" + "; - if (itemp->isWide()) { - stmt += "VL_TO_STRING_W("; - stmt += cvtToStr(itemp->widthWords()); - stmt += ", "; - } else { - stmt += "VL_TO_STRING("; - } - stmt += itemp->nameProtect(); - stmt += ");\n"; - nodep->user1(true); // So what we extend dumps this - funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); - } - } - } - if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) { - string stmt = "out += \""; - if (!comma.empty()) stmt += "\", \"+ "; - // comma = ", "; // Nothing further so not needed - stmt += nodep->extendsp()->dtypep()->nameProtect(); - stmt += "::to_string_middle();\n"; - nodep->user1(true); // So what we extend dumps this - funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); - } - funcp->addStmtsp(new AstCStmt(nodep->fileline(), "return out;\n")); - nodep->addStmtp(funcp); - } - - // VISITORS - virtual void visit(AstNodeModule* nodep) override { - if (v3Global.opt.trace()) { - AstCUse* usep - = m_state.newUse(nodep, VUseType::INT_FWD_CLASS, v3Global.opt.traceClassBase()); - usep->protect(false); - } - makeUseCells(nodep); - { CUseDTypeVisitor dtypeVisitor(nodep, m_state); } - if (AstClass* classp = VN_CAST(nodep, Class)) { - makeVlToString(classp); - makeToString(classp); - makeToStringMiddle(classp); - } - } - virtual void visit(AstNode*) override {} // All in AstNodeModule - -public: - // CONSTRUCTORS - explicit CUseVisitor(AstNodeModule* nodep) - : m_state{nodep} { - iterate(nodep); + explicit CUseVisitor(AstNodeModule* modp) + : m_modp(modp) { + iterate(modp); } virtual ~CUseVisitor() override = default; VL_UNCOPYABLE(CUseVisitor); @@ -233,7 +103,7 @@ void V3CUse::cUseAll() { modp = VN_CAST(modp->nextp(), NodeModule)) { // Insert under this module; someday we should e.g. make Ast // for each output file and put under that - CUseVisitor visitor(modp); + CUseVisitor{modp}; } V3Global::dumpCheckGlobalTree("cuse", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Case.cpp b/src/V3Case.cpp index 23653a592..843f804d3 100644 --- a/src/V3Case.cpp +++ b/src/V3Case.cpp @@ -530,10 +530,10 @@ public: void V3Case::caseAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { CaseVisitor visitor(nodep); } // Destruct before checking + { CaseVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("case", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } void V3Case::caseLint(AstNodeCase* nodep) { UINFO(4, __FUNCTION__ << ": " << endl); - CaseLintVisitor visitor(nodep); + CaseLintVisitor visitor{nodep}; } diff --git a/src/V3Cast.cpp b/src/V3Cast.cpp index c8e5536cf..de912c72e 100644 --- a/src/V3Cast.cpp +++ b/src/V3Cast.cpp @@ -66,7 +66,8 @@ private: AstNRelinker relinkHandle; nodep->unlinkFrBack(&relinkHandle); // - AstCCast* castp = new AstCCast(nodep->fileline(), nodep, needsize, nodep->widthMin()); + AstCCast* const castp + = new AstCCast{nodep->fileline(), nodep, needsize, nodep->widthMin()}; relinkHandle.relink(castp); // if (debug() > 8) castp->dumpTree(cout, "-castins: "); // @@ -103,7 +104,7 @@ private: if (!VN_IS(nodep->backp(), NullCheck)) { AstNRelinker relinkHandle; nodep->unlinkFrBack(&relinkHandle); - AstNode* newp = new AstNullCheck(nodep->fileline(), nodep); + AstNode* const newp = new AstNullCheck{nodep->fileline(), nodep}; relinkHandle.relink(newp); } } @@ -154,9 +155,10 @@ private: } } virtual void visit(AstVarRef* nodep) override { - if (nodep->access().isReadOnly() && !VN_IS(nodep->backp(), CCast) - && VN_IS(nodep->backp(), NodeMath) && !VN_IS(nodep->backp(), ArraySel) - && nodep->backp()->width() && castSize(nodep) != castSize(nodep->varp())) { + AstNode* const backp = nodep->backp(); + if (nodep->access().isReadOnly() && !VN_IS(backp, CCast) && VN_IS(backp, NodeMath) + && !VN_IS(backp, ArraySel) && !VN_IS(backp, RedXor) && backp->width() + && castSize(nodep) != castSize(nodep->varp())) { // Cast vars to IData first, else below has upper bits wrongly set // CData x=3; out = (QData)(x<<30); insertCast(nodep, castSize(nodep)); @@ -201,6 +203,6 @@ public: void V3Cast::castAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { CastVisitor visitor(nodep); } // Destruct before checking + { CastVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("cast", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Cdc.cpp b/src/V3Cdc.cpp index 22d2d0333..98b9168c3 100644 --- a/src/V3Cdc.cpp +++ b/src/V3Cdc.cpp @@ -337,7 +337,7 @@ private: int filelineWidth() { if (!m_filelineWidth) { - CdcWidthVisitor visitor(v3Global.rootp()); + CdcWidthVisitor visitor{v3Global.rootp()}; m_filelineWidth = visitor.maxWidth(); } return m_filelineWidth; @@ -495,7 +495,7 @@ private: UINFO(3, __FUNCTION__ << ": " << endl); // Trace all sources and sinks - for (int traceDests = 0; traceDests < 2; ++traceDests) { + for (const bool& traceDests : {false, true}) { UINFO(9, " Trace Direction " << (traceDests ? "dst" : "src") << endl); m_graph.userClearVertices(); // user1: bool - was analyzed for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { @@ -508,7 +508,7 @@ private: const string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc_edges.txt"; - const std::unique_ptr ofp(V3File::new_ofstream(filename)); + const std::unique_ptr ofp{V3File::new_ofstream(filename)}; if (ofp->fail()) v3fatal("Can't write " << filename); *ofp << "Edge Report for " << v3Global.opt.prefix() << '\n'; @@ -758,5 +758,5 @@ public: void V3Cdc::cdcAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - CdcVisitor visitor(nodep); + CdcVisitor visitor{nodep}; } diff --git a/src/V3Changed.cpp b/src/V3Changed.cpp index 79e2ad14d..aac14f59a 100644 --- a/src/V3Changed.cpp +++ b/src/V3Changed.cpp @@ -47,11 +47,33 @@ public: AstCFunc* m_tlChgFuncp = nullptr; // Top level change function we're building int m_numStmts = 0; // Number of statements added to m_chgFuncp int m_funcNum = 0; // Number of change functions emitted + bool m_madeTopChg = false; ChangedState() = default; ~ChangedState() = default; void maybeCreateChgFuncp() { + maybeCreateTopChg(); + maybeCreateMidChg(); + } + void maybeCreateTopChg() { + if (m_madeTopChg) return; + m_madeTopChg = true; + v3Global.rootp()->changeRequest(true); + + // Create a wrapper change detection function that calls each change detection function + m_tlChgFuncp + = new AstCFunc{m_scopetopp->fileline(), "_change_request", m_scopetopp, "QData"}; + m_tlChgFuncp->isStatic(false); + m_tlChgFuncp->isLoose(true); + m_tlChgFuncp->declPrivate(true); + m_scopetopp->addActivep(m_tlChgFuncp); + // Each change detection function needs at least one AstChangeDet + // to ensure that V3EmitC outputs the necessary code. + maybeCreateMidChg(); + m_chgFuncp->addStmtsp(new AstChangeDet{m_scopetopp->fileline(), nullptr, nullptr, false}); + } + void maybeCreateMidChg() { // Don't create an extra function call if splitting is disabled if (!v3Global.opt.outputSplitCFuncs()) { m_chgFuncp = m_tlChgFuncp; @@ -59,29 +81,29 @@ public: } if (!m_chgFuncp || v3Global.opt.outputSplitCFuncs() < m_numStmts) { m_chgFuncp - = new AstCFunc(m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum), - m_scopetopp, "QData"); + = new AstCFunc{m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum), + m_scopetopp, "QData"}; m_chgFuncp->isStatic(false); m_chgFuncp->isLoose(true); m_chgFuncp->declPrivate(true); m_scopetopp->addActivep(m_chgFuncp); // Add a top call to it - AstCCall* callp = new AstCCall(m_scopetopp->fileline(), m_chgFuncp); + AstCCall* const callp = new AstCCall{m_scopetopp->fileline(), m_chgFuncp}; if (!m_tlChgFuncp->stmtsp()) { - m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp)); + m_tlChgFuncp->addStmtsp(new AstCReturn{m_scopetopp->fileline(), callp}); } else { - AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn); + AstCReturn* const returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn); UASSERT_OBJ(returnp, m_scopetopp, "Lost CReturn in top change function"); // This is currently using AstLogOr which will shortcut the // evaluation if any function returns true. This is likely what // we want and is similar to the logic already in use inside // V3EmitC, however, it also means that verbose logging may // miss to print change detect variables. - AstNode* newp = new AstCReturn( + AstNode* const newp = new AstCReturn{ m_scopetopp->fileline(), - new AstLogOr(m_scopetopp->fileline(), callp, returnp->lhsp()->unlinkFrBack())); + new AstLogOr{m_scopetopp->fileline(), callp, returnp->lhsp()->unlinkFrBack()}}; returnp->replaceWith(newp); VL_DO_DANGLING(returnp->deleteTree(), returnp); } @@ -123,14 +145,17 @@ private: } m_statep->maybeCreateChgFuncp(); - AstChangeDet* changep = new AstChangeDet(m_vscp->fileline(), m_varEqnp->cloneTree(true), - m_newRvEqnp->cloneTree(true), false); + AstChangeDet* const changep = new AstChangeDet{ + m_vscp->fileline(), m_varEqnp->cloneTree(true), m_newRvEqnp->cloneTree(true), false}; m_statep->m_chgFuncp->addStmtsp(changep); - AstAssign* initp = new AstAssign(m_vscp->fileline(), m_newLvEqnp->cloneTree(true), - m_varEqnp->cloneTree(true)); + AstAssign* const initp = new AstAssign{m_vscp->fileline(), m_newLvEqnp->cloneTree(true), + m_varEqnp->cloneTree(true)}; m_statep->m_chgFuncp->addFinalsp(initp); - EmitCBaseCounterVisitor visitor(initp); - m_statep->m_numStmts += visitor.count(); + + // Later code will expand words which adds to GCC compile time, + // so add penalty based on word width also + EmitCBaseCounterVisitor visitor{initp}; + m_statep->m_numStmts += visitor.count() + m_varEqnp->widthWords(); } virtual void visit(AstBasicDType*) override { // @@ -145,11 +170,11 @@ private: VL_RESTORER(m_newLvEqnp); VL_RESTORER(m_newRvEqnp); { - m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->cloneTree(true), index); + m_varEqnp = new AstArraySel{nodep->fileline(), m_varEqnp->cloneTree(true), index}; m_newLvEqnp - = new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index); + = new AstArraySel{nodep->fileline(), m_newLvEqnp->cloneTree(true), index}; m_newRvEqnp - = new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index); + = new AstArraySel{nodep->fileline(), m_newRvEqnp->cloneTree(true), index}; iterate(nodep->subDTypep()->skipRefp()); @@ -180,26 +205,30 @@ private: public: // CONSTRUCTORS ChangedInsertVisitor(AstVarScope* vscp, ChangedState* statep) { + // DPI export trigger should never need change detect. See similar assertions in V3Order + // (OrderVisitor::nodeMarkCircular), and V3GenClk (GenClkRenameVisitor::genInpClk). + UASSERT_OBJ(vscp != v3Global.rootp()->dpiExportTriggerp(), vscp, + "DPI export trigger should not need change detect"); m_statep = statep; m_vscp = vscp; m_detects = 0; { - AstVar* varp = m_vscp->varp(); - string newvarname - = ("__Vchglast__" + m_vscp->scopep()->nameDotless() + "__" + varp->shortName()); + AstVar* const varp = m_vscp->varp(); + string newvarname{"__Vchglast__" + m_vscp->scopep()->nameDotless() + "__" + + varp->shortName()}; // Create: VARREF(_last) // ASSIGN(VARREF(_last), VARREF(var)) // ... // CHANGEDET(VARREF(_last), VARREF(var)) - AstVar* newvarp - = new AstVar(varp->fileline(), AstVarType::MODULETEMP, newvarname, varp); + AstVar* const newvarp + = new AstVar{varp->fileline(), AstVarType::MODULETEMP, newvarname, varp}; m_statep->m_topModp->addStmtp(newvarp); - m_newvscp = new AstVarScope(m_vscp->fileline(), m_statep->m_scopetopp, newvarp); + m_newvscp = new AstVarScope{m_vscp->fileline(), m_statep->m_scopetopp, newvarp}; m_statep->m_scopetopp->addVarp(m_newvscp); - m_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, VAccess::READ); - m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, VAccess::WRITE); - m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, VAccess::READ); + m_varEqnp = new AstVarRef{m_vscp->fileline(), m_vscp, VAccess::READ}; + m_newLvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::WRITE}; + m_newRvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::READ}; } iterate(vscp->dtypep()->skipRefp()); m_varEqnp->deleteTree(); @@ -228,7 +257,7 @@ private: void genChangeDet(AstVarScope* vscp) { vscp->v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: " << vscp->prettyNameQ()); - ChangedInsertVisitor visitor(vscp, m_statep); + ChangedInsertVisitor visitor{vscp, m_statep}; } // VISITORS @@ -242,24 +271,11 @@ private: UINFO(4, " TS " << nodep << endl); // Clearing AstNode::user1ClearTree(); - // Create the change detection function - AstScope* scopep = nodep->scopep(); + // Prep for if make change detection function + AstScope* const scopep = nodep->scopep(); UASSERT_OBJ(scopep, nodep, "No scope found on top level, perhaps you have no statements?"); m_statep->m_scopetopp = scopep; - // Create a wrapper change detection function that calls each change detection function - m_statep->m_tlChgFuncp - = new AstCFunc(nodep->fileline(), "_change_request", scopep, "QData"); - m_statep->m_tlChgFuncp->isStatic(false); - m_statep->m_tlChgFuncp->isLoose(true); - m_statep->m_tlChgFuncp->declPrivate(true); - m_statep->m_scopetopp->addActivep(m_statep->m_tlChgFuncp); - // Each change detection function needs at least one AstChangeDet - // to ensure that V3EmitC outputs the necessary code. - m_statep->maybeCreateChgFuncp(); - m_statep->m_chgFuncp->addStmtsp( - new AstChangeDet(nodep->fileline(), nullptr, nullptr, false)); - iterateChildren(nodep); } virtual void visit(AstVarScope* nodep) override { @@ -288,7 +304,7 @@ void V3Changed::changedAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { ChangedState state; - ChangedVisitor visitor(nodep, &state); + ChangedVisitor visitor{nodep, &state}; } // Destruct before checking V3Global::dumpCheckGlobalTree("changed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Class.cpp b/src/V3Class.cpp index a47b052d1..da29a24c1 100644 --- a/src/V3Class.cpp +++ b/src/V3Class.cpp @@ -153,6 +153,6 @@ public: void V3Class::classAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { ClassVisitor visitor(nodep); } // Destruct before checking + { ClassVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("class", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index c9087bab7..5afa46e57 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -314,6 +314,6 @@ public: void V3Clean::cleanAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { CleanVisitor visitor(nodep); } // Destruct before checking + { CleanVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("clean", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index ddb20dec0..7f941851d 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -257,6 +257,14 @@ private: // Process the activates iterateChildren(nodep); UINFO(4, " TOPSCOPE iter done " << nodep << endl); + // Clear the DPI export trigger flag at the end of eval + if (AstVarScope* const dpiExportTriggerp = v3Global.rootp()->dpiExportTriggerp()) { + FileLine* const fl = dpiExportTriggerp->fileline(); + AstAssign* const assignp + = new AstAssign{fl, new AstVarRef{fl, dpiExportTriggerp, VAccess::WRITE}, + new AstConst{fl, AstConst::BitFalse{}}}; + m_evalFuncp->addFinalsp(assignp); + } // Split large functions splitCheck(m_evalFuncp); splitCheck(m_initFuncp); @@ -435,6 +443,6 @@ public: void V3Clock::clockAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { ClockVisitor visitor(nodep); } // Destruct before checking + { ClockVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("clock", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Combine.cpp b/src/V3Combine.cpp index b5cd7ff20..71582a60f 100644 --- a/src/V3Combine.cpp +++ b/src/V3Combine.cpp @@ -223,6 +223,6 @@ public: void V3Combine::combineAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { CombineVisitor visitor(nodep); } // Destruct before checking + { CombineVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("combine", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Common.cpp b/src/V3Common.cpp new file mode 100644 index 000000000..0cb564a5d --- /dev/null +++ b/src/V3Common.cpp @@ -0,0 +1,120 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Add common contents to modules +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* +// V3Common's Transformations: +// +// Each class: +// Create string access functions +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Common.h" +#include "V3Ast.h" +#include "V3Global.h" +#include "V3EmitCBase.h" + +//###################################################################### +// Common component builders + +static void makeVlToString(AstClass* nodep) { + AstCFunc* const funcp + = new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"}; + funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep) + ">& obj"); + funcp->isMethod(false); + funcp->isConst(false); + funcp->isStatic(false); + funcp->protect(false); + AstNode* const exprp = new AstCMath{nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0}; + exprp->dtypeSetString(); + funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp}); + nodep->addStmtp(funcp); +} +static void makeToString(AstClass* nodep) { + AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "to_string", nullptr, "std::string"}; + funcp->isConst(true); + funcp->isStatic(false); + funcp->protect(false); + AstNode* const exprp + = new AstCMath{nodep->fileline(), R"(std::string("'{") + to_string_middle() + "}")", 0}; + exprp->dtypeSetString(); + funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp}); + nodep->addStmtp(funcp); +} +static void makeToStringMiddle(AstClass* nodep) { + AstCFunc* const funcp + = new AstCFunc{nodep->fileline(), "to_string_middle", nullptr, "std::string"}; + funcp->isConst(true); + funcp->isStatic(false); + funcp->protect(false); + funcp->addStmtsp(new AstCStmt{nodep->fileline(), "std::string out;\n"}); + std::string comma; + for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) { + if (auto* const varp = VN_CAST(itemp, Var)) { + if (!varp->isParam()) { + string stmt = "out += \""; + stmt += comma; + comma = ", "; + stmt += itemp->origNameProtect(); + stmt += ":\" + "; + if (itemp->isWide()) { + stmt += "VL_TO_STRING_W("; + stmt += cvtToStr(itemp->widthWords()); + stmt += ", "; + } else { + stmt += "VL_TO_STRING("; + } + stmt += itemp->nameProtect(); + stmt += ");\n"; + nodep->user1(true); // So what we extend dumps this + funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt}); + } + } + } + if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) { + string stmt = "out += \""; + if (!comma.empty()) stmt += "\", \"+ "; + // comma = ", "; // Nothing further so not needed + stmt += nodep->extendsp()->dtypep()->nameProtect(); + stmt += "::to_string_middle();\n"; + nodep->user1(true); // So what we extend dumps this + funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt}); + } + funcp->addStmtsp(new AstCStmt{nodep->fileline(), "return out;\n"}); + nodep->addStmtp(funcp); +} + +//###################################################################### +// V3Common class functions + +void V3Common::commonAll() { + UINFO(2, __FUNCTION__ << ": " << endl); + // Create common contents for each module + for (AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) { + if (AstClass* const classp = VN_CAST(nodep, Class)) { + // NODE STATE + // Entire netlist: + // AstClass::user1() -> bool. True if class needs to_string dumper + AstUser1InUse m_inuser1; + // Create ToString methods + makeVlToString(classp); + makeToString(classp); + makeToStringMiddle(classp); + } + } + V3Global::dumpCheckGlobalTree("common", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); +} diff --git a/src/V3Common.h b/src/V3Common.h new file mode 100644 index 000000000..408507e77 --- /dev/null +++ b/src/V3Common.h @@ -0,0 +1,30 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Add common contents to modules +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* + +#ifndef VERILATOR_V3COMMON_H_ +#define VERILATOR_V3COMMON_H_ + +#include "config_build.h" +#include "verilatedos.h" + +//============================================================================ + +class V3Common final { +public: + static void commonAll(); +}; + +#endif // Guard diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 49dc1a377..6c279f8ad 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -30,8 +30,10 @@ #include "V3Width.h" #include "V3Simulate.h" #include "V3Stats.h" +#include "V3UniqueNames.h" #include +#include //###################################################################### // Utilities @@ -76,18 +78,65 @@ public: bool found() const { return m_found; } }; +static bool isConst(const AstNode* nodep, uint64_t v) { + const AstConst* const constp = VN_CAST_CONST(nodep, Const); + return constp && constp->toUQuad() == v; +} + +template +static typename std::enable_if::value, bool>::type isPow2(T val) { + return (val & (val - 1)) == 0; +} + +static int countTrailingZeroes(uint64_t val) { + UASSERT(val, "countTrailingZeroes argument must be non-zero"); +#if defined(__GNUC__) && !defined(VL_NO_BUILTINS) + return __builtin_ctzll(val); +#else + int bit = 0; + val = ~val; + while (val & 1) { + ++bit; + val >>= 1; + } + return bit; +#endif +} + // This visitor can be used in the post-expanded Ast from V3Expand, where the Ast satisfies: // - Constants are 64 bit at most (because words are accessed via AstWordSel) // - Variables are scoped. class ConstBitOpTreeVisitor final : public AstNVisitor { + // NODE STATE + // AstVarRef::user4u -> Base index of m_varInfos that points VarInfo + // AstVarScope::user4u -> Same as AstVarRef::user4 + AstUser4InUse m_inuser4; + // TYPES + // Holds a node to be added as a term in the reduction tree, it's equivalent op count, and a + // bool indicating if the term is clean (0/1 value, or if the top bits might be dirty) + using ResultTerm = std::tuple; + struct LeafInfo final { // Leaf node (either AstConst or AstVarRef) bool m_polarity = true; int m_lsb = 0; int m_wordIdx = -1; // -1 means AstWordSel is not used. AstVarRef* m_refp = nullptr; AstConst* m_constp = nullptr; + + int width() const { + UASSERT(m_refp, "m_refp should be set"); + const int width = m_refp->varp()->widthMin(); + if (!m_refp->isWide()) { + UASSERT(m_wordIdx == -1, "Bad word index into non-wide"); + return width; + } else { + UASSERT(m_wordIdx >= 0, "Bad word index into wide"); + const int bitsInMSW = VL_BITBIT_E(width) ? VL_BITBIT_E(width) : VL_EDATASIZE; + return m_wordIdx == m_refp->widthWords() - 1 ? bitsInMSW : VL_EDATASIZE; + } + } }; struct BitPolarityEntry final { // Found bit polarity during iterate() @@ -95,9 +144,9 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { bool m_polarity; int m_bit; BitPolarityEntry(const LeafInfo& info, bool pol, int bit) - : m_info(info) - , m_polarity(pol) - , m_bit(bit) {} + : m_info{info} + , m_polarity{pol} + , m_bit{bit} {} BitPolarityEntry() = default; }; @@ -105,18 +154,18 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { ConstBitOpTreeVisitor& m_visitor; const size_t m_polaritiesSize; const size_t m_frozenSize; - const int m_ops; + const unsigned m_ops; const bool m_polarity; bool m_restore; public: explicit Restorer(ConstBitOpTreeVisitor& visitor) - : m_visitor(visitor) - , m_polaritiesSize(visitor.m_bitPolarities.size()) - , m_frozenSize(visitor.m_frozenNodes.size()) - , m_ops(visitor.m_ops) - , m_polarity(visitor.m_polarity) - , m_restore(true) {} + : m_visitor{visitor} + , m_polaritiesSize{visitor.m_bitPolarities.size()} + , m_frozenSize{visitor.m_frozenNodes.size()} + , m_ops{visitor.m_ops} + , m_polarity{visitor.m_polarity} + , m_restore{true} {} ~Restorer() { UASSERT(m_visitor.m_bitPolarities.size() >= m_polaritiesSize, "m_bitPolarities must grow monotorilaclly"); @@ -137,94 +186,145 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { // Collect information for each Variable to transform as below class VarInfo final { // MEMBERS - int m_constResult = -1; // -1: result is not constant, 0 or 1: result of this tree - ConstBitOpTreeVisitor* m_parentp; // ConstBitOpTreeVisitor that holds this VarInfo - AstVarRef* m_refp; // Points the variable that this VarInfo covers + int m_knownResult = -1; // -1: result is not known, 0 or 1: result of this tree + ConstBitOpTreeVisitor* const m_parentp; // ConstBitOpTreeVisitor that holds this VarInfo + AstVarRef* const m_refp; // Points the variable that this VarInfo covers + const int m_width; // Width of term this VarInfo refers to V3Number m_bitPolarity; // Coefficient of each bit public: // METHODS - bool hasConstantResult() const { return m_constResult >= 0; } + bool hasConstResult() const { return m_knownResult >= 0 || m_bitPolarity.isAllX(); } + // The constant result. Only valid if hasConstResult() returned true. + bool getConstResult() const { + // Note that this condition covers m_knownResult == -1 but m_bitPolarity.isAllX(), + // in which case the result is 0 + return m_knownResult == 1; + } + const AstVarRef* refp() const { return m_refp; } bool sameVarAs(const AstNodeVarRef* otherp) const { return m_refp->same(otherp); } void setPolarity(bool compBit, int bit) { - UASSERT_OBJ(!hasConstantResult(), m_refp, "Already has result of " << m_constResult); - UASSERT_OBJ(bit < VL_QUADSIZE, m_refp, - "bit:" << bit << " is too big after V3Expand" - << " back:" << m_refp->backp()); - if (bit >= m_bitPolarity.width()) { // Need to expand m_bitPolarity - const V3Number oldPol = std::move(m_bitPolarity); - // oldPol.width() is 8, 16, or 32 because this visitor is called after V3Expand - // newWidth is increased by 2x because - // - CCast will cast to such bitwidth anyway - // - can avoid frequent expansion - int newWidth = oldPol.width(); - while (bit >= newWidth) newWidth *= 2; - m_bitPolarity = V3Number{m_refp, newWidth}; - UASSERT_OBJ(newWidth == 16 || newWidth == 32 || newWidth == 64, m_refp, - "bit:" << bit << " newWidth:" << newWidth); - m_bitPolarity.setAllBitsX(); - for (int i = 0; i < oldPol.width(); ++i) { - if (oldPol.bitIs0(i)) - m_bitPolarity.setBit(i, '0'); - else if (oldPol.bitIs1(i)) - m_bitPolarity.setBit(i, '1'); - } - } - UASSERT_OBJ(bit < m_bitPolarity.width(), m_refp, - "bit:" << bit << " width:" << m_bitPolarity.width() << m_refp); - if (m_bitPolarity.bitIsX(bit)) { // The bit is not yet set + // Ignore if already determined a known reduction + if (m_knownResult >= 0) return; + UASSERT_OBJ(bit < m_width, m_refp, + "Bit index out of range: " << bit << " width: " << m_width); + if (m_bitPolarity.bitIsX(bit)) { // The bit is not yet marked with either polarity m_bitPolarity.setBit(bit, compBit); - } else { // Priviously set the bit + } else { // The bit has already been marked with some polarity const bool sameFlag = m_bitPolarity.bitIs1(bit) == compBit; if (m_parentp->isXorTree()) { - // ^{x[0], ~x[0], x[2], x[3]} === ~^{x[2], x[3]} - UASSERT_OBJ(sameFlag, m_refp, "Only true is set in Xor tree"); + UASSERT_OBJ(compBit && sameFlag, m_refp, "Only true is set in Xor tree"); + // a ^ a ^ b == b so we can ignore a m_bitPolarity.setBit(bit, 'x'); } else { // And, Or - // Can ignore this nodep as the bit is already registered + // Can ignore this nodep as the bit is already marked with the same polarity if (sameFlag) return; // a & a == a, b | b == b - // Otherwise result is constant - m_constResult = m_parentp->isAndTree() ? 0 : 1; + // Otherwise result is constant (a & ~a == 0) or (a | ~a == 1) + m_knownResult = m_parentp->isAndTree() ? 0 : 1; m_bitPolarity.setAllBitsX(); // The variable is not referred anymore } } } - AstNode* getResult() const { - FileLine* fl = m_refp->fileline(); + + // Return reduction term for this VarInfo, together with the number of ops in the term, + // and a boolean indicating if the term is clean (1-bit vs multi-bit value) + ResultTerm getResultTerm() const { + UASSERT(!hasConstResult(), "getTerm on reduction that yields constant"); + FileLine* const fl = m_refp->fileline(); + + // Get the term we are referencing (the WordSel, if wide, otherwise just the VarRef) AstNode* srcp = VN_CAST(m_refp->backp(), WordSel); if (!srcp) srcp = m_refp; - const int width = m_bitPolarity.width(); + srcp = srcp->cloneTree(false); - if (hasConstantResult()) - return new AstConst{fl, - V3Number{srcp, width, static_cast(m_constResult)}}; + // Signed variables might have redundant sign bits that need masking. + const bool hasRedundantSignBits + = m_refp->varp()->dtypep()->isSigned() + && (m_refp->isWide() ? (m_width != VL_EDATASIZE) + : (m_width < 8 || !isPow2(m_width))); - AstConst* maskValuep = new AstConst{fl, V3Number{srcp, width, 0}}; - maskValuep->num().opBitsNonX(m_bitPolarity); // 'x' -> 0, 0->1, 1->1 - // Let AstConst be in lhs as it is the common convention - AstAnd* maskedp = new AstAnd{fl, maskValuep, srcp->cloneTree(false)}; - AstNode* resultp; - if (m_parentp->isXorTree()) { - resultp = new AstRedXor{fl, maskedp}; - resultp->dtypep()->widthForce(width, 1); + // Get the mask that selects the bits that are relevant in this term + V3Number maskNum{srcp, m_width, 0}; + maskNum.opBitsNonX(m_bitPolarity); // 'x' -> 0, 0->1, 1->1 + const uint64_t maskVal = maskNum.toUQuad(); + UASSERT(maskVal != 0, "Should have been recognized as having const 0 result"); + + // Parts of the return value + AstNode* resultp = srcp; // The tree for this term + unsigned ops = 0; // Number of ops in this term + bool clean = false; // Whether the term is clean (has value 0 or 1) + + if (isPow2(maskVal)) { + // If we only want a single bit, shift it out instead of a masked compare. Shifts + // don't go through the flags register on x86 and are hence faster. This is also + // always fewer or same ops as mask and compare, but with shorter instructions on + // x86. + + // Find the index of the bit we want. + const int bit = countTrailingZeroes(maskVal); + // If we want something other than the bottom bit, shift it out + if (bit != 0) { + resultp = new AstShiftR{fl, resultp, + new AstConst{fl, static_cast(bit)}, m_width}; + ++ops; + } + // Negate it if necessary + const bool negate = m_bitPolarity.bitIs0(bit); + if (negate) { + resultp = new AstNot{fl, resultp}; + ++ops; + } + // Clean if MSB of unsigned value, and not negated + clean = (bit == m_width - 1) && !hasRedundantSignBits && !negate; } else { - AstConst* compValuep = maskValuep->cloneTree(false); - compValuep->num().opBitsOne(m_bitPolarity); // 'x'->0, 0->0, 1->1 - if (m_parentp->isAndTree()) { - resultp = new AstEq{fl, compValuep, maskedp}; + // We want multiple bits. Go ahead and extract them. + + // Check if masking is required, and if so apply it + const bool needsMasking = maskVal != VL_MASK_Q(m_width) || hasRedundantSignBits; + if (needsMasking) { + resultp = new AstAnd{fl, new AstConst{fl, maskNum}, resultp}; + ++ops; + } + + // Create the sub-expression for this term + if (m_parentp->isXorTree()) { + if (needsMasking) { + // Reduce the masked term to the minimum known width, + // to use the smallest RedXor formula + const int widthMin = maskNum.widthMin(); + resultp->dtypeChgWidth(widthMin, widthMin); + } + resultp = new AstRedXor{fl, resultp}; + ++ops; + clean = false; + // VL_REDXOR_* returns IData, set width accordingly to avoid unnecessary casts + resultp->dtypeChgWidth(VL_IDATASIZE, 1); + } else if (m_parentp->isAndTree()) { + V3Number compNum{srcp, m_width, 0}; + compNum.opBitsOne(m_bitPolarity); // 'x'->0, 0->0, 1->1 + resultp = new AstEq{fl, new AstConst{fl, compNum}, resultp}; + ++ops; + clean = true; } else { // Or - compValuep->num().opXor(V3Number{compValuep->num()}, maskValuep->num()); - resultp = new AstNeq{fl, compValuep, maskedp}; + V3Number compNum{srcp, m_width, 0}; + compNum.opBitsOne(m_bitPolarity); // 'x'->0, 0->0, 1->1 + compNum.opXor(V3Number{compNum}, maskNum); + resultp = new AstNeq{fl, new AstConst{fl, compNum}, resultp}; + ++ops; + clean = true; } } - return resultp; + + return ResultTerm{resultp, ops, clean}; } + public: // CONSTRUCTORS - VarInfo(ConstBitOpTreeVisitor* parent, AstVarRef* refp) - : m_parentp(parent) - , m_refp(refp) - , m_bitPolarity(refp, refp->isWide() ? VL_EDATASIZE : refp->width()) { + VarInfo(ConstBitOpTreeVisitor* parent, AstVarRef* refp, int width) + : m_parentp{parent} + , m_refp{refp} + , m_width{width} + , m_bitPolarity{refp, m_width} { m_bitPolarity.setAllBitsX(); } }; @@ -232,21 +332,15 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { // MEMBERS bool m_failed = false; bool m_polarity = true; // Flip when AstNot comes - int m_ops = 0; // Number of operations such as And, Or, Xor, Sel... + unsigned m_ops; // Number of operations such as And, Or, Xor, Sel... int m_lsb = 0; // Current LSB LeafInfo* m_leafp = nullptr; // AstConst or AstVarRef that currently looking for - AstNode* m_rootp; // Root of this AST subtree - AstNode* m_curOpp = nullptr; // The node that should be added to m_frozenNodes + AstNode* const m_rootp; // Root of this AST subtree - AstUser4InUse m_inuser4; std::vector m_frozenNodes; // Nodes that cannot be optimized std::vector m_bitPolarities; // Polarity of bits found during iterate() std::vector> m_varInfos; // VarInfo for each variable, [0] is nullptr - // NODE STATE - // AstVarRef::user4u -> Base index of m_varInfos that points VarInfo - // AstVarScope::user4u -> Same as AstVarRef::user4 - // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -260,12 +354,12 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { #define CONST_BITOP_SET_FAILED(reason, nodep) setFailed(true, reason, nodep, __LINE__) bool setFailed(bool fail, const char* reason, AstNode* nodep, int line) { - if (fail) { + if (fail && !m_failed) { UINFO(9, "cannot optimize " << m_rootp << " reason:" << reason << " called from line:" << line << " when checking:" << nodep << std::endl); // if (debug() >= 9) m_rootp->dumpTree(std::cout << "Root node:\n"); + m_failed = true; } - m_failed |= fail; return m_failed; } void incrOps(const AstNode* nodep, int line) { @@ -287,7 +381,7 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { const size_t idx = baseIdx + std::max(0, ref.m_wordIdx); VarInfo* varInfop = m_varInfos[idx].get(); if (!varInfop) { - varInfop = new VarInfo{this, ref.m_refp}; + varInfop = new VarInfo{this, ref.m_refp, ref.width()}; m_varInfos[idx].reset(varInfop); } else { if (!varInfop->sameVarAs(ref.m_refp)) @@ -306,22 +400,12 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { } bool ok = !m_failed; - if (expectConst) + if (expectConst) { ok &= !info.m_refp && info.m_constp; - else + } else { ok &= info.m_refp && !info.m_constp; - return ok ? info : LeafInfo{}; - } - AstNode* combineTree(AstNode* lhsp, AstNode* rhsp) { - if (!lhsp) return rhsp; - if (isAndTree()) - return new AstAnd(m_rootp->fileline(), lhsp, rhsp); - else if (isOrTree()) - return new AstOr(m_rootp->fileline(), lhsp, rhsp); - else { - UASSERT_OBJ(isXorTree(), m_rootp, "must be either Xor or RedXor"); - return new AstXor(m_rootp->fileline(), lhsp, rhsp); } + return ok ? info : LeafInfo{}; } // VISITORS @@ -341,7 +425,10 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { virtual void visit(AstNot* nodep) override { CONST_BITOP_RETURN_IF(nodep->widthMin() != 1, nodep); AstNode* lhsp = nodep->lhsp(); - CONST_BITOP_RETURN_IF(VN_IS(lhsp, And) || VN_IS(lhsp, Or) || VN_IS(lhsp, Const), lhsp); + if (AstCCast* castp = VN_CAST(lhsp, CCast)) lhsp = castp->lhsp(); + CONST_BITOP_RETURN_IF(!VN_IS(lhsp, VarRef) && !VN_IS(lhsp, Xor) && !VN_IS(lhsp, RedXor) + && !VN_IS(lhsp, ShiftR), + lhsp); incrOps(nodep, __LINE__); m_polarity = !m_polarity; iterateChildren(nodep); @@ -370,134 +457,164 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { m_leafp->m_lsb = m_lsb; } - virtual void visit(AstRedXor* nodep) override { // Expect '^(mask & v)' + virtual void visit(AstRedXor* nodep) override { Restorer restorer{*this}; CONST_BITOP_RETURN_IF(!VN_IS(m_rootp, Xor), nodep); - AstAnd* andp = VN_CAST(nodep->lhsp(), And); - CONST_BITOP_RETURN_IF(!andp, nodep->lhsp()); + AstNode* lhsp = nodep->lhsp(); + if (AstCCast* const castp = VN_CAST(lhsp, CCast)) lhsp = castp->lhsp(); + if (AstAnd* const andp = VN_CAST(lhsp, And)) { // '^(mask & leaf)' + CONST_BITOP_RETURN_IF(!andp, lhsp); - const auto mask = findLeaf(andp->lhsp(), true); - CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp()); + const LeafInfo& mask = findLeaf(andp->lhsp(), true); + CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp()); - const LeafInfo leaf = findLeaf(andp->rhsp(), false); - CONST_BITOP_RETURN_IF(!leaf.m_refp, andp->rhsp()); + const LeafInfo& ref = findLeaf(andp->rhsp(), false); + CONST_BITOP_RETURN_IF(!ref.m_refp, andp->rhsp()); - restorer.disableRestore(); // Now all subtree succeeded + restorer.disableRestore(); // Now all subtree succeeded - incrOps(nodep, __LINE__); - incrOps(andp, __LINE__); - const V3Number& maskNum = mask.m_constp->num(); - for (int i = 0; i < maskNum.width(); ++i) { - // Set true, m_treePolarity takes care of the entire parity - if (maskNum.bitIs1(i)) m_bitPolarities.emplace_back(leaf, true, i + leaf.m_lsb); + const V3Number& maskNum = mask.m_constp->num(); + + incrOps(nodep, __LINE__); + incrOps(andp, __LINE__); + + // Mark all bits checked in this reduction + const int maxBitIdx = std::min(ref.m_lsb + maskNum.width(), ref.width()); + for (int bitIdx = ref.m_lsb; bitIdx < maxBitIdx; ++bitIdx) { + const int maskIdx = bitIdx - ref.m_lsb; + if (maskNum.bitIs0(maskIdx)) continue; + // Set true, m_polarity takes care of the entire parity + m_bitPolarities.emplace_back(ref, true, bitIdx); + } + } else { // '^leaf' + const LeafInfo& ref = findLeaf(lhsp, false); + CONST_BITOP_RETURN_IF(!ref.m_refp, lhsp); + + restorer.disableRestore(); // Now all checks passed + + incrOps(nodep, __LINE__); + + // Mark all bits checked by this comparison + for (int bitIdx = ref.m_lsb; bitIdx < ref.width(); ++bitIdx) { + m_bitPolarities.emplace_back(ref, true, bitIdx); + } } } virtual void visit(AstNodeBiop* nodep) override { - const auto isConst = [](AstNode* nodep, vluint64_t v) -> bool { - AstConst* constp = VN_CAST(nodep, Const); - return constp && constp->toUQuad() == v; - }; - if (nodep->type() == m_rootp->type()) { // And, Or, Xor - CONST_BITOP_RETURN_IF(!m_polarity && isXorTree(), nodep); - incrOps(nodep, __LINE__); - VL_RESTORER(m_curOpp); - VL_RESTORER(m_leafp); - - for (int i = 0; i < 2; ++i) { - Restorer restorer{*this}; - LeafInfo leafInfo; - m_leafp = &leafInfo; - m_curOpp = i == 0 ? nodep->lhsp() : nodep->rhsp(); - const bool origFailed = m_failed; - iterate(m_curOpp); - if (leafInfo.m_constp || m_failed) { - // Rvert changes in leaf - restorer.restoreNow(); - m_frozenNodes.push_back(m_curOpp); - m_failed = origFailed; - continue; - } - restorer.disableRestore(); // Now all checks passed - if (leafInfo.m_refp) - m_bitPolarities.emplace_back(leafInfo, isXorTree() || leafInfo.m_polarity, - leafInfo.m_lsb); - } - return; - } else if (VN_IS(m_rootp, Xor) && VN_IS(nodep, Eq) && isConst(nodep->lhsp(), 0) - && VN_IS(nodep->rhsp(), And)) { // 0 == (1 & RedXor) - Restorer restorer{*this}; - AstAnd* andp = static_cast(nodep->rhsp()); // already checked above - CONST_BITOP_RETURN_IF(!isConst(andp->lhsp(), 1), andp->lhsp()); - AstRedXor* redXorp = VN_CAST(andp->rhsp(), RedXor); - CONST_BITOP_RETURN_IF(!redXorp, andp->rhsp()); - incrOps(nodep, __LINE__); - incrOps(andp, __LINE__); - m_polarity = !m_polarity; - iterate(redXorp); - CONST_BITOP_RETURN_IF(m_failed, redXorp); - - restorer.disableRestore(); // Now all checks passed - return; - } else if (VN_IS(m_rootp, Xor) && VN_IS(nodep, And) && isConst(nodep->lhsp(), 1) - && (VN_IS(nodep->rhsp(), Xor) - || VN_IS(nodep->rhsp(), RedXor))) { // 1 & (v[3] ^ v[2]) + if (VN_IS(nodep, And) && isConst(nodep->lhsp(), 1)) { // 1 & _ + // Always reach past a plain making AND Restorer restorer{*this}; incrOps(nodep, __LINE__); iterate(nodep->rhsp()); CONST_BITOP_RETURN_IF(m_failed, nodep->rhsp()); restorer.disableRestore(); // Now all checks passed - return; + } else if (nodep->type() == m_rootp->type()) { // And, Or, Xor + incrOps(nodep, __LINE__); + VL_RESTORER(m_leafp); + + for (const bool right : {false, true}) { + Restorer restorer{*this}; + LeafInfo leafInfo; + m_leafp = &leafInfo; + AstNode* opp = right ? nodep->rhsp() : nodep->lhsp(); + const bool origFailed = m_failed; + iterate(opp); + if (leafInfo.m_constp || m_failed) { + // Revert changes in leaf + restorer.restoreNow(); + // Reach past a cast then add to frozen nodes to be added to final reduction + if (AstCCast* const castp = VN_CAST(opp, CCast)) opp = castp->lhsp(); + m_frozenNodes.push_back(opp); + m_failed = origFailed; + continue; + } + restorer.disableRestore(); // Now all checks passed + if (leafInfo.m_refp) { + // The conditional on the lsb being in range is necessary for some degenerate + // case, e.g.: (IData)((QData)wide[0] >> 32), or <1-bit-var> >> 1, which is + // just zero + if (leafInfo.m_lsb < leafInfo.width()) { + m_bitPolarities.emplace_back(leafInfo, isXorTree() || leafInfo.m_polarity, + leafInfo.m_lsb); + } else if (isAndTree()) { + // If there is a constant 0 term in an And tree, we must include it. Fudge + // this by adding a bit with both polarities, which will simplify to zero + m_bitPolarities.emplace_back(leafInfo, true, 0); + m_bitPolarities.emplace_back(leafInfo, false, 0); + } + } + } } else if ((isAndTree() && VN_IS(nodep, Eq)) || (isOrTree() && VN_IS(nodep, Neq))) { Restorer restorer{*this}; CONST_BITOP_RETURN_IF(!m_polarity, nodep); + AstNode* lhsp = nodep->lhsp(); + if (AstCCast* const castp = VN_CAST(lhsp, CCast)) lhsp = castp->lhsp(); + AstConst* const constp = VN_CAST(lhsp, Const); + CONST_BITOP_RETURN_IF(!constp, nodep->lhsp()); + const bool maskFlip = isOrTree(); - const LeafInfo comp = findLeaf(nodep->lhsp(), true); - CONST_BITOP_RETURN_IF(!comp.m_constp || comp.m_lsb != 0, nodep->lhsp()); + const V3Number& compNum = constp->num(); - AstAnd* andp = VN_CAST(nodep->rhsp(), And); // comp == (mask & v) - CONST_BITOP_RETURN_IF(!andp, nodep->rhsp()); + if (AstAnd* const andp = VN_CAST(nodep->rhsp(), And)) { // comp == (mask & v) + const LeafInfo& mask = findLeaf(andp->lhsp(), true); + CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp()); - const LeafInfo mask = findLeaf(andp->lhsp(), true); - CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp()); + const LeafInfo& ref = findLeaf(andp->rhsp(), false); + CONST_BITOP_RETURN_IF(!ref.m_refp, andp->rhsp()); - const LeafInfo ref = findLeaf(andp->rhsp(), false); - CONST_BITOP_RETURN_IF(!ref.m_refp, andp->rhsp()); + restorer.disableRestore(); // Now all checks passed - restorer.disableRestore(); // Now all checks passed + const V3Number& maskNum = mask.m_constp->num(); - const V3Number maskNum = mask.m_constp->num(); - const V3Number compNum = comp.m_constp->num(); - for (int i = 0; i < maskNum.width(); ++i) { - const int bit = i + ref.m_lsb; - if (maskNum.bitIs0(i)) continue; - m_bitPolarities.emplace_back(ref, compNum.bitIs1(i) != maskFlip, bit); + incrOps(nodep, __LINE__); + incrOps(andp, __LINE__); + + // Mark all bits checked by this comparison + const int maxBitIdx = std::min(ref.m_lsb + compNum.width(), ref.width()); + for (int bitIdx = ref.m_lsb; bitIdx < maxBitIdx; ++bitIdx) { + const int maskIdx = bitIdx - ref.m_lsb; + if (maskNum.bitIs0(maskIdx)) continue; + const bool polarity = compNum.bitIs1(maskIdx) != maskFlip; + m_bitPolarities.emplace_back(ref, polarity, bitIdx); + } + } else { // comp == v + const LeafInfo& ref = findLeaf(nodep->rhsp(), false); + CONST_BITOP_RETURN_IF(!ref.m_refp, nodep->rhsp()); + + restorer.disableRestore(); // Now all checks passed + + incrOps(nodep, __LINE__); + + // Mark all bits checked by this comparison + const int maxBitIdx = std::min(ref.m_lsb + compNum.width(), ref.width()); + for (int bitIdx = ref.m_lsb; bitIdx < maxBitIdx; ++bitIdx) { + const int maskIdx = bitIdx - ref.m_lsb; + const bool polarity = compNum.bitIs1(maskIdx) != maskFlip; + m_bitPolarities.emplace_back(ref, polarity, bitIdx); + } } - incrOps(nodep, __LINE__); - incrOps(andp, __LINE__); - return; + } else { + CONST_BITOP_SET_FAILED("Mixture of different ops cannot be optimized", nodep); } - CONST_BITOP_SET_FAILED("Mixture of different ops cannot be optimized", nodep); } // CONSTRUCTORS - ConstBitOpTreeVisitor(AstNode* nodep, int ops) - : m_ops(ops) - , m_rootp(nodep) { + ConstBitOpTreeVisitor(AstNode* nodep, unsigned externalOps) + : m_ops{externalOps} + , m_rootp{nodep} { // Fill nullptr at [0] because AstVarScope::user4 is 0 by default m_varInfos.push_back(nullptr); CONST_BITOP_RETURN_IF(!isAndTree() && !isOrTree() && !isXorTree(), nodep); - AstNode::user4ClearTree(); - if (AstNodeBiop* biopp = VN_CAST(nodep, NodeBiop)) { + if (AstNodeBiop* const biopp = VN_CAST(nodep, NodeBiop)) { iterate(biopp); } else { + UASSERT_OBJ(VN_IS(nodep, RedXor), nodep, "Must be RedXor"); incrOps(nodep, __LINE__); iterateChildren(nodep); } for (auto&& entry : m_bitPolarities) { - VarInfo& info = getVarInfo(entry.m_info); - if (info.hasConstantResult()) continue; - info.setPolarity(entry.m_polarity, entry.m_bit); + getVarInfo(entry.m_info).setPolarity(entry.m_polarity, entry.m_bit); } UASSERT_OBJ(isXorTree() || m_polarity, nodep, "must be the original polarity"); } @@ -514,58 +631,173 @@ public: // (3'b000 != (3'b011 & v)) | v[2] => 3'b000 != (3'b111 & v) // Reduction ops are transformed in the same way. // &{v[0], v[1]} => 2'b11 == (2'b11 & v) - static AstNode* simplify(AstNode* nodep, int ops, VDouble0& reduction) { - ConstBitOpTreeVisitor visitor{nodep, ops}; + static AstNode* simplify(AstNode* nodep, int resultWidth, unsigned externalOps, + VDouble0& reduction) { + UASSERT_OBJ(1 <= resultWidth && resultWidth <= 64, nodep, "resultWidth out of range"); + + // Walk tree, gathering all terms referenced in expression + ConstBitOpTreeVisitor visitor{nodep, externalOps}; + + // If failed on root node is not optimizable, or there are no variable terms, then done if (visitor.m_failed || visitor.m_varInfos.size() == 1) return nullptr; - // Two ops for each varInfo. (And and Eq) - const int vars = visitor.m_varInfos.size() - 1; - int constTerms = 0; + // FileLine used for constructing all new nodes in this function + FileLine* const fl = nodep->fileline(); + + // Get partial result each term referenced, count total number of ops and keep track of + // whether we have clean/dirty terms. visitor.m_varInfos appears in deterministic order, + // so the optimized tree is deterministic as well. + + std::vector termps; + termps.reserve(visitor.m_varInfos.size() - 1); + unsigned resultOps = 0; + bool hasCleanTerm = false; + bool hasDirtyTerm = false; + for (auto&& v : visitor.m_varInfos) { - if (v && v->hasConstantResult()) ++constTerms; - } - // Expected number of ops after this simplification - // e.g. (comp0 == (mask0 & var0)) & (comp1 == (mask1 & var1)) & .... - // e.g. redXor(mask1 & var0) ^ redXor(mask1 & var1) - // 2 ops per variables, numVars - 1 ops among variables - int expOps = 2 * (vars - constTerms) + vars - 1; - expOps += 2 * visitor.m_frozenNodes.size(); - if (visitor.isXorTree()) { - ++expOps; // AstRedXor::cleanOut() == false, so need 1 & redXor - if (!visitor.m_polarity) ++expOps; // comparison with 0 - } - if (visitor.m_ops <= expOps) return nullptr; // Unless benefitial, return - - reduction += visitor.m_ops - expOps; - - AstNode* resultp = nullptr; - // VarInfo in visitor.m_varInfos appears in deterministic order, - // so the optimized AST is deterministic too. - for (auto&& varinfop : visitor.m_varInfos) { - if (!varinfop) continue; - AstNode* partialresultp = varinfop->getResult(); - resultp = visitor.combineTree(resultp, partialresultp); - } - AstNode* frozensp = nullptr; - for (AstNode* frozenp : visitor.m_frozenNodes) { - frozenp->unlinkFrBack(); - frozensp = visitor.combineTree(frozensp, frozenp); - } - if (frozensp) resultp = visitor.combineTree(resultp, frozensp); - - if (visitor.isXorTree()) { - // VL_REDXOR_N functions don't guarantee to return only 0/1 - const int width = resultp->width(); - FileLine* fl = nodep->fileline(); - resultp = new AstAnd{fl, new AstConst{fl, V3Number{nodep, width, 1}}, resultp}; - if (!visitor.m_polarity) { - resultp = new AstEq{fl, new AstConst{fl, V3Number{nodep, width, 0}}, resultp}; - resultp->dtypep()->widthForce(1, 1); + if (!v) continue; // Skip nullptr at m_varInfos[0] + if (v->hasConstResult()) { + // If a constant term is known, we can either drop it or the whole tree is constant + AstNode* resultp = nullptr; + if (v->getConstResult()) { + UASSERT_OBJ(visitor.isOrTree(), nodep, + "Only OR tree can yield known 1 result"); + UINFO(9, "OR tree with const 1 term: " << v->refp() << endl); + // Known 1 bit in OR tree, whole result is 1 + resultp = new AstConst{fl, AstConst::BitTrue{}}; + } else if (visitor.isAndTree()) { + UINFO(9, "AND tree with const 0 term: " << v->refp() << endl); + // Known 0 bit in AND tree, whole result is 0 + resultp = new AstConst{fl, AstConst::BitFalse{}}; + } else { + // Known 0 bit in OR or XOR tree. Ignore it. + continue; + } + // Set width and widthMin precisely + resultp->dtypeChgWidth(resultWidth, 1); + for (AstNode* const termp : termps) termp->deleteTree(); + return resultp; + } + const ResultTerm result = v->getResultTerm(); + termps.push_back(std::get<0>(result)); + resultOps += std::get<1>(result); + if (std::get<2>(result)) { + hasCleanTerm = true; + UINFO(9, "Clean term: " << termps.back() << endl); + } else { + hasDirtyTerm = true; + UINFO(9, "Dirty term: " << termps.back() << endl); } } - if (resultp->width() != nodep->width()) { - resultp = new AstCCast{resultp->fileline(), resultp, nodep}; + + // Check if frozen terms are clean or not + for (AstNode* const termp : visitor.m_frozenNodes) { + // Comparison operators are clean + if (VN_IS(termp, Eq) || VN_IS(termp, Neq) || VN_IS(termp, Lt) || VN_IS(termp, Lte) + || VN_IS(termp, Gt) || VN_IS(termp, Gte)) { + hasCleanTerm = true; + } else { + // Otherwise, conservatively assume the frozen term is dirty + hasDirtyTerm = true; + UINFO(9, "Dirty frozen term: " << termp << endl); + } } + + // Figure out if a final negation is required + const bool needsFlip = visitor.isXorTree() && !visitor.m_polarity; + + // Figure out if the final tree needs cleaning + const bool needsCleaning = visitor.isAndTree() ? !hasCleanTerm : hasDirtyTerm; + + // Add size of reduction tree to op count + resultOps += termps.size() + visitor.m_frozenNodes.size() - 1; + // Add final polarity flip in Xor tree + if (needsFlip) ++resultOps; + // Add final cleaning AND + if (needsCleaning) ++resultOps; + + if (debug() >= 9) { // LCOV_EXCL_START + cout << "Bitop tree considered: " << endl; + for (AstNode* const termp : termps) termp->dumpTree("Reduced term: "); + for (AstNode* const termp : visitor.m_frozenNodes) termp->dumpTree("Frozen term: "); + cout << "Needs flipping: " << needsFlip << endl; + cout << "Needs cleaning: " << needsCleaning << endl; + cout << "Size: " << resultOps << " input size: " << visitor.m_ops << endl; + } // LCOV_EXCL_END + + // Sometimes we have no terms left after ignoring redundant terms + // (all of which were zeroes) + if (termps.empty() && visitor.m_frozenNodes.empty()) { + reduction += visitor.m_ops; + AstNode* const resultp = needsFlip ? new AstConst{fl, AstConst::BitTrue{}} + : new AstConst{fl, AstConst::BitFalse{}}; + resultp->dtypeChgWidth(resultWidth, 1); + return resultp; + } + + // Only substitute the result if beneficial as determined by operation count + if (visitor.m_ops <= resultOps) { + for (AstNode* const termp : termps) termp->deleteTree(); + return nullptr; + } + + // Update statistics + reduction += visitor.m_ops - resultOps; + + // Reduction op to combine terms + const auto reduce = [&visitor, fl](AstNode* lhsp, AstNode* rhsp) -> AstNode* { + if (!lhsp) return rhsp; + if (visitor.isAndTree()) { + return new AstAnd{fl, lhsp, rhsp}; + } else if (visitor.isOrTree()) { + return new AstOr{fl, lhsp, rhsp}; + } else { + return new AstXor{fl, lhsp, rhsp}; + } + }; + + // Compute result by reducing all terms + AstNode* resultp = nullptr; + for (AstNode* const termp : termps) { // + resultp = reduce(resultp, termp); + } + // Add any frozen terms to the reduction + for (AstNode* const frozenp : visitor.m_frozenNodes) { + resultp = reduce(resultp, frozenp->unlinkFrBack()); + } + + // Set width of masks to expected result width. This is required to prevent later removal + // of the masking node e.g. by the "AND with all ones" rule. If the result width happens + // to be 1, we still need to ensure the AstAnd is not dropped, so use a wider maks in this + // special case. + const int maskWidth = resultWidth == 1 ? VL_IDATASIZE : resultWidth; + + // Apply final polarity flip + if (needsFlip) { + if (needsCleaning) { + // Cleaning will be added below. Use a NOT which is a byte shorter on x86 + resultp = new AstNot{fl, resultp}; + } else { + // Keep result clean by using XOR(1, _) + AstConst* const maskp = new AstConst{fl, AstConst::WidthedValue{}, maskWidth, 1}; + resultp = new AstXor{fl, maskp, resultp}; + } + } + + // Apply final cleaning + if (needsCleaning) { + AstConst* const maskp = new AstConst{fl, AstConst::WidthedValue{}, maskWidth, 1}; + resultp = new AstAnd{fl, maskp, resultp}; + } + + // Cast back to original size if required + if (resultp->width() != resultWidth) { + resultp = new AstCCast{fl, resultp, resultWidth, 1}; + } + + // Set width and widthMin precisely + resultp->dtypeChgWidth(resultWidth, 1); + return resultp; } }; @@ -599,6 +831,9 @@ private: AstNode* m_scopep = nullptr; // Current scope AstAttrOf* m_attrp = nullptr; // Current attribute VDouble0 m_statBitOpReduction; // Ops reduced in ConstBitOpTreeVisitor + const bool m_globalPass; // ConstVisitor invoked as a global pass + static uint32_t s_globalPassNum; // Counts number of times ConstVisitor invoked as global pass + V3UniqueNames m_concswapNames; // For generating unique temporary variable names // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -836,7 +1071,8 @@ private: } else if (AstShiftL* const shiftp = VN_CAST(nodep->rhsp(), ShiftL)) { if (const AstConst* scp = VN_CAST_CONST(shiftp->rhsp(), Const)) { // Check if mask is full over the non-zero bits - V3Number maskLo(nodep, nodep->width()), maskHi(nodep, nodep->width()); + V3Number maskLo(nodep, nodep->width()); + V3Number maskHi(nodep, nodep->width()); maskLo.setMask(nodep->width() - scp->num().toUInt()); maskHi.opShiftL(maskLo, scp->num()); return checkMask(maskHi); @@ -846,35 +1082,41 @@ private: } bool matchBitOpTree(AstNode* nodep) { + if (nodep->widthMin() != 1) return false; if (!v3Global.opt.oConstBitOpTree()) return false; + string debugPrefix; + if (debug() >= 9) { // LCOV_EXCL_START + static int c = 0; + debugPrefix = "matchBitOpTree["; + debugPrefix += cvtToStr(++c); + debugPrefix += "] "; + nodep->dumpTree(debugPrefix + "INPUT: "); + } // LCOV_EXCL_STOP + AstNode* newp = nullptr; - bool tried = false; - if (AstAnd* andp = VN_CAST(nodep, And)) { // 1 & BitOpTree - if (AstConst* bitMaskp = VN_CAST(andp->lhsp(), Const)) { - if (bitMaskp->num().toUQuad() != 1) return false; - newp = ConstBitOpTreeVisitor::simplify(andp->rhsp(), 1, m_statBitOpReduction); - tried = true; - } - } - if (!tried) { - // (comp == BitOpTree) & BitOpTree - // (comp != BitOpTree) | BitOpTree - newp = ConstBitOpTreeVisitor::simplify(nodep, 0, m_statBitOpReduction); + const AstAnd* const andp = VN_CAST(nodep, And); + const int width = nodep->width(); + if (andp && isConst(andp->lhsp(), 1)) { // 1 & BitOpTree + newp = ConstBitOpTreeVisitor::simplify(andp->rhsp(), width, 1, m_statBitOpReduction); + } else { // BitOpTree + newp = ConstBitOpTreeVisitor::simplify(nodep, width, 0, m_statBitOpReduction); } + if (newp) { UINFO(4, "Transformed leaf of bit tree to " << newp << std::endl); - 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); } + + if (debug() >= 9) { // LCOV_EXCL_START + if (newp) { + newp->dumpTree(debugPrefix + "RESULT: "); + } else { + cout << debugPrefix << "not replaced" << endl; + } + } // LCOV_EXCL_STOP + return newp; } static bool operandShiftSame(const AstNode* nodep) { @@ -1495,9 +1737,10 @@ private: // -> EXTEND(nodep) // like a AstExtend{$rhsp}, but we need to set the width correctly from base node arg0p->unlinkFrBack(); - AstNode* newp = (VN_IS(nodep, ExtendS) - ? static_cast(new AstExtendS(nodep->fileline(), arg0p)) - : static_cast(new AstExtend(nodep->fileline(), arg0p))); + AstNode* const newp + = (VN_IS(nodep, ExtendS) + ? static_cast(new AstExtendS{nodep->fileline(), arg0p}) + : static_cast(new AstExtend{nodep->fileline(), arg0p})); newp->dtypeFrom(nodep); nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); @@ -1713,8 +1956,8 @@ private: // Note only do this (need user4) when m_warn, which is // done as unique visitor AstUser4InUse m_inuser4; - ConstVarMarkVisitor mark(nodep->lhsp()); - ConstVarFindVisitor find(nodep->rhsp()); + ConstVarMarkVisitor mark{nodep->lhsp()}; + ConstVarFindVisitor find{nodep->rhsp()}; if (find.found()) need_temp = true; } if (need_temp) { @@ -1758,14 +2001,16 @@ private: newp = AstNode::addNext(newp, asn2ap); } else { UASSERT_OBJ(m_modp, nodep, "Not under module"); + UASSERT_OBJ(m_globalPass, nodep, + "Should not reach here when not invoked on whole AstNetlist"); // We could create just one temp variable, but we'll get better optimization // if we make one per term. - string name1 = (string("__Vconcswap") + cvtToStr(m_modp->varNumGetInc())); - string name2 = (string("__Vconcswap") + cvtToStr(m_modp->varNumGetInc())); - AstVar* temp1p = new AstVar(sel1p->fileline(), AstVarType::BLOCKTEMP, name1, - VFlagLogicPacked(), msb1 - lsb1 + 1); - AstVar* temp2p = new AstVar(sel2p->fileline(), AstVarType::BLOCKTEMP, name2, - VFlagLogicPacked(), msb2 - lsb2 + 1); + AstVar* const temp1p + = new AstVar(sel1p->fileline(), AstVarType::BLOCKTEMP, + m_concswapNames.get(sel1p), VFlagLogicPacked(), msb1 - lsb1 + 1); + AstVar* const temp2p + = new AstVar(sel2p->fileline(), AstVarType::BLOCKTEMP, + m_concswapNames.get(sel2p), VFlagLogicPacked(), msb2 - lsb2 + 1); m_modp->addStmtp(temp1p); m_modp->addStmtp(temp2p); AstNodeAssign* asn1ap @@ -1927,6 +2172,7 @@ private: VL_RESTORER(m_modp); { m_modp = nodep; + m_concswapNames.reset(); iterateChildren(nodep); } } @@ -3085,7 +3331,7 @@ private: TREEOPV("AstRedOr {$lhsp.castExtend}", "AstRedOr {$lhsp->castExtend()->lhsp()}"); TREEOPV("AstRedXor{$lhsp.castExtend}", "AstRedXor{$lhsp->castExtend()->lhsp()}"); TREEOP ("AstRedXor{$lhsp.castXor, VN_IS(VN_CAST($lhsp,,Xor)->lhsp(),,Const)}", "AstXor{AstRedXor{$lhsp->castXor()->lhsp()}, AstRedXor{$lhsp->castXor()->rhsp()}}"); // ^(const ^ a) => (^const)^(^a) - TREEOPC("AstAnd {nodep->widthMin() == 1, $lhsp.castConst, $rhsp.castRedXor, matchBitOpTree(nodep)}", "DONE"); + TREEOPC("AstAnd {$lhsp.castConst, $rhsp.castRedXor, matchBitOpTree(nodep)}", "DONE"); TREEOPV("AstOneHot{$lhsp.width1}", "replaceWLhs(nodep)"); TREEOPV("AstOneHot0{$lhsp.width1}", "replaceNum(nodep,1)"); // Binary AND/OR is faster than logical and/or (usually) @@ -3109,9 +3355,9 @@ private: TREEOP ("AstAnd {operandShiftSame(nodep)}", "replaceShiftSame(nodep)"); TREEOP ("AstOr {operandShiftSame(nodep)}", "replaceShiftSame(nodep)"); TREEOP ("AstXor {operandShiftSame(nodep)}", "replaceShiftSame(nodep)"); - TREEOPC("AstAnd {nodep->widthMin() == 1, matchBitOpTree(nodep)}", "DONE"); - TREEOPC("AstOr {nodep->widthMin() == 1, matchBitOpTree(nodep)}", "DONE"); - TREEOPC("AstXor {nodep->widthMin() == 1, matchBitOpTree(nodep)}", "DONE"); + TREEOPC("AstAnd {matchBitOpTree(nodep)}", "DONE"); + TREEOPC("AstOr {matchBitOpTree(nodep)}", "DONE"); + TREEOPC("AstXor {matchBitOpTree(nodep)}", "DONE"); // Note can't simplify a extend{extends}, extends{extend}, as the sign // bits end up in the wrong places TREEOPV("AstExtend {$lhsp.castExtend}", "replaceExtend(nodep, VN_CAST(nodep->lhsp(), Extend)->lhsp())"); @@ -3193,7 +3439,9 @@ public: }; // CONSTRUCTORS - explicit ConstVisitor(ProcMode pmode) { + ConstVisitor(ProcMode pmode, bool globalPass) + : m_globalPass{globalPass} + , m_concswapNames{globalPass ? ("__Vconcswap_" + cvtToStr(s_globalPassNum++)) : ""} { // clang-format off switch (pmode) { case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true; @@ -3221,6 +3469,8 @@ public: } }; +uint32_t ConstVisitor::s_globalPassNum = 0; + //###################################################################### // Const class functions @@ -3233,7 +3483,7 @@ AstNode* V3Const::constifyParamsEdit(AstNode* nodep) { // Make sure we've sized everything first nodep = V3Width::widthParamsEdit(nodep); - ConstVisitor visitor(ConstVisitor::PROC_PARAMS); + ConstVisitor visitor{ConstVisitor::PROC_PARAMS, /* globalPass: */ false}; if (AstVar* varp = VN_CAST(nodep, Var)) { // If a var wants to be constified, it's really a param, and // we want the value to be constant. We aren't passed just the @@ -3263,7 +3513,7 @@ AstNode* V3Const::constifyGenerateParamsEdit(AstNode* nodep) { // Make sure we've sized everything first nodep = V3Width::widthGenerateParamsEdit(nodep); - ConstVisitor visitor(ConstVisitor::PROC_GENERATE); + ConstVisitor visitor{ConstVisitor::PROC_GENERATE, /* globalPass: */ false}; if (AstVar* varp = VN_CAST(nodep, Var)) { // If a var wants to be constified, it's really a param, and // we want the value to be constant. We aren't passed just the @@ -3281,7 +3531,7 @@ void V3Const::constifyAllLint(AstNetlist* nodep) { // Only call from Verilator.cpp, as it uses user#'s UINFO(2, __FUNCTION__ << ": " << endl); { - ConstVisitor visitor(ConstVisitor::PROC_V_WARN); + ConstVisitor visitor{ConstVisitor::PROC_V_WARN, /* globalPass: */ true}; (void)visitor.mainAcceptEdit(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); @@ -3290,14 +3540,14 @@ void V3Const::constifyAllLint(AstNetlist* nodep) { void V3Const::constifyCpp(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { - ConstVisitor visitor(ConstVisitor::PROC_CPP); + ConstVisitor visitor{ConstVisitor::PROC_CPP, /* globalPass: */ true}; (void)visitor.mainAcceptEdit(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("const_cpp", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } AstNode* V3Const::constifyEdit(AstNode* nodep) { - ConstVisitor visitor(ConstVisitor::PROC_V_NOWARN); + ConstVisitor visitor{ConstVisitor::PROC_V_NOWARN, /* globalPass: */ false}; nodep = visitor.mainAcceptEdit(nodep); return nodep; } @@ -3308,7 +3558,7 @@ void V3Const::constifyAllLive(AstNetlist* nodep) { // IE doesn't prune dead statements, as we need to do some usability checks after this UINFO(2, __FUNCTION__ << ": " << endl); { - ConstVisitor visitor(ConstVisitor::PROC_LIVE); + ConstVisitor visitor{ConstVisitor::PROC_LIVE, /* globalPass: */ true}; (void)visitor.mainAcceptEdit(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); @@ -3318,14 +3568,14 @@ void V3Const::constifyAll(AstNetlist* nodep) { // Only call from Verilator.cpp, as it uses user#'s UINFO(2, __FUNCTION__ << ": " << endl); { - ConstVisitor visitor(ConstVisitor::PROC_V_EXPENSIVE); + ConstVisitor visitor{ConstVisitor::PROC_V_EXPENSIVE, /* globalPass: */ true}; (void)visitor.mainAcceptEdit(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } AstNode* V3Const::constifyExpensiveEdit(AstNode* nodep) { - ConstVisitor visitor(ConstVisitor::PROC_V_EXPENSIVE); + ConstVisitor visitor{ConstVisitor::PROC_V_EXPENSIVE, /* globalPass: */ false}; nodep = visitor.mainAcceptEdit(nodep); return nodep; } diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index 2f9524df8..76dd38fd8 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -543,6 +543,6 @@ public: void V3Coverage::coverage(AstNetlist* rootp) { UINFO(2, __FUNCTION__ << ": " << endl); - { CoverageVisitor visitor(rootp); } // Destruct before checking + { CoverageVisitor visitor{rootp}; } // Destruct before checking V3Global::dumpCheckGlobalTree("coverage", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3CoverageJoin.cpp b/src/V3CoverageJoin.cpp index f0f30c098..8ceb3ccd5 100644 --- a/src/V3CoverageJoin.cpp +++ b/src/V3CoverageJoin.cpp @@ -114,6 +114,6 @@ public: void V3CoverageJoin::coverageJoin(AstNetlist* rootp) { UINFO(2, __FUNCTION__ << ": " << endl); - { CoverageJoinVisitor visitor(rootp); } // Destruct before checking + { CoverageJoinVisitor visitor{rootp}; } // Destruct before checking V3Global::dumpCheckGlobalTree("coveragejoin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index b08c5e78c..dafc1110c 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -318,7 +318,7 @@ private: // And its children may now be killable too; correct counts // Recurse, as cells may not be directly under the module but in a generate if (!modp->dead()) { // If was dead didn't increment user1's - DeadModVisitor visitor(modp); + DeadModVisitor visitor{modp}; } VL_DO_DANGLING(modp->unlinkFrBack()->deleteTree(), modp); retry = true; @@ -463,31 +463,31 @@ public: void V3Dead::deadifyModules(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DeadVisitor visitor(nodep, false, false, false, false); } // Destruct before checking + { DeadVisitor visitor{nodep, false, false, false, false}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deadModules", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } void V3Dead::deadifyDTypes(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DeadVisitor visitor(nodep, false, true, false, false); } // Destruct before checking + { DeadVisitor visitor{nodep, false, true, false, false}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deadDtypes", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DeadVisitor visitor(nodep, false, true, true, false); } // Destruct before checking + { DeadVisitor visitor{nodep, false, true, true, false}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } void V3Dead::deadifyAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DeadVisitor visitor(nodep, true, true, false, true); } // Destruct before checking + { DeadVisitor visitor{nodep, true, true, false, true}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deadAll", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } void V3Dead::deadifyAllScoped(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DeadVisitor visitor(nodep, true, true, true, true); } // Destruct before checking + { DeadVisitor visitor{nodep, true, true, true, true}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deadAllScoped", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index 3e3cd280f..7b4c0f88b 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -510,6 +510,6 @@ public: void V3Delayed::delayedAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DelayedVisitor visitor(nodep); } // Destruct before checking + { DelayedVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("delayed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Depth.cpp b/src/V3Depth.cpp index 9aef1f3b6..72fc81d6d 100644 --- a/src/V3Depth.cpp +++ b/src/V3Depth.cpp @@ -29,6 +29,7 @@ #include "V3Global.h" #include "V3Depth.h" #include "V3Ast.h" +#include "V3UniqueNames.h" #include @@ -39,11 +40,11 @@ private: // NODE STATE // STATE - AstNodeModule* m_modp = nullptr; // Current module AstCFunc* m_cfuncp = nullptr; // Current block AstNode* m_stmtp = nullptr; // Current statement int m_depth = 0; // How deep in an expression int m_maxdepth = 0; // Maximum depth in an expression + V3UniqueNames m_tempNames; // For generating unique temporary variable names // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -51,18 +52,16 @@ private: void createDeepTemp(AstNode* nodep) { UINFO(6, " Deep " << nodep << endl); // if (debug() >= 9) nodep->dumpTree(cout, "deep:"); - - const string newvarname = (string("__Vdeeptemp") + cvtToStr(m_modp->varNumGetInc())); - AstVar* varp - = new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname, nodep->dtypep()); + AstVar* const varp = new AstVar{nodep->fileline(), AstVarType::STMTTEMP, + m_tempNames.get(nodep), nodep->dtypep()}; UASSERT_OBJ(m_cfuncp, nodep, "Deep expression not under a function"); m_cfuncp->addInitsp(varp); // Replace node tree with reference to var - AstVarRef* newp = new AstVarRef(nodep->fileline(), varp, VAccess::READ); + AstVarRef* const newp = new AstVarRef{nodep->fileline(), varp, VAccess::READ}; nodep->replaceWith(newp); // Put assignment before the referencing statement - AstAssign* assp = new AstAssign( - nodep->fileline(), new AstVarRef(nodep->fileline(), varp, VAccess::WRITE), nodep); + AstAssign* const assp = new AstAssign{ + nodep->fileline(), new AstVarRef{nodep->fileline(), varp, VAccess::WRITE}, nodep}; AstNRelinker linker2; m_stmtp->unlinkFrBack(&linker2); assp->addNext(m_stmtp); @@ -70,21 +69,13 @@ private: } // VISITORS - virtual void visit(AstNodeModule* nodep) override { - UINFO(4, " MOD " << nodep << endl); - VL_RESTORER(m_modp); - { - m_modp = nodep; - m_cfuncp = nullptr; - iterateChildren(nodep); - } - } virtual void visit(AstCFunc* nodep) override { VL_RESTORER(m_cfuncp); { m_cfuncp = nodep; m_depth = 0; m_maxdepth = 0; + m_tempNames.reset(); iterateChildren(nodep); } } @@ -149,7 +140,10 @@ private: public: // CONSTRUCTORS - explicit DepthVisitor(AstNetlist* nodep) { iterate(nodep); } + explicit DepthVisitor(AstNetlist* nodep) + : m_tempNames{"__Vdeeptemp"} { + iterate(nodep); + } virtual ~DepthVisitor() override = default; }; @@ -158,6 +152,6 @@ public: void V3Depth::depthAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DepthVisitor visitor(nodep); } // Destruct before checking + { DepthVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("depth", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3DepthBlock.cpp b/src/V3DepthBlock.cpp index 54a131a75..5fa2c3804 100644 --- a/src/V3DepthBlock.cpp +++ b/src/V3DepthBlock.cpp @@ -129,6 +129,6 @@ public: void V3DepthBlock::depthBlockAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DepthBlockVisitor visitor(nodep); } // Destruct before checking + { DepthBlockVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deepblock", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Descope.cpp b/src/V3Descope.cpp index 9b2e2cf76..8ab4dccf5 100644 --- a/src/V3Descope.cpp +++ b/src/V3Descope.cpp @@ -73,6 +73,7 @@ private: // module. string descopedSelfPointer(const AstScope* scopep) { UASSERT(scopep, "Var/Func not scoped"); + UASSERT(!VN_IS(scopep->modp(), Class), "References to classes handled elsewhere"); // Static functions can't use relative references via 'this->' const bool relativeRefOk = !m_funcp->isStatic(); @@ -83,9 +84,7 @@ private: if (relativeRefOk && scopep == m_scopep) { return "this"; - } else if (VN_IS(scopep->modp(), Class)) { - return "this"; - } else if (!m_modSingleton && relativeRefOk && scopep->aboveScopep() == m_scopep + } else if (relativeRefOk && !m_modSingleton && scopep->aboveScopep() == m_scopep && VN_IS(scopep->modp(), Module)) { // Reference to scope of instance directly under this module, can just "this->cell", // which can potentially be V3Combined, but note this requires one extra pointer @@ -183,6 +182,10 @@ private: } // VISITORS + virtual void visit(AstNetlist* nodep) override { + nodep->dpiExportTriggerp(nullptr); + iterateChildren(nodep); + } virtual void visit(AstNodeModule* nodep) override { VL_RESTORER(m_modp); { @@ -215,20 +218,40 @@ private: UASSERT_OBJ(m_scopep, nodep, "Node not under scope"); const AstVar* const varp = nodep->varScopep()->varp(); const AstScope* const scopep = nodep->varScopep()->scopep(); - if (!varp->isFuncLocal()) { nodep->selfPointer(descopedSelfPointer(scopep)); } + if (varp->isFuncLocal()) { + // Reference to function locals need no self pointer + nodep->selfPointer(""); + } else if (scopep->modp() == v3Global.rootp()->constPoolp()->modp()) { + // Reference to constant pool value need no self pointer + nodep->selfPointer(""); + } else if (VN_IS(scopep->modp(), Class)) { + // Direct reference to class members are from within the class itself, references from + // outside the class must go via AstMemberSel + nodep->selfPointer("this"); + } else { + nodep->selfPointer(descopedSelfPointer(scopep)); + } nodep->varScopep(nullptr); UINFO(9, " refout " << nodep << endl); } - virtual void visit(AstNodeCCall* nodep) override { + virtual void visit(AstCCall* nodep) override { // UINFO(9, " " << nodep << endl); iterateChildren(nodep); // Convert the hierch name UASSERT_OBJ(m_scopep, nodep, "Node not under scope"); const AstScope* const scopep = nodep->funcp()->scopep(); - nodep->selfPointer(descopedSelfPointer(scopep)); + if (VN_IS(scopep->modp(), Class)) { + // Direct call to class methods are from within the class itself, method calls from + // outside the class must go via AstCMethodCall + nodep->selfPointer("this"); + } else { + nodep->selfPointer(descopedSelfPointer(scopep)); + } // Can't do this, as we may have more calls later // nodep->funcp()->scopep(nullptr); } + virtual void visit(AstCMethodCall* nodep) override { iterateChildren(nodep); } + virtual void visit(AstCNew* nodep) override { iterateChildren(nodep); } virtual void visit(AstCFunc* nodep) override { VL_RESTORER(m_funcp); if (!nodep->user1()) { @@ -266,6 +289,6 @@ public: void V3Descope::descopeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DescopeVisitor visitor(nodep); } // Destruct before checking + { DescopeVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("descope", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3DupFinder.cpp b/src/V3DupFinder.cpp index 061e659db..9b8187f1a 100644 --- a/src/V3DupFinder.cpp +++ b/src/V3DupFinder.cpp @@ -44,7 +44,7 @@ V3DupFinder::iterator V3DupFinder::findDuplicate(AstNode* nodep, V3DupFinderUser } void V3DupFinder::dumpFile(const string& filename, bool tree) { - const std::unique_ptr logp(V3File::new_ofstream(filename)); + const std::unique_ptr logp{V3File::new_ofstream(filename)}; if (logp->fail()) v3fatal("Can't write " << filename); std::unordered_map dist; diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp deleted file mode 100644 index fc8cb08fa..000000000 --- a/src/V3EmitC.cpp +++ /dev/null @@ -1,1133 +0,0 @@ -// -*- mode: C++; c-file-style: "cc-mode" -*- -//************************************************************************* -// DESCRIPTION: Verilator: Emit C++ for tree -// -// Code available from: https://verilator.org -// -//************************************************************************* -// -// Copyright 2003-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 -// -//************************************************************************* - -#include "config_build.h" -#include "verilatedos.h" - -#include "V3Global.h" -#include "V3EmitC.h" -#include "V3EmitCFunc.h" - -#include -#include - -//###################################################################### -// Internal EmitC implementation - -class EmitCImp final : EmitCFunc { - // MEMBERS - AstNodeModule* m_fileModp = nullptr; // Files (names, headers) constructed using this module - bool m_slow = false; // Creating __Slow file - bool m_fast = false; // Creating non __Slow file (or both) - - //--------------------------------------- - // METHODS - - V3OutCFile* newOutCFile(bool slow, bool source, int filenum = 0) { - m_lazyDecls.reset(); // Need to emit new lazy declarations - - string filenameNoExt = v3Global.opt.makeDir() + "/" + prefixNameProtect(m_fileModp); - if (filenum) filenameNoExt += "__" + cvtToStr(filenum); - filenameNoExt += (slow ? "__Slow" : ""); - V3OutCFile* ofp = nullptr; - if (v3Global.opt.lintOnly()) { - // Unfortunately we have some lint checks here, so we can't just skip processing. - // We should move them to a different stage. - const string filename = VL_DEV_NULL; - newCFile(filename, slow, source); - ofp = new V3OutCFile(filename); - } else if (optSystemC()) { - const string filename = filenameNoExt + (source ? ".cpp" : ".h"); - newCFile(filename, slow, source); - ofp = new V3OutScFile(filename); - } else { - const string filename = filenameNoExt + (source ? ".cpp" : ".h"); - newCFile(filename, slow, source); - ofp = new V3OutCFile(filename); - } - - ofp->putsHeader(); - if (source) { - ofp->puts("// DESCRIPTION: Verilator output: Design implementation internals\n"); - } else { - ofp->puts("// DESCRIPTION: Verilator output: Design internal header\n"); - } - ofp->puts("// See " + topClassName() + ".h for the primary calling header\n"); - return ofp; - } - - //--------------------------------------- - // VISITORS - using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning - virtual void visit(AstCFunc* nodep) override { - // TRACE_* and DPI handled elsewhere - if (nodep->funcType().isTrace()) return; - if (nodep->dpiImportPrototype()) return; - if (nodep->dpiExportDispatcher()) return; - if (!(nodep->slow() ? m_slow : m_fast)) return; - - maybeSplit(); - - EmitCFunc::visit(nodep); - } - - //--------------------------------------- - // ACCESSORS - - // METHODS - // Low level - void emitTypedefs(AstNode* firstp) { - bool first = true; - for (AstNode* loopp = firstp; loopp; loopp = loopp->nextp()) { - if (const AstTypedef* nodep = VN_CAST(loopp, Typedef)) { - if (nodep->attrPublic()) { - if (first) { - first = false; - puts("\n// TYPEDEFS\n"); - puts("// That were declared public\n"); - } else { - puts("\n"); - } - if (const AstEnumDType* adtypep - = VN_CAST(nodep->dtypep()->skipRefToEnump(), EnumDType)) { - if (adtypep->width() > 64) { - putsDecoration("// enum " + nodep->nameProtect() - + " // Ignored: Too wide for C++\n"); - } else { - puts("enum " + nodep->name() + " {\n"); - for (AstEnumItem* itemp = adtypep->itemsp(); itemp; - itemp = VN_CAST(itemp->nextp(), EnumItem)) { - puts(itemp->nameProtect()); - puts(" = "); - iterateAndNextNull(itemp->valuep()); - if (VN_IS(itemp->nextp(), EnumItem)) puts(","); - puts("\n"); - } - puts("};\n"); - } - } - } - } - } - } - void emitParams(AstNodeModule* modp, bool init, bool* firstp, string& sectionr) { - bool anyi = false; - for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (const AstVar* varp = VN_CAST(nodep, Var)) { - if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) { - if (!init && sectionr != "") { - puts(sectionr); - sectionr = ""; - } - UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?"); - // These should be static const values, however older MSVC++ did't - // support them; should be ok now under C++11, need to refactor. - if (varp->isWide()) { // Unsupported for output - if (!init) { - putsDecoration("// enum WData " + varp->nameProtect() + " //wide"); - } - } else if (varp->isString()) { - if (init) { - puts("const std::string "); - puts(prefixNameProtect(modp) + "::" + protect("var_" + varp->name()) - + "("); - iterateAndNextNull(varp->valuep()); - puts(");\n"); - anyi = true; - } else { - puts("static const std::string " + protect("var_" + varp->name()) - + ";\n"); - } - } else if (!VN_IS(varp->valuep(), Const)) { // Unsupported for output - // putsDecoration("// enum ..... "+varp->nameProtect() - // +"not simple value, see variable above instead"); - } else if (VN_IS(varp->dtypep(), BasicDType) - && VN_CAST(varp->dtypep(), BasicDType) - ->isOpaque()) { // Can't put out e.g. doubles - } else { - if (init) { - puts(varp->isQuad() ? "const QData " : "const IData "); - puts(prefixNameProtect(modp) + "::" + protect("var_" + varp->name()) - + "("); - iterateAndNextNull(varp->valuep()); - puts(");\n"); - anyi = true; - } else { - // enum - puts(varp->isQuad() ? "enum _QData" : "enum _IData"); - puts("" + varp->nameProtect() + " { " + varp->nameProtect() + " = "); - iterateAndNextNull(varp->valuep()); - puts("};\n"); - // var - puts(varp->isQuad() ? "static const QData " : "static const IData "); - puts(protect("var_" + varp->name()) + ";\n"); - } - } - } - } - } - if (anyi) puts("\n"); - } - void emitIntFuncDecls(AstNodeModule* modp, bool inClassBody) { - std::vector funcsp; - - for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (const AstCFunc* funcp = VN_CAST(nodep, CFunc)) { - if (funcp->dpiImportPrototype()) // Declared in __Dpi.h - continue; - if (funcp->dpiExportDispatcher()) // Declared in __Dpi.h - continue; - if (funcp->isMethod() != inClassBody) // Only methods go inside class - continue; - if (funcp->isMethod() && funcp->isLoose()) // Loose methods are declared lazily - continue; - funcsp.push_back(funcp); - } - } - - stable_sort(funcsp.begin(), funcsp.end(), [](const AstNode* ap, const AstNode* bp) { // - return ap->name() < bp->name(); - }); - - for (const AstCFunc* funcp : funcsp) { - if (inClassBody) ofp()->putsPrivate(funcp->declPrivate()); - emitCFuncDecl(funcp, modp); - } - } - - // Medium level - void emitCtorImp(AstNodeModule* modp); - void emitConfigureImp(AstNodeModule* modp); - void emitCoverageDecl(AstNodeModule* modp); - void emitCoverageImp(AstNodeModule* modp); - void emitDestructorImp(AstNodeModule* modp); - void emitSavableImp(AstNodeModule* modp); - void emitTextSection(AstType type); - // High level - void emitImpTop(); - void emitImp(AstNodeModule* modp); - void emitIntTop(const AstNodeModule* modp); - void emitInt(AstNodeModule* modp); - void maybeSplit(); - -public: - EmitCImp() {} - virtual ~EmitCImp() override = default; - void mainImp(AstNodeModule* modp, bool slow); - void mainInt(AstNodeModule* modp); - void mainDoFunc(AstCFunc* nodep) { iterate(nodep); } -}; - -//###################################################################### -// Internal EmitC - -void EmitCImp::emitCoverageDecl(AstNodeModule*) { - if (v3Global.opt.coverage()) { - ofp()->putsPrivate(false); // Accessed from loose methods - putsDecoration("// Coverage\n"); - puts("void __vlCoverInsert("); - puts(v3Global.opt.threads() ? "std::atomic" : "uint32_t"); - puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); - puts("const char* hierp, const char* pagep, const char* commentp, const char* " - "linescovp);\n"); - } -} - -void EmitCImp::emitCtorImp(AstNodeModule* modp) { - puts("\n"); - bool first = true; - string section; - emitParams(modp, true, &first, section /*ref*/); - - const string modName = prefixNameProtect(modp); - - puts("\n"); - m_lazyDecls.emit("void " + modName + "__", protect("_ctor_var_reset"), - "(" + modName + "* vlSelf);"); - puts("\n"); - - if (VN_IS(modp, Class)) { - modp->v3fatalSrc("constructors should be AstCFuncs instead"); - } else { - puts(modName + "::" + modName + "(const char* _vcname__)\n"); - puts(" : VerilatedModule(_vcname__)\n"); - first = false; // printed the first ':' - } - emitVarCtors(&first); - - puts(" {\n"); - - putsDecoration("// Reset structure values\n"); - puts(modName + "__" + protect("_ctor_var_reset") + "(this);\n"); - emitTextSection(AstType::atScCtor); - - puts("}\n"); -} - -void EmitCImp::emitConfigureImp(AstNodeModule* modp) { - const string modName = prefixNameProtect(modp); - - if (v3Global.opt.coverage()) { - puts("\n"); - m_lazyDecls.emit("void " + modName + "__", protect("_configure_coverage"), - "(" + modName + "* vlSelf, bool first);"); - } - - puts("\nvoid " + modName + "::" + protect("__Vconfigure") + "(" + symClassName() - + "* _vlSymsp, bool first) {\n"); - puts("if (false && first) {} // Prevent unused\n"); - puts("this->vlSymsp = _vlSymsp;\n"); // First, as later stuff needs it. - if (v3Global.opt.coverage()) { - puts(modName + "__" + protect("_configure_coverage") + "(this, first);\n"); - } - puts("}\n"); - splitSizeInc(10); -} - -void EmitCImp::emitCoverageImp(AstNodeModule*) { - if (v3Global.opt.coverage()) { - puts("\n// Coverage\n"); - // Rather than putting out VL_COVER_INSERT calls directly, we do it via this function - // This gets around gcc slowness constructing all of the template arguments. - puts("void " + prefixNameProtect(m_modp) + "::__vlCoverInsert("); - puts(v3Global.opt.threads() ? "std::atomic" : "uint32_t"); - puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); - puts("const char* hierp, const char* pagep, const char* commentp, const char* linescovp) " - "{\n"); - if (v3Global.opt.threads()) { - puts("assert(sizeof(uint32_t) == sizeof(std::atomic));\n"); - puts("uint32_t* count32p = reinterpret_cast(countp);\n"); - } else { - puts("uint32_t* count32p = countp;\n"); - } - // static doesn't need save-restore as is constant - puts("static uint32_t fake_zero_count = 0;\n"); - // Used for second++ instantiation of identical bin - puts("if (!enable) count32p = &fake_zero_count;\n"); - puts("*count32p = 0;\n"); - puts("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), count32p,"); - puts(" \"filename\",filenamep,"); - puts(" \"lineno\",lineno,"); - puts(" \"column\",column,\n"); - // Need to move hier into scopes and back out if do this - // puts( "\"hier\",std::string(vlSymsp->name())+hierp,"); - puts("\"hier\",std::string(name())+hierp,"); - puts(" \"page\",pagep,"); - puts(" \"comment\",commentp,"); - puts(" (linescovp[0] ? \"linescov\" : \"\"), linescovp);\n"); - puts("}\n"); - splitSizeInc(10); - } -} - -void EmitCImp::emitDestructorImp(AstNodeModule* modp) { - puts("\n"); - puts(prefixNameProtect(modp) + "::~" + prefixNameProtect(modp) + "() {\n"); - emitTextSection(AstType::atScDtor); - puts("}\n"); - splitSizeInc(10); -} - -void EmitCImp::emitSavableImp(AstNodeModule* modp) { - if (v3Global.opt.savable()) { - puts("\n// Savable\n"); - for (int de = 0; de < 2; ++de) { - const string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; - const string funcname = de ? "__Vdeserialize" : "__Vserialize"; - const string op = de ? ">>" : "<<"; - // NOLINTNEXTLINE(performance-inefficient-string-concatenation) - puts("void " + prefixNameProtect(modp) + "::" + protect(funcname) + "(" + classname - + "& os) {\n"); - // Place a computed checksum to ensure proper structure save/restore formatting - // OK if this hash includes some things we won't dump, since - // just looking for loading the wrong model - VHashSha256 hash; - for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (const AstVar* varp = VN_CAST(nodep, Var)) { - hash.insert(varp->name()); - hash.insert(varp->dtypep()->width()); - } - } - ofp()->printf("vluint64_t __Vcheckval = 0x%" VL_PRI64 "xULL;\n", - static_cast(hash.digestUInt64())); - if (de) { - puts("os.readAssert(__Vcheckval);\n"); - } else { - 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 - for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (const AstVar* varp = VN_CAST(nodep, Var)) { - if (varp->isIO() && modp->isTop() && optSystemC()) { - // System C top I/O doesn't need loading, as the - // lower level subinst code does it. - } else if (varp->isParam()) { - } else if (varp->isStatic() && varp->isConst()) { - } else { - int vects = 0; - AstNodeDType* elementp = varp->dtypeSkipRefp(); - for (AstUnpackArrayDType* arrayp = VN_CAST(elementp, UnpackArrayDType); - arrayp; arrayp = VN_CAST(elementp, UnpackArrayDType)) { - const int vecnum = vects++; - UASSERT_OBJ(arrayp->hi() >= arrayp->lo(), varp, - "Should have swapped msb & lsb earlier."); - const string ivar = string("__Vi") + cvtToStr(vecnum); - puts("for (int __Vi" + cvtToStr(vecnum) + "=" + cvtToStr(0)); - puts("; " + ivar + "<" + cvtToStr(arrayp->elementsConst())); - puts("; ++" + ivar + ") {\n"); - elementp = arrayp->subDTypep()->skipRefp(); - } - const AstBasicDType* const basicp = elementp->basicp(); - // Do not save MTask state, only matters within an evaluation - if (basicp && basicp->keyword().isMTaskState()) continue; - // Want to detect types that are represented as arrays - // (i.e. packed types of more than 64 bits). - if (elementp->isWide() - && !(basicp && basicp->keyword() == AstBasicDTypeKwd::STRING)) { - const int vecnum = vects++; - const string ivar = string("__Vi") + cvtToStr(vecnum); - puts("for (int __Vi" + cvtToStr(vecnum) + "=" + cvtToStr(0)); - puts("; " + ivar + "<" + cvtToStr(elementp->widthWords())); - puts("; ++" + ivar + ") {\n"); - } - puts("os" + op + varp->nameProtect()); - for (int v = 0; v < vects; ++v) puts("[__Vi" + cvtToStr(v) + "]"); - puts(";\n"); - for (int v = 0; v < vects; ++v) puts("}\n"); - } - } - } - - puts("}\n"); - } - } -} - -void EmitCImp::emitTextSection(AstType type) { - int last_line = -999; - for (AstNode* nodep = m_modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (const AstNodeText* textp = VN_CAST(nodep, NodeText)) { - if (nodep->type() == type) { - if (last_line != nodep->fileline()->lineno()) { - if (last_line < 0) { - puts("\n//*** Below code from `systemc in Verilog file\n"); - } - putsDecoration( - ifNoProtect("// From `systemc at " + nodep->fileline()->ascii() + "\n")); - last_line = nodep->fileline()->lineno(); - } - ofp()->putsNoTracking(textp->text()); - last_line++; - } - } - } - if (last_line > 0) puts("//*** Above code from `systemc in Verilog file\n\n"); -} - -void EmitCImp::emitIntTop(const AstNodeModule* modp) { - // Always have this first; gcc has short circuiting if #ifdef is first in a file - ofp()->putsGuard(); - puts("\n"); - - ofp()->putsIntTopInclude(); - puts("#include \"verilated_heavy.h\"\n"); - if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n"); - if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n"); - if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n"); -} - -void EmitCImp::emitInt(AstNodeModule* modp) { - puts("\n//==========\n\n"); - - if (AstClass* classp = VN_CAST(modp, Class)) { - if (classp->extendsp()) - puts("#include \"" + prefixNameProtect(classp->extendsp()->classp()->classOrPackagep()) - + ".h\"\n"); - } - - emitModCUse(modp, VUseType::INT_INCLUDE); - - // Declare foreign instances up front to make C++ happy - puts("class " + symClassName() + ";\n"); - emitModCUse(modp, VUseType::INT_FWD_CLASS); - - puts("\n//----------\n\n"); - emitTextSection(AstType::atScHdr); - - if (AstClass* classp = VN_CAST(modp, Class)) { - puts("class " + prefixNameProtect(modp)); - if (classp->extendsp()) - puts(" : public " + prefixNameProtect(classp->extendsp()->classp())); - puts(" {\n"); - } else { - puts("VL_MODULE(" + prefixNameProtect(modp) + ") {\n"); - } - ofp()->resetPrivate(); - ofp()->putsPrivate(false); // public: - - { // Instantiated cells - bool did = false; - for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (AstCell* cellp = VN_CAST(nodep, Cell)) { - if (!did) { - did = true; - putsDecoration("// CELLS\n"); - } - puts(prefixNameProtect(cellp->modp()) + "* " + cellp->nameProtect() + ";\n"); - } - } - } - - emitTypedefs(modp->stmtsp()); - - string section; - section = "\n// PORTS\n"; - emitVarList(modp->stmtsp(), EVL_CLASS_IO, "", section /*ref*/); - - section = "\n// LOCAL SIGNALS\n"; - emitVarList(modp->stmtsp(), EVL_CLASS_SIG, "", section /*ref*/); - - section = "\n// LOCAL VARIABLES\n"; - emitVarList(modp->stmtsp(), EVL_CLASS_TEMP, "", section /*ref*/); - - puts("\n// INTERNAL VARIABLES\n"); - if (!VN_IS(modp, Class)) { // Avoid clang unused error (& don't want in every object) - ofp()->putsPrivate(false); // public: so loose methods can pick it up - puts(symClassName() + "* vlSymsp; // Symbol table\n"); - } - ofp()->putsPrivate(false); // public: - emitCoverageDecl(modp); // may flip public/private - - section = "\n// PARAMETERS\n"; - ofp()->putsPrivate(false); // public: - emitVarList(modp->stmtsp(), EVL_CLASS_PAR, "", - section /*ref*/); // Only those that are non-CONST - bool first = true; - emitParams(modp, false, &first, section /*ref*/); - - if (!VN_IS(modp, Class)) { - puts("\n// CONSTRUCTORS\n"); - ofp()->resetPrivate(); - // We don't need a private copy constructor, as VerilatedModule has one for us. - ofp()->putsPrivate(true); - puts("VL_UNCOPYABLE(" + prefixNameProtect(modp) + "); ///< Copying not allowed\n"); - } - - if (VN_IS(modp, Class)) { - // CFuncs with isConstructor/isDestructor used instead - } else { - ofp()->putsPrivate(false); // public: - puts(prefixNameProtect(modp) + "(const char* name);\n"); - puts("~" + prefixNameProtect(modp) + "();\n"); - } - - emitTextSection(AstType::atScInt); - - puts("\n// INTERNAL METHODS\n"); - - if (!VN_IS(modp, Class)) { - ofp()->putsPrivate(false); // public: - puts("void " + protect("__Vconfigure") + "(" + symClassName() + "* symsp, bool first);\n"); - } - - ofp()->putsPrivate(false); // public: - emitIntFuncDecls(modp, true); - - if (v3Global.opt.savable()) { - ofp()->putsPrivate(false); // public: - puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n"); - puts("void " + protect("__Vdeserialize") + "(VerilatedDeserialize& os);\n"); - } - - puts("}"); - if (!VN_IS(modp, Class)) puts(" VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES)"); - puts(";\n"); - - puts("\n//----------\n\n"); - emitIntFuncDecls(modp, false); -} - -//---------------------------------------------------------------------- - -void EmitCImp::emitImpTop() { - puts("\n"); - puts("#include \"" + prefixNameProtect(m_fileModp) + ".h\"\n"); - puts("#include \"" + symClassName() + ".h\"\n"); - - if (v3Global.dpi()) { - puts("\n"); - puts("#include \"verilated_dpi.h\"\n"); - } - - emitModCUse(m_fileModp, VUseType::IMP_INCLUDE); - emitModCUse(m_fileModp, VUseType::IMP_FWD_CLASS); - - emitTextSection(AstType::atScImpHdr); -} - -void EmitCImp::emitImp(AstNodeModule* modp) { - puts("\n//==========\n"); - if (m_slow) { - string section; - emitVarList(modp->stmtsp(), EVL_CLASS_ALL, prefixNameProtect(modp), section /*ref*/); - if (!VN_IS(modp, Class)) emitCtorImp(modp); - if (!VN_IS(modp, Class)) emitConfigureImp(modp); - if (!VN_IS(modp, Class)) emitDestructorImp(modp); - emitSavableImp(modp); - emitCoverageImp(modp); - } - - if (m_fast) { emitTextSection(AstType::atScImp); } - - // Blocks - for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (AstCFunc* funcp = VN_CAST(nodep, CFunc)) { mainDoFunc(funcp); } - } -} - -//###################################################################### - -void EmitCImp::maybeSplit() { - if (!splitNeeded()) return; - - // Splitting file, so using parallel build. - v3Global.useParallelBuild(true); - // Close old file - VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); - // Open a new file - m_ofp = newOutCFile(!m_fast, true /*source*/, splitFilenumInc()); - emitImpTop(); -} - -void EmitCImp::mainInt(AstNodeModule* modp) { - m_modp = modp; - m_fileModp = modp; - m_slow = true; - m_fast = true; - - UINFO(5, " Emitting " << prefixNameProtect(modp) << endl); - - m_ofp = newOutCFile(false /*slow*/, false /*source*/); - emitIntTop(modp); - emitInt(modp); - if (AstClassPackage* packagep = VN_CAST(modp, ClassPackage)) { - // Put the non-static class implementation in same h file for speed - m_modp = packagep->classp(); - emitInt(packagep->classp()); - m_modp = modp; - } - ofp()->putsEndGuard(); - VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); -} - -void EmitCImp::mainImp(AstNodeModule* modp, bool slow) { - // Output a module - m_modp = modp; - m_fileModp = modp; - m_slow = slow; - m_fast = !slow; - - UINFO(5, " Emitting " << prefixNameProtect(modp) << endl); - - m_ofp = newOutCFile(!m_fast, true /*source*/); - emitImpTop(); - emitImp(modp); - - if (AstClassPackage* packagep = VN_CAST(modp, ClassPackage)) { - // Put the non-static class implementation in same C++ files as - // often optimizations are possible when both are seen by the - // compiler together - m_modp = packagep->classp(); - emitImp(packagep->classp()); - m_modp = modp; - } - - VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); -} - -//###################################################################### -// Tracing routines - -class EmitCTrace final : EmitCFunc { - // NODE STATE/TYPES - // Cleared on netlist - // AstNode::user1() -> int. Enum number - AstUser1InUse m_inuser1; - - // MEMBERS - AstCFunc* m_cfuncp = nullptr; // Function we're in now - bool m_slow; // Making slow file - int m_enumNum = 0; // Enumeration number (whole netlist) - int m_baseCode = -1; // Code of first AstTraceInc in this function - - // METHODS - void newOutCFile(int filenum) { - m_lazyDecls.reset(); // Need to emit new lazy declarations - - string filename - = (v3Global.opt.makeDir() + "/" + topClassName() + "_" + protect("_Trace")); - if (filenum) filename += "__" + cvtToStr(filenum); - filename += (m_slow ? "__Slow" : ""); - filename += ".cpp"; - - AstCFile* cfilep = newCFile(filename, m_slow, true /*source*/); - cfilep->support(true); - - if (m_ofp) v3fatalSrc("Previous file not closed"); - if (optSystemC()) { - m_ofp = new V3OutScFile(filename); - } else { - m_ofp = new V3OutCFile(filename); - } - m_ofp->putsHeader(); - m_ofp->puts("// DESCR" - "IPTION: Verilator output: Tracing implementation internals\n"); - - emitTraceHeader(); - } - - void emitTraceHeader() { - // Includes - puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n"); - puts("#include \"" + symClassName() + ".h\"\n"); - puts("\n"); - } - - bool emitTraceIsScBv(AstTraceInc* nodep) { - const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef); - if (!varrefp) return false; - AstVar* varp = varrefp->varp(); - return varp->isSc() && varp->isScBv(); - } - - bool emitTraceIsScBigUint(AstTraceInc* nodep) { - const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef); - if (!varrefp) return false; - AstVar* varp = varrefp->varp(); - return varp->isSc() && varp->isScBigUint(); - } - - bool emitTraceIsScUint(AstTraceInc* nodep) { - const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef); - if (!varrefp) return false; - AstVar* varp = varrefp->varp(); - return varp->isSc() && varp->isScUint(); - } - - void emitTraceInitOne(AstTraceDecl* nodep, int enumNum) { - if (nodep->dtypep()->basicp()->isDouble()) { - puts("tracep->declDouble"); - } else if (nodep->isWide()) { - puts("tracep->declArray"); - } else if (nodep->isQuad()) { - puts("tracep->declQuad"); - } else if (nodep->bitRange().ranged()) { - puts("tracep->declBus"); - } else { - puts("tracep->declBit"); - } - - puts("(c+" + cvtToStr(nodep->code())); - if (nodep->arrayRange().ranged()) puts("+i*" + cvtToStr(nodep->widthWords())); - puts(","); - if (nodep->isScoped()) puts("Verilated::catName(scopep,"); - putsQuoted(VIdProtect::protectWordsIf(nodep->showname(), nodep->protect())); - if (nodep->isScoped()) puts(",(int)scopet,\" \")"); - // Direction - if (v3Global.opt.traceFormat().fst()) { - puts("," + cvtToStr(enumNum)); - // fstVarDir - if (nodep->declDirection().isInoutish()) { - puts(",FST_VD_INOUT"); - } else if (nodep->declDirection().isWritable()) { - puts(",FST_VD_OUTPUT"); - } else if (nodep->declDirection().isNonOutput()) { - puts(",FST_VD_INPUT"); - } else { - puts(", FST_VD_IMPLICIT"); - } - // - // fstVarType - const AstVarType vartype = nodep->varType(); - const AstBasicDTypeKwd kwd = nodep->declKwd(); - string fstvt; - // Doubles have special decoding properties, so must indicate if a double - if (nodep->dtypep()->basicp()->isDouble()) { - if (vartype == AstVarType::GPARAM || vartype == AstVarType::LPARAM) { - fstvt = "FST_VT_VCD_REAL_PARAMETER"; - } else { - fstvt = "FST_VT_VCD_REAL"; - } - } - // clang-format off - else if (vartype == AstVarType::GPARAM) { fstvt = "FST_VT_VCD_PARAMETER"; } - else if (vartype == AstVarType::LPARAM) { fstvt = "FST_VT_VCD_PARAMETER"; } - else if (vartype == AstVarType::SUPPLY0) { fstvt = "FST_VT_VCD_SUPPLY0"; } - else if (vartype == AstVarType::SUPPLY1) { fstvt = "FST_VT_VCD_SUPPLY1"; } - else if (vartype == AstVarType::TRI0) { fstvt = "FST_VT_VCD_TRI0"; } - else if (vartype == AstVarType::TRI1) { fstvt = "FST_VT_VCD_TRI1"; } - else if (vartype == AstVarType::TRIWIRE) { fstvt = "FST_VT_VCD_TRI"; } - else if (vartype == AstVarType::WIRE) { fstvt = "FST_VT_VCD_WIRE"; } - else if (vartype == AstVarType::PORT) { fstvt = "FST_VT_VCD_WIRE"; } - // - else if (kwd == AstBasicDTypeKwd::INTEGER) { fstvt = "FST_VT_VCD_INTEGER"; } - else if (kwd == AstBasicDTypeKwd::BIT) { fstvt = "FST_VT_SV_BIT"; } - else if (kwd == AstBasicDTypeKwd::LOGIC) { fstvt = "FST_VT_SV_LOGIC"; } - else if (kwd == AstBasicDTypeKwd::INT) { fstvt = "FST_VT_SV_INT"; } - else if (kwd == AstBasicDTypeKwd::SHORTINT) { fstvt = "FST_VT_SV_SHORTINT"; } - else if (kwd == AstBasicDTypeKwd::LONGINT) { fstvt = "FST_VT_SV_LONGINT"; } - else if (kwd == AstBasicDTypeKwd::BYTE) { fstvt = "FST_VT_SV_BYTE"; } - else { fstvt = "FST_VT_SV_BIT"; } - // clang-format on - // - // Not currently supported - // FST_VT_VCD_EVENT - // FST_VT_VCD_PORT - // FST_VT_VCD_SHORTREAL - // FST_VT_VCD_REALTIME - // FST_VT_VCD_SPARRAY - // FST_VT_VCD_TRIAND - // FST_VT_VCD_TRIOR - // FST_VT_VCD_TRIREG - // FST_VT_VCD_WAND - // FST_VT_VCD_WOR - // FST_VT_SV_ENUM - // FST_VT_GEN_STRING - puts("," + fstvt); - } - // Range - if (nodep->arrayRange().ranged()) { - puts(", true,(i+" + cvtToStr(nodep->arrayRange().lo()) + ")"); - } else { - puts(", false,-1"); - } - if (!nodep->dtypep()->basicp()->isDouble() && nodep->bitRange().ranged()) { - puts(", " + cvtToStr(nodep->bitRange().left()) + "," - + cvtToStr(nodep->bitRange().right())); - } - puts(");"); - } - - int emitTraceDeclDType(AstNodeDType* nodep) { - // Return enum number or -1 for none - if (v3Global.opt.traceFormat().fst()) { - // Skip over refs-to-refs, but stop before final ref so can get data type name - // Alternatively back in V3Width we could push enum names from upper typedefs - if (AstEnumDType* enump = VN_CAST(nodep->skipRefToEnump(), EnumDType)) { - int enumNum = enump->user1(); - if (!enumNum) { - enumNum = ++m_enumNum; - enump->user1(enumNum); - int nvals = 0; - puts("{\n"); - puts("const char* " + protect("__VenumItemNames") + "[]\n"); - puts("= {"); - for (AstEnumItem* itemp = enump->itemsp(); itemp; - itemp = VN_CAST(itemp->nextp(), EnumItem)) { - if (++nvals > 1) puts(", "); - putbs("\"" + itemp->prettyName() + "\""); - } - puts("};\n"); - nvals = 0; - puts("const char* " + protect("__VenumItemValues") + "[]\n"); - puts("= {"); - for (AstEnumItem* itemp = enump->itemsp(); itemp; - itemp = VN_CAST(itemp->nextp(), EnumItem)) { - AstConst* constp = VN_CAST(itemp->valuep(), Const); - if (++nvals > 1) puts(", "); - putbs("\"" + constp->num().displayed(nodep, "%0b") + "\""); - } - puts("};\n"); - puts("tracep->declDTypeEnum(" + cvtToStr(enumNum) + ", \"" - + enump->prettyName() + "\", " + cvtToStr(nvals) + ", " - + cvtToStr(enump->widthMin()) + ", " + protect("__VenumItemNames") + ", " - + protect("__VenumItemValues") + ");\n"); - puts("}\n"); - } - return enumNum; - } - } - return -1; - } - - void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) { - iterateAndNextNull(nodep->precondsp()); - const string func = nodep->full() ? "full" : "chg"; - bool emitWidth = true; - if (nodep->dtypep()->basicp()->isDouble()) { - puts("tracep->" + func + "Double"); - emitWidth = false; - } else if (nodep->isWide() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep)) { - puts("tracep->" + func + "WData"); - } else if (nodep->isQuad()) { - puts("tracep->" + func + "QData"); - } else if (nodep->declp()->widthMin() > 16) { - puts("tracep->" + func + "IData"); - } else if (nodep->declp()->widthMin() > 8) { - puts("tracep->" + func + "SData"); - } else if (nodep->declp()->widthMin() > 1) { - puts("tracep->" + func + "CData"); - } else { - puts("tracep->" + func + "Bit"); - emitWidth = false; - } - - const uint32_t offset = (arrayindex < 0) ? 0 : (arrayindex * nodep->declp()->widthWords()); - const uint32_t code = nodep->declp()->code() + offset; - puts(v3Global.opt.trueTraceThreads() && !nodep->full() ? "(base+" : "(oldp+"); - puts(cvtToStr(code - m_baseCode)); - puts(","); - emitTraceValue(nodep, arrayindex); - if (emitWidth) puts("," + cvtToStr(nodep->declp()->widthMin())); - puts(");\n"); - } - void emitTraceValue(AstTraceInc* nodep, int arrayindex) { - if (AstVarRef* const varrefp = VN_CAST(nodep->valuep(), VarRef)) { - AstVar* varp = varrefp->varp(); - puts("("); - if (emitTraceIsScBigUint(nodep)) { - puts("(vluint32_t*)"); - } else if (emitTraceIsScBv(nodep)) { - puts("VL_SC_BV_DATAP("); - } - iterate(varrefp); // Put var name out - // Tracing only supports 1D arrays - if (nodep->declp()->arrayRange().ranged()) { - if (arrayindex == -2) { - puts("[i]"); - } else if (arrayindex == -1) { - puts("[0]"); - } else { - puts("[" + cvtToStr(arrayindex) + "]"); - } - } - if (varp->isSc()) puts(".read()"); - if (emitTraceIsScUint(nodep)) { - puts(nodep->isQuad() ? ".to_uint64()" : ".to_uint()"); - } else if (emitTraceIsScBigUint(nodep)) { - puts(".get_raw()"); - } else if (emitTraceIsScBv(nodep)) { - puts(")"); - } - puts(")"); - } else { - puts("("); - iterate(nodep->valuep()); - puts(")"); - } - } - - // VISITORS - using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning - virtual void visit(AstNetlist* nodep) override { - // Top module only - iterate(nodep->topModulep()); - } - virtual void visit(AstNodeModule* nodep) override { - m_modp = nodep; - iterateChildren(nodep); - m_modp = nullptr; - } - virtual void visit(AstCFunc* nodep) override { - if (nodep->slow() != m_slow) return; - VL_RESTORER(m_cfuncp); - VL_RESTORER(m_useSelfForThis); - if (nodep->funcType().isTrace()) { // TRACE_* - m_cfuncp = nodep; - - if (splitNeeded()) { - // Splitting file, so using parallel build. - v3Global.useParallelBuild(true); - // Close old file - VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); - // Open a new file - newOutCFile(splitFilenumInc()); - } - - splitSizeInc(nodep); - - puts("\n"); - m_lazyDecls.emit(nodep); - emitCFuncHeader(nodep, m_modp, /* withScope: */ true); - puts(" {\n"); - - if (nodep->isLoose()) { - m_lazyDecls.declared(nodep); // Defined here, so no longer needs declaration - if (!nodep->isStatic()) { // Standard prologue - puts("if (false && vlSelf) {} // Prevent unused\n"); - m_useSelfForThis = true; - puts(symClassAssign()); - } - } - - if (nodep->initsp()) { - string section; - emitVarList(nodep->initsp(), EVL_FUNC_ALL, "", section /*ref*/); - iterateAndNextNull(nodep->initsp()); - } - - m_baseCode = -1; - - if (nodep->funcType() == AstCFuncType::TRACE_CHANGE_SUB) { - const AstNode* const stmtp = nodep->stmtsp(); - const AstIf* const ifp = VN_CAST_CONST(stmtp, If); - const AstTraceInc* const tracep - = VN_CAST_CONST(ifp ? ifp->ifsp() : stmtp, TraceInc); - // On rare occasions we can end up with an empty sub function - m_baseCode = tracep ? tracep->declp()->code() : 0; - if (v3Global.opt.trueTraceThreads()) { - puts("const vluint32_t base = vlSymsp->__Vm_baseCode + " + cvtToStr(m_baseCode) - + ";\n"); - puts("if (false && tracep && base) {} // Prevent unused\n"); - } else { - puts("vluint32_t* const oldp = tracep->oldp(vlSymsp->__Vm_baseCode + " - + cvtToStr(m_baseCode) + ");\n"); - puts("if (false && oldp) {} // Prevent unused\n"); - } - } else if (nodep->funcType() == AstCFuncType::TRACE_FULL_SUB) { - m_baseCode = 0; - puts("vluint32_t* const oldp = tracep->oldp(vlSymsp->__Vm_baseCode);\n"); - puts("if (false && oldp) {} // Prevent unused\n"); - } else if (nodep->funcType() == AstCFuncType::TRACE_INIT_SUB) { - puts("const int c = vlSymsp->__Vm_baseCode;\n"); - puts("if (false && tracep && c) {} // Prevent unused\n"); - } - - if (nodep->stmtsp()) { - putsDecoration("// Body\n"); - puts("{\n"); - iterateAndNextNull(nodep->stmtsp()); - puts("}\n"); - } - if (nodep->finalsp()) { - putsDecoration("// Final\n"); - iterateAndNextNull(nodep->finalsp()); - } - puts("}\n"); - } - } - virtual void visit(AstTraceDecl* nodep) override { - const int enumNum = emitTraceDeclDType(nodep->dtypep()); - if (nodep->arrayRange().ranged()) { - puts("{int i; for (i=0; i<" + cvtToStr(nodep->arrayRange().elements()) + "; i++) {\n"); - emitTraceInitOne(nodep, enumNum); - puts("}}\n"); - } else { - emitTraceInitOne(nodep, enumNum); - puts("\n"); - } - } - virtual void visit(AstTraceInc* nodep) override { - if (nodep->declp()->arrayRange().ranged()) { - // It traces faster if we unroll the loop - for (int i = 0; i < nodep->declp()->arrayRange().elements(); i++) { - emitTraceChangeOne(nodep, i); - } - } else { - emitTraceChangeOne(nodep, -1); - } - } - virtual void visit(AstCoverDecl* nodep) override {} - virtual void visit(AstCoverInc* nodep) override {} - -public: - explicit EmitCTrace(bool slow) - : m_slow{slow} {} - virtual ~EmitCTrace() override = default; - void main() { - // Put out the file - newOutCFile(0); - - iterate(v3Global.rootp()); - - VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); - } -}; - -//###################################################################### -// EmitC class functions - -static void setParentClassPointers() { - // Set user4p in all CFunc and Var to point to the containing AstNodeModule - const auto setAll = [](AstNodeModule* modp) -> void { - for (AstNode* nodep = VN_CAST(modp, NodeModule)->stmtsp(); nodep; nodep = nodep->nextp()) { - if (VN_IS(nodep, CFunc) || VN_IS(nodep, Var)) nodep->user4p(modp); - } - }; - for (AstNode* modp = v3Global.rootp()->modulesp(); modp; modp = modp->nextp()) { - setAll(VN_CAST(modp, NodeModule)); - } - setAll(v3Global.rootp()->constPoolp()->modp()); -} - -void V3EmitC::emitc() { - UINFO(2, __FUNCTION__ << ": " << endl); - // Set user4 to parent module - AstUser4InUse user4InUse; - setParentClassPointers(); - // Process each module in turn - for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; - nodep = VN_CAST(nodep->nextp(), NodeModule)) { - if (VN_IS(nodep, Class)) continue; // Imped with ClassPackage - { - EmitCImp cint; - cint.mainInt(nodep); - cint.mainImp(nodep, true); - } - { - EmitCImp fast; - fast.mainImp(nodep, false); - } - } -} - -void V3EmitC::emitcTrace() { - UINFO(2, __FUNCTION__ << ": " << endl); - if (v3Global.opt.trace()) { - // Set user4 to parent module - AstUser4InUse user4InUse; - setParentClassPointers(); - { - EmitCTrace slow(true); - slow.main(); - } - { - EmitCTrace fast(false); - fast.main(); - } - } -} - -void V3EmitC::emitcFiles() { - UINFO(2, __FUNCTION__ << ": " << endl); - for (AstNodeFile* filep = v3Global.rootp()->filesp(); filep; - filep = VN_CAST(filep->nextp(), NodeFile)) { - AstCFile* cfilep = VN_CAST(filep, CFile); - if (cfilep && cfilep->tblockp()) { - V3OutCFile of(cfilep->name()); - of.puts("// DESCR" - "IPTION: Verilator generated C++\n"); - EmitCFunc visitor(cfilep->tblockp(), &of, true); - } - } -} diff --git a/src/V3EmitC.h b/src/V3EmitC.h index 406a00429..a9e6c5ea9 100644 --- a/src/V3EmitC.h +++ b/src/V3EmitC.h @@ -27,12 +27,12 @@ class V3EmitC final { public: - static void emitc(); static void emitcConstPool(); + static void emitcHeaders(); + static void emitcImp(); static void emitcInlines(); static void emitcModel(); static void emitcSyms(bool dpiHdrOnly = false); - static void emitcTrace(); static void emitcFiles(); }; diff --git a/src/V3EmitCBase.cpp b/src/V3EmitCBase.cpp index f955f99dc..0632a32e3 100644 --- a/src/V3EmitCBase.cpp +++ b/src/V3EmitCBase.cpp @@ -20,11 +20,26 @@ #include "V3EmitCBase.h" #include "V3Task.h" +//###################################################################### +// EmitCParentModule implementation + +EmitCParentModule::EmitCParentModule() { + const auto setAll = [](AstNodeModule* modp) -> void { + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (VN_IS(nodep, CFunc) || VN_IS(nodep, Var)) { nodep->user4p(modp); } + } + }; + for (AstNode* modp = v3Global.rootp()->modulesp(); modp; modp = modp->nextp()) { + setAll(VN_CAST(modp, NodeModule)); + } + setAll(v3Global.rootp()->constPoolp()->modp()); +} + //###################################################################### // EmitCBaseVisitor implementation string EmitCBaseVisitor::funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp) { - modp = modp ? modp : VN_CAST(nodep->user4p(), NodeModule); + modp = modp ? modp : EmitCParentModule::get(nodep); string name; if (nodep->isConstructor()) { name += prefixNameProtect(modp); @@ -54,8 +69,7 @@ string EmitCBaseVisitor::cFuncArgs(const AstCFunc* nodep) { string args; if (nodep->isLoose() && !nodep->isStatic()) { if (nodep->isConst().trueKnown()) args += "const "; - AstNodeModule* modp = VN_CAST(nodep->user4p(), NodeModule); - args += prefixNameProtect(modp); + args += prefixNameProtect(EmitCParentModule::get(nodep)); args += "* vlSelf"; } if (!nodep->argTypes().empty()) { @@ -82,6 +96,7 @@ string EmitCBaseVisitor::cFuncArgs(const AstCFunc* nodep) { void EmitCBaseVisitor::emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, bool withScope) { + if (funcp->slow()) puts("VL_ATTR_COLD "); if (!funcp->isConstructor() && !funcp->isDestructor()) { puts(funcp->rtnTypeVoid()); puts(" "); @@ -109,12 +124,11 @@ void EmitCBaseVisitor::emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* puts("virtual "); } emitCFuncHeader(funcp, modp, /* withScope: */ false); - if (funcp->slow()) puts(" VL_ATTR_COLD"); puts(";\n"); if (!funcp->ifdef().empty()) puts("#endif // " + funcp->ifdef() + "\n"); } -void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, const string& prefixIfImp, bool asRef) { +void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, bool asRef) { const AstBasicDType* const basicp = nodep->basicp(); bool refNeedParens = VN_IS(nodep->dtypeSkipRefp(), UnpackArrayDType); @@ -199,12 +213,12 @@ void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, const string& prefixIfIm && name.substr(name.size() - suffix.size()) == suffix; if (beStatic) puts("static VL_THREAD_LOCAL "); } - puts(nodep->vlArgType(true, false, false, prefixIfImp, asRef)); + puts(nodep->vlArgType(true, false, false, "", asRef)); puts(";\n"); } } -void EmitCBaseVisitor::emitModCUse(AstNodeModule* modp, VUseType useType) { +void EmitCBaseVisitor::emitModCUse(const AstNodeModule* modp, VUseType useType) { string nl; for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) { if (AstCUse* usep = VN_CAST(itemp, CUse)) { @@ -221,3 +235,24 @@ void EmitCBaseVisitor::emitModCUse(AstNodeModule* modp, VUseType useType) { } puts(nl); } + +void EmitCBaseVisitor::emitTextSection(const AstNodeModule* modp, AstType type) { + int last_line = -999; + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstNodeText* textp = VN_CAST(nodep, NodeText)) { + if (nodep->type() == type) { + if (last_line != nodep->fileline()->lineno()) { + if (last_line < 0) { + puts("\n//*** Below code from `systemc in Verilog file\n"); + } + putsDecoration( + ifNoProtect("// From `systemc at " + nodep->fileline()->ascii() + "\n")); + last_line = nodep->fileline()->lineno(); + } + ofp()->putsNoTracking(textp->text()); + last_line++; + } + } + } + if (last_line > 0) puts("//*** Above code from `systemc in Verilog file\n\n"); +} diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index b9362991c..dcebb4484 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -27,6 +27,24 @@ #include #include +//###################################################################### +// Set user4p in all CFunc and Var to point to the containing AstNodeModule + +class EmitCParentModule final { + // NODE STATE + // AstFunc::user4p() AstNodeModule* Parent module pointer + // AstVar::user4p() AstNodeModule* Parent module pointer + AstUser4InUse user4InUse; + +public: + EmitCParentModule(); + VL_UNCOPYABLE(EmitCParentModule); + + static const AstNodeModule* get(const AstNode* nodep) { + return VN_CAST_CONST(nodep->user4p(), NodeModule); + } +}; + //###################################################################### // Base Visitor class -- holds output file pointer @@ -71,16 +89,24 @@ public: return v3Global.opt.prefix(); } - static bool isConstPoolMod(AstNode* modp) { + static bool isConstPoolMod(const AstNode* modp) { return modp == v3Global.rootp()->constPoolp()->modp(); } + static bool isAnonOk(const AstVar* varp) { + return v3Global.opt.compLimitMembers() != 0 // Enabled + && !varp->isStatic() // Not a static variable + && !varp->isSc() // Aggregates can't be anon + && (varp->basicp() && !varp->basicp()->isOpaque()); // Aggregates can't be anon + } + static AstCFile* newCFile(const string& filename, bool slow, bool source); string cFuncArgs(const AstCFunc* nodep); void emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, bool withScope); void emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, bool cLinkage = false); - void emitVarDecl(const AstVar* nodep, const string& prefixIfImp, bool asRef = false); - void emitModCUse(AstNodeModule* modp, VUseType useType); + void emitVarDecl(const AstVar* nodep, bool asRef = false); + void emitModCUse(const AstNodeModule* modp, VUseType useType); + void emitTextSection(const AstNodeModule* modp, AstType type); // CONSTRUCTORS EmitCBaseVisitor() = default; @@ -96,7 +122,7 @@ private: int m_count = 0; // Number of statements // VISITORS virtual void visit(AstNode* nodep) override { - m_count++; + ++m_count; iterateChildren(nodep); } diff --git a/src/V3EmitCConstInit.h b/src/V3EmitCConstInit.h new file mode 100644 index 000000000..386926b63 --- /dev/null +++ b/src/V3EmitCConstInit.h @@ -0,0 +1,122 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Emit C++ for tree +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Ast.h" +#include "V3EmitCBase.h" +#include "V3Error.h" + +#include + +//###################################################################### +// Emitter that can emit constant initializer expressions + +class EmitCConstInit VL_NOT_FINAL : public EmitCBaseVisitor { + // MEMBERS + bool m_inUnpacked = false; + uint32_t m_unpackedWord = 0; + + // METHODS + VL_DEBUG_FUNC; // Declare debug() + +protected: + // VISITORS + virtual void visit(AstInitArray* nodep) override { + const AstUnpackArrayDType* const dtypep + = VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType); + UASSERT_OBJ(dtypep, nodep, "Array initializer has non-array dtype"); + const uint32_t size = dtypep->elementsConst(); + const uint32_t elemBytes = dtypep->subDTypep()->widthTotalBytes(); + const uint32_t tabMod = dtypep->subDTypep()->isString() ? 1 // String + : elemBytes <= 2 ? 8 // CData, SData + : elemBytes <= 4 ? 4 // IData + : elemBytes <= 8 ? 2 // QData + : 1; + VL_RESTORER(m_inUnpacked); + VL_RESTORER(m_unpackedWord); + m_inUnpacked = true; + // Note the double {{ initializer. The first { starts the initializer of the VlUnpacked, + // and the second starts the initializer of m_storage within the VlUnpacked. + puts("{"); + ofp()->putsNoTracking("{"); + puts("\n"); + for (uint32_t n = 0; n < size; ++n) { + m_unpackedWord = n; + if (n) puts((n % tabMod) ? ", " : ",\n"); + iterate(nodep->getIndexDefaultedValuep(n)); + } + puts("\n"); + puts("}"); + ofp()->putsNoTracking("}"); + } + + virtual void visit(AstInitItem* nodep) override { // LCOV_EXCL_START + nodep->v3fatal("Handled by AstInitArray"); + } // LCOV_EXCL_STOP + + virtual void visit(AstConst* nodep) override { + const V3Number& num = nodep->num(); + UASSERT_OBJ(!num.isFourState(), nodep, "4-state value in constant pool"); + AstNodeDType* const dtypep = nodep->dtypep(); + if (num.isString()) { + // Note: putsQuoted does not track indentation, so we use this instead + puts("\""); + puts(num.toString()); + puts("\""); + } else if (dtypep->isWide()) { + const uint32_t size = dtypep->widthWords(); + // Note the double {{ initializer. The first { starts the initializer of the VlWide, + // and the second starts the initializer of m_storage within the VlWide. + puts("{"); + ofp()->putsNoTracking("{"); + if (m_inUnpacked) puts(" // VlWide " + cvtToStr(m_unpackedWord)); + puts("\n"); + for (uint32_t n = 0; n < size; ++n) { + if (n) puts((n % 4) ? ", " : ",\n"); + ofp()->printf("0x%08" PRIx32, num.edataWord(n)); + } + puts("\n"); + puts("}"); + ofp()->putsNoTracking("}"); + } else if (dtypep->isDouble()) { + const double dnum = num.toDouble(); + const char* const fmt + = !m_inUnpacked && (static_cast(dnum) == dnum && -1000 < dnum && dnum < 1000) + ? "%3.1f" // Force decimal point + : "%.17e"; // %e always yields a float literal + ofp()->printf(fmt, dnum); + } else if (dtypep->isQuad()) { + const uint64_t qnum = static_cast(num.toUQuad()); + const char* const fmt + = !m_inUnpacked && (qnum < 10) ? ("%" PRIx64 "ULL") : ("0x%016" PRIx64 "ULL"); + ofp()->printf(fmt, qnum); + } else { + const uint32_t unum = num.toUInt(); + const char* const fmt = !m_inUnpacked && (unum < 10) ? ("%" PRIu32 "U") + : (dtypep->widthMin() > 16) ? ("0x%08" PRIx32 "U") + : (dtypep->widthMin() > 8) ? ("0x%04" PRIx32 "U") + : ("0x%02" PRIx32 "U"); + ofp()->printf(fmt, unum); + } + } + + // Default + virtual void visit(AstNode* nodep) override { // LCOV_EXCL_START + nodep->v3fatalSrc("Unknown node type reached EmitCConstInit: " << nodep->prettyTypeName()); + } // LCOV_EXCL_STOP +}; diff --git a/src/V3EmitCConstPool.cpp b/src/V3EmitCConstPool.cpp index 589914a6e..abd50bd41 100644 --- a/src/V3EmitCConstPool.cpp +++ b/src/V3EmitCConstPool.cpp @@ -18,7 +18,7 @@ #include "verilatedos.h" #include "V3EmitC.h" -#include "V3EmitCBase.h" +#include "V3EmitCConstInit.h" #include "V3File.h" #include "V3Global.h" #include "V3String.h" @@ -30,10 +30,8 @@ //###################################################################### // Const pool emitter -class EmitCConstPool final : EmitCBaseVisitor { +class EmitCConstPool final : public EmitCConstInit { // MEMBERS - bool m_inUnpacked = false; - uint32_t m_unpackedWord = 0; uint32_t m_outFileCount = 0; int m_outFileSize = 0; VDouble0 m_tablesEmitted; @@ -51,7 +49,7 @@ class EmitCConstPool final : EmitCBaseVisitor { ofp->puts("// DESCRIPTION: Verilator output: Constant pool\n"); ofp->puts("//\n"); ofp->puts("\n"); - ofp->puts("#include \"verilated_heavy.h\"\n"); + ofp->puts("#include \"verilated.h\"\n"); return ofp; } @@ -102,77 +100,9 @@ class EmitCConstPool final : EmitCBaseVisitor { } // VISITORS - virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } - - virtual void visit(AstInitArray* nodep) override { - const AstUnpackArrayDType* const dtypep - = VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType); - UASSERT_OBJ(dtypep, nodep, "Array initializer has non-array dtype"); - const uint32_t size = dtypep->elementsConst(); - const uint32_t elemBytes = dtypep->subDTypep()->widthTotalBytes(); - const uint32_t tabMod = dtypep->subDTypep()->isString() ? 1 // String - : elemBytes <= 2 ? 8 // CData, SData - : elemBytes <= 4 ? 4 // IData - : elemBytes <= 8 ? 2 // QData - : 1; - VL_RESTORER(m_inUnpacked); - VL_RESTORER(m_unpackedWord); - m_inUnpacked = true; - // Note the double {{ initializer. The first { starts the initializer of the VlUnpacked, - // and the second starts the initializer of m_storage within the VlUnpacked. - puts("{"); - ofp()->putsNoTracking("{"); - puts("\n"); - for (uint32_t n = 0; n < size; ++n) { - m_unpackedWord = n; - if (n) puts(n % tabMod ? ", " : ",\n"); - iterate(nodep->getIndexDefaultedValuep(n)); - } - puts("\n"); - puts("}"); - ofp()->putsNoTracking("}"); - } - - virtual void visit(AstInitItem* nodep) override { // LCOV_EXCL_START - nodep->v3fatal("Handled by AstInitArray"); - } // LCOV_EXCL_END - virtual void visit(AstConst* nodep) override { - const V3Number& num = nodep->num(); - UASSERT_OBJ(!num.isFourState(), nodep, "4-state value in constant pool"); - AstNodeDType* const dtypep = nodep->dtypep(); - m_outFileSize += 1; - if (num.isString()) { - // Note: putsQuoted does not track indentation, so we use this instead - puts("\""); - puts(num.toString()); - puts("\""); - m_outFileSize += 9; - } else if (dtypep->isWide()) { - const uint32_t size = dtypep->widthWords(); - m_outFileSize += size - 1; - // Note the double {{ initializer. The first { starts the initializer of the VlWide, - // and the second starts the initializer of m_storage within the VlWide. - puts("{"); - ofp()->putsNoTracking("{"); - if (m_inUnpacked) puts(" // VlWide " + cvtToStr(m_unpackedWord)); - puts("\n"); - for (uint32_t n = 0; n < size; ++n) { - if (n) puts(n % 4 ? ", " : ",\n"); - ofp()->printf("0x%08" PRIx32, num.edataWord(n)); - } - puts("\n"); - puts("}"); - ofp()->putsNoTracking("}"); - } else if (dtypep->isQuad()) { - ofp()->printf("0x%016" PRIx64, static_cast(num.toUQuad())); - } else if (dtypep->widthMin() > 16) { - ofp()->printf("0x%08" PRIx32, num.toUInt()); - } else if (dtypep->widthMin() > 8) { - ofp()->printf("0x%04" PRIx32, num.toUInt()); - } else { - ofp()->printf("0x%02" PRIx32, num.toUInt()); - } + m_outFileSize += nodep->num().isString() ? 10 : nodep->isWide() ? nodep->widthWords() : 1; + EmitCConstInit::visit(nodep); } public: diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index 5a5613981..ed130c578 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -28,90 +28,9 @@ // We use a static char array in VL_VALUE_STRING constexpr int VL_VALUE_STRING_MAX_WIDTH = 8192; -//###################################################################### -// Establish mtask variable sort order in mtasks mode - -class EmitVarTspSorter final : public V3TSP::TspStateBase { -private: - // MEMBERS - const MTaskIdSet& m_mtaskIds; // Mtask we're ordering - static unsigned s_serialNext; // Unique ID to establish serial order - unsigned m_serial; // Serial ordering -public: - // CONSTRUCTORS - explicit EmitVarTspSorter(const MTaskIdSet& mtaskIds) - : m_mtaskIds(mtaskIds) { // Cannot be {} or GCC 4.8 false warning - m_serial = ++s_serialNext; // Cannot be ()/{} or GCC 4.8 false warning - } - virtual ~EmitVarTspSorter() = default; - // METHODS - virtual bool operator<(const TspStateBase& other) const override { - return operator<(dynamic_cast(other)); - } - bool operator<(const EmitVarTspSorter& other) const { return m_serial < other.m_serial; } - const MTaskIdSet& mtaskIds() const { return m_mtaskIds; } - virtual int cost(const TspStateBase* otherp) const override { - return cost(dynamic_cast(otherp)); - } - virtual int cost(const EmitVarTspSorter* otherp) const { - int cost = diffs(m_mtaskIds, otherp->m_mtaskIds); - cost += diffs(otherp->m_mtaskIds, m_mtaskIds); - return cost; - } - // Returns the number of elements in set_a that don't appear in set_b - static int diffs(const MTaskIdSet& set_a, const MTaskIdSet& set_b) { - int diffs = 0; - for (int i : set_a) { - if (set_b.find(i) == set_b.end()) ++diffs; - } - return diffs; - } -}; - -unsigned EmitVarTspSorter::s_serialNext = 0; - //###################################################################### // EmitCFunc -void EmitCFunc::emitCtorSep(bool* firstp) { - if (*firstp) { - puts(" : "); - *firstp = false; - } else { - puts(", "); - } - if (ofp()->exceededWidth()) puts("\n "); -} - -void EmitCFunc::emitVarCtors(bool* firstp) { - if (!m_ctorVarsVec.empty()) { - ofp()->indentInc(); - if (*firstp) puts("\n"); - for (const AstVar* varp : m_ctorVarsVec) { - const AstBasicDType* const dtypep = VN_CAST(varp->dtypeSkipRefp(), BasicDType); - if (!dtypep) { - puts("// Skipping array: "); - puts(varp->nameProtect()); - puts("\n"); - } else if (dtypep->keyword().isMTaskState()) { - emitCtorSep(firstp); - puts(varp->nameProtect()); - puts("("); - iterate(varp->valuep()); - puts(")"); - } else { - emitCtorSep(firstp); - puts(varp->nameProtect()); - puts("("); - putsQuoted(varp->nameProtect()); - puts(")"); - } - } - puts("\n"); - ofp()->indentDec(); - } -} - bool EmitCFunc::emitSimpleOk(AstNodeMath* nodep) { // Can we put out a simple (A + B) instead of VL_ADD_III(A,B)? if (nodep->emitSimpleOperator() == "") return false; @@ -514,199 +433,12 @@ void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const stri displayEmit(nodep, isScan); } -void EmitCFunc::emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp, - string& sectionr) { - // Put out a list of signal declarations - // in order of 0:clocks, 1:vluint8, 2:vluint16, 4:vluint32, 5:vluint64, 6:wide, 7:arrays - // This aids cache packing and locality - // - // Largest->smallest reduces the number of pad variables. Also - // experimented with alternating between large->small and small->large - // on successive Mtask groups, but then when a new mtask gets added may - // cause a huge delta. - // - // TODO: Move this sort to an earlier visitor stage. - VarSortMap varAnonMap; - VarSortMap varNonanonMap; - - for (int isstatic = 1; isstatic >= 0; isstatic--) { - if (prefixIfImp != "" && !isstatic) continue; - for (AstNode* nodep = firstp; nodep; nodep = nodep->nextp()) { - if (const AstVar* varp = VN_CAST(nodep, Var)) { - bool doit = true; - switch (which) { - case EVL_CLASS_IO: doit = varp->isIO(); break; - case EVL_CLASS_SIG: - doit = ((varp->isSignal() || varp->isClassMember()) && !varp->isIO()); - break; - case EVL_CLASS_TEMP: doit = (varp->isTemp() && !varp->isIO()); break; - case EVL_CLASS_PAR: - doit = (varp->isParam() && !VN_IS(varp->valuep(), Const)); - break; - case EVL_CLASS_ALL: doit = true; break; - case EVL_FUNC_ALL: doit = true; break; - default: v3fatalSrc("Bad Case"); - } - if (varp->isStatic() ? !isstatic : isstatic) doit = false; - if (doit) { - const int sigbytes = varp->dtypeSkipRefp()->widthAlignBytes(); - int sortbytes = 9; - if (varp->isUsedClock() && varp->widthMin() == 1) { - sortbytes = 0; - } else if (VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType)) { - sortbytes = 8; - } else if (varp->basicp() && varp->basicp()->isOpaque()) { - sortbytes = 7; - } else if (varp->isScBv() || varp->isScBigUint()) { - sortbytes = 6; - } else if (sigbytes == 8) { - sortbytes = 5; - } else if (sigbytes == 4) { - sortbytes = 4; - } else if (sigbytes == 2) { - sortbytes = 2; - } else if (sigbytes == 1) { - sortbytes = 1; - } - const bool anonOk - = (v3Global.opt.compLimitMembers() != 0 // Enabled - && !varp->isStatic() && !varp->isIO() // Confusing to user - && !varp->isSc() // Aggregates can't be anon - && (varp->basicp() - && !varp->basicp()->isOpaque()) // Aggregates can't be anon - && which != EVL_FUNC_ALL); // Anon not legal in funcs, and gcc - // bug free there anyhow - if (anonOk) { - varAnonMap[sortbytes].push_back(varp); - } else { - varNonanonMap[sortbytes].push_back(varp); - } - } - } - } - } - - if (!varAnonMap.empty() || !varNonanonMap.empty()) { - if (!sectionr.empty()) { - puts(sectionr); - sectionr = ""; - } - VarVec anons; - VarVec nonanons; - emitVarSort(varAnonMap, &anons); - emitVarSort(varNonanonMap, &nonanons); - emitSortedVarList(anons, nonanons, prefixIfImp); - } -} - -void EmitCFunc::emitVarSort(const VarSortMap& vmap, VarVec* sortedp) { - UASSERT(sortedp->empty(), "Sorted should be initially empty"); - if (!v3Global.opt.mtasks()) { - // Plain old serial mode. Sort by size, from small to large, - // to optimize for both packing and small offsets in code. - for (const auto& itr : vmap) { - for (VarVec::const_iterator jt = itr.second.begin(); jt != itr.second.end(); ++jt) { - sortedp->push_back(*jt); - } - } - return; - } - - // MacroTask mode. Sort by MTask-affinity group first, size second. - using MTaskVarSortMap = std::map; - MTaskVarSortMap m2v; - for (VarSortMap::const_iterator it = vmap.begin(); it != vmap.end(); ++it) { - const int size_class = it->first; - const VarVec& vec = it->second; - for (const AstVar* varp : vec) { m2v[varp->mtaskIds()][size_class].push_back(varp); } - } - - // Create a TSP sort state for each MTaskIdSet footprint - V3TSP::StateVec states; - for (MTaskVarSortMap::iterator it = m2v.begin(); it != m2v.end(); ++it) { - states.push_back(new EmitVarTspSorter(it->first)); - } - - // Do the TSP sort - V3TSP::StateVec sorted_states; - V3TSP::tspSort(states, &sorted_states); - - for (V3TSP::StateVec::iterator it = sorted_states.begin(); it != sorted_states.end(); ++it) { - const EmitVarTspSorter* statep = dynamic_cast(*it); - const VarSortMap& localVmap = m2v[statep->mtaskIds()]; - // use rbegin/rend to sort size large->small - for (VarSortMap::const_reverse_iterator jt = localVmap.rbegin(); jt != localVmap.rend(); - ++jt) { - const VarVec& vec = jt->second; - for (VarVec::const_iterator kt = vec.begin(); kt != vec.end(); ++kt) { - sortedp->push_back(*kt); - } - } - VL_DO_DANGLING(delete statep, statep); - } -} - -void EmitCFunc::emitSortedVarList(const VarVec& anons, const VarVec& nonanons, - const string& prefixIfImp) { - string curVarCmt; - // Output anons - { - const int anonMembers = anons.size(); - const int lim = v3Global.opt.compLimitMembers(); - int anonL3s = 1; - int anonL2s = 1; - int anonL1s = 1; - if (anonMembers > (lim * lim * lim)) { - anonL3s = (anonMembers + (lim * lim * lim) - 1) / (lim * lim * lim); - anonL2s = lim; - anonL1s = lim; - } else if (anonMembers > (lim * lim)) { - anonL2s = (anonMembers + (lim * lim) - 1) / (lim * lim); - anonL1s = lim; - } else if (anonMembers > lim) { - anonL1s = (anonMembers + lim - 1) / lim; - } - if (anonL1s != 1) - puts("// Anonymous structures to workaround compiler member-count bugs\n"); - auto it = anons.cbegin(); - for (int l3 = 0; l3 < anonL3s && it != anons.cend(); ++l3) { - if (anonL3s != 1) puts("struct {\n"); - for (int l2 = 0; l2 < anonL2s && it != anons.cend(); ++l2) { - if (anonL2s != 1) puts("struct {\n"); - for (int l1 = 0; l1 < anonL1s && it != anons.cend(); ++l1) { - if (anonL1s != 1) puts("struct {\n"); - for (int l0 = 0; l0 < lim && it != anons.cend(); ++l0) { - const AstVar* varp = *it; - emitVarDecl(varp, prefixIfImp); - ++it; - } - if (anonL1s != 1) puts("};\n"); - } - if (anonL2s != 1) puts("};\n"); - } - if (anonL3s != 1) puts("};\n"); - } - // Leftovers, just in case off by one error somewhere above - for (; it != anons.end(); ++it) { - const AstVar* varp = *it; - emitVarDecl(varp, prefixIfImp); - } - } - // Output nonanons - for (const AstVar* varp : nonanons) { - if (varp->isIO() && varp->isSc()) { m_ctorVarsVec.push_back(varp); } - AstBasicDType* const basicp = varp->basicp(); - if (basicp && basicp->keyword().isMTaskState()) { m_ctorVarsVec.push_back(varp); } - emitVarDecl(varp, prefixIfImp); - } -} - -void EmitCFunc::emitCCallArgs(AstNodeCCall* nodep) { +void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPointer) { + puts("("); bool comma = false; if (nodep->funcp()->isLoose() && !nodep->funcp()->isStatic()) { - UASSERT_OBJ(!nodep->selfPointer().empty(), nodep, - "Call to loose method without self pointer"); - puts(nodep->selfPointerProtect(m_useSelfForThis)); + UASSERT_OBJ(!selfPointer.empty(), nodep, "Call to loose method without self pointer"); + puts(selfPointer); comma = true; } if (!nodep->argTypes().empty()) { @@ -719,6 +451,12 @@ void EmitCFunc::emitCCallArgs(AstNodeCCall* nodep) { iterate(subnodep); comma = true; } + if (VN_IS(nodep->backp(), NodeMath) || VN_IS(nodep->backp(), CReturn)) { + // We should have a separate CCall for math and statement usage, but... + puts(")"); + } else { + puts(");\n"); + } } void EmitCFunc::emitDereference(const string& pointer) { @@ -1034,7 +772,7 @@ void EmitCFunc::emitChangeDet() { puts("QData __req = false; // Logically a bool\n"); // But not because it results in // faster code bool gotOne = false; - for (AstChangeDet* changep : m_blkChangeDetVec) { + for (AstChangeDet* const changep : m_blkChangeDetVec) { if (changep->lhsp()) { if (!gotOne) { // Not a clocked block puts("__req |= ("); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index bee0293a4..f4e72f380 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -21,7 +21,7 @@ #include "verilatedos.h" #include "V3Global.h" -#include "V3EmitCBase.h" +#include "V3EmitCConstInit.h" #include #include @@ -53,8 +53,7 @@ class EmitCLazyDecls final : public AstNVisitor { // Already declared manually if (m_emittedManually.count(funcp->nameProtect())) return; // Needs lazy declaration, emit one - m_emitter.emitCFuncDecl(funcp, VN_CAST_CONST(funcp->user4p(), NodeModule), - funcp->dpiImportPrototype()); + m_emitter.emitCFuncDecl(funcp, EmitCParentModule::get(funcp), funcp->dpiImportPrototype()); m_needsBlankLine = true; } @@ -82,7 +81,9 @@ class EmitCLazyDecls final : public AstNVisitor { virtual void visit(AstVarRef* nodep) override { AstVar* const varp = nodep->varp(); // Only constant pool symbols are lazy declared for now ... - if (EmitCBaseVisitor::isConstPoolMod(varp->user4p())) { lazyDeclareConstPoolVar(varp); } + if (EmitCBaseVisitor::isConstPoolMod(EmitCParentModule::get(varp))) { + lazyDeclareConstPoolVar(varp); + } } virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } @@ -90,7 +91,7 @@ class EmitCLazyDecls final : public AstNVisitor { VL_DEBUG_FUNC; public: - EmitCLazyDecls(EmitCBaseVisitor& emitter) + explicit EmitCLazyDecls(EmitCBaseVisitor& emitter) : m_emitter(emitter) {} void emit(AstNode* nodep) { m_needsBlankLine = false; @@ -112,39 +113,31 @@ public: //###################################################################### // Emit statements and math operators -class EmitCFunc VL_NOT_FINAL : public EmitCBaseVisitor { +class EmitCFunc VL_NOT_FINAL : public EmitCConstInit { private: - using VarVec = std::vector; - using VarSortMap = std::map; // Map size class to VarVec - - bool m_suppressSemi; AstVarRef* m_wideTempRefp; // Variable that _WW macros should be setting - VarVec m_ctorVarsVec; // All variables in constructor order int m_labelNum; // Next label number int m_splitSize; // # of cfunc nodes placed into output file - int m_splitFilenum; // File number being created, 0 = primary bool m_inUC = false; // Inside an AstUCStmt or AstUCMath std::vector m_blkChangeDetVec; // All encountered changes in block + bool m_emitConstInit = false; // Emitting constant initializer protected: EmitCLazyDecls m_lazyDecls; // Visitor for emitting lazy declarations bool m_useSelfForThis = false; // Replace "this" with "vlSelf" - AstNodeModule* m_modp = nullptr; // Current module being emitted + const AstNodeModule* m_modp = nullptr; // Current module being emitted + AstCFunc* m_cfuncp = nullptr; // Current function being emitted public: // METHODS VL_DEBUG_FUNC; // Declare debug() // ACCESSORS - int splitFilenumInc() { - m_splitSize = 0; - return ++m_splitFilenum; - } - int splitSize() const { return m_splitSize; } void splitSizeInc(int count) { m_splitSize += count; } void splitSizeInc(AstNode* nodep) { splitSizeInc(EmitCBaseCounterVisitor(nodep).count()); } + void splitSizeReset() { m_splitSize = 0; } bool splitNeeded() const { - return v3Global.opt.outputSplit() && splitSize() >= v3Global.opt.outputSplit(); + return v3Global.opt.outputSplit() && m_splitSize >= v3Global.opt.outputSplit(); } // METHODS @@ -154,19 +147,6 @@ public: void displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const string& vfmt, bool ignore, char fmtLetter); - enum EisWhich : uint8_t { - EVL_CLASS_IO, - EVL_CLASS_SIG, - EVL_CLASS_TEMP, - EVL_CLASS_PAR, - EVL_CLASS_ALL, - EVL_FUNC_ALL - }; - void emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp, string& sectionr); - static void emitVarSort(const VarSortMap& vmap, VarVec* sortedp); - void emitSortedVarList(const VarVec& anons, const VarVec& nonanons, const string& prefixIfImp); - void emitVarCtors(bool* firstp); - void emitCtorSep(bool* firstp); bool emitSimpleOk(AstNodeMath* nodep); void emitIQW(AstNode* nodep) { // Other abbrevs: "C"har, "S"hort, "F"loat, "D"ouble, stri"N"g @@ -188,7 +168,7 @@ public: } void emitOpName(AstNode* nodep, const string& format, AstNode* lhsp, AstNode* rhsp, AstNode* thsp); - void emitCCallArgs(AstNodeCCall* nodep); + void emitCCallArgs(const AstNodeCCall* nodep, const string& selfPointer); void emitDereference(const string& pointer); void emitCvtPackStr(AstNode* nodep); void emitCvtWideArray(AstNode* nodep, AstNode* fromp); @@ -199,10 +179,20 @@ public: AstNodeDType* dtypep, int depth, const string& suffix); void doubleOrDetect(AstChangeDet* changep, bool& gotOne); void emitChangeDet(); + void emitConstInit(AstNode* initp) { + // We should refactor emit to produce output into a provided buffer, not go through members + // variables. That way we could just invoke the appropriate emitter as needed. + VL_RESTORER(m_emitConstInit); + m_emitConstInit = true; + iterate(initp); + } // VISITORS + using EmitCConstInit::visit; virtual void visit(AstCFunc* nodep) override { VL_RESTORER(m_useSelfForThis); + VL_RESTORER(m_cfuncp); + m_cfuncp = nodep; m_blkChangeDetVec.clear(); @@ -238,25 +228,28 @@ public: puts(nodep->isLoose() ? "__" : "::"); puts(nodep->nameProtect() + "\\n\"); );\n"); - if (nodep->initsp()) putsDecoration("// Variables\n"); for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) { if (AstVar* varp = VN_CAST(subnodep, Var)) { - if (varp->isFuncReturn()) emitVarDecl(varp, ""); + if (varp->isFuncReturn()) emitVarDecl(varp); } } - string section; - emitVarList(nodep->initsp(), EVL_FUNC_ALL, "", section /*ref*/); - emitVarList(nodep->stmtsp(), EVL_FUNC_ALL, "", section /*ref*/); - iterateAndNextNull(nodep->initsp()); + if (nodep->initsp()) { + putsDecoration("// Init\n"); + iterateAndNextNull(nodep->initsp()); + } + + if (nodep->stmtsp()) { + putsDecoration("// Body\n"); + iterateAndNextNull(nodep->stmtsp()); + } - if (nodep->stmtsp()) putsDecoration("// Body\n"); - iterateAndNextNull(nodep->stmtsp()); if (!m_blkChangeDetVec.empty()) emitChangeDet(); - if (nodep->finalsp()) putsDecoration("// Final\n"); - iterateAndNextNull(nodep->finalsp()); - // + if (nodep->finalsp()) { + putsDecoration("// Final\n"); + iterateAndNextNull(nodep->finalsp()); + } if (!m_blkChangeDetVec.empty()) puts("return __req;\n"); @@ -264,6 +257,11 @@ public: if (nodep->ifdef() != "") puts("#endif // " + nodep->ifdef() + "\n"); } + virtual void visit(AstVar* nodep) override { + UASSERT_OBJ(m_cfuncp, nodep, "Cannot emit non-local variable"); + emitVarDecl(nodep); + } + virtual void visit(AstNodeAssign* nodep) override { bool paren = true; bool decind = false; @@ -344,7 +342,7 @@ public: iterateAndNextNull(nodep->rhsp()); if (paren) puts(")"); if (decind) ofp()->blockDec(); - if (!m_suppressSemi) puts(";\n"); + puts(";\n"); } virtual void visit(AstAlwaysPublic*) override {} virtual void visit(AstAssocSel* nodep) override { @@ -359,24 +357,19 @@ public: } puts(")"); } - virtual void visit(AstNodeCCall* nodep) override { + virtual void visit(AstCCall* nodep) override { const AstCFunc* const funcp = nodep->funcp(); - if (AstCMethodCall* ccallp = VN_CAST(nodep, CMethodCall)) { - UASSERT_OBJ(!funcp->isLoose(), nodep, "Loose method called via AstCMethodCall"); - // make this a Ast type for future opt - iterate(ccallp->fromp()); - putbs("->"); - puts(funcp->nameProtect()); - } else if (funcp->dpiImportPrototype()) { + const AstNodeModule* const funcModp = EmitCParentModule::get(funcp); + if (funcp->dpiImportPrototype()) { // Calling DPI import puts(funcp->name()); } else if (funcp->isProperMethod() && funcp->isStatic()) { // Call static method via the containing class - puts(prefixNameProtect(funcp->user4p()) + "::"); + puts(prefixNameProtect(funcModp) + "::"); puts(funcp->nameProtect()); - } else if (VN_IS(funcp->user4p(), Class) && funcp->user4p() != m_modp) { + } else if (VN_IS(funcModp, Class) && funcModp != m_modp) { // Calling superclass method - puts(prefixNameProtect(funcp->user4p()) + "::"); + puts(prefixNameProtect(funcModp) + "::"); puts(funcp->nameProtect()); } else if (funcp->isLoose()) { // Calling loose method @@ -388,14 +381,22 @@ public: } puts(funcp->nameProtect()); } - puts("("); - emitCCallArgs(nodep); - if (VN_IS(nodep->backp(), NodeMath) || VN_IS(nodep->backp(), CReturn)) { - // We should have a separate CCall for math and statement usage, but... - puts(")"); - } else { - puts(");\n"); - } + emitCCallArgs(nodep, nodep->selfPointerProtect(m_useSelfForThis)); + } + virtual void visit(AstCMethodCall* nodep) override { + const AstCFunc* const funcp = nodep->funcp(); + UASSERT_OBJ(!funcp->isLoose(), nodep, "Loose method called via AstCMethodCall"); + iterate(nodep->fromp()); + putbs("->"); + puts(funcp->nameProtect()); + emitCCallArgs(nodep, ""); + } + virtual void visit(AstCNew* nodep) override { + puts("std::make_shared<" + prefixNameProtect(nodep->dtypep()) + ">("); + puts("vlSymsp"); // TODO make this part of argsp, and eliminate when unnecessary + if (nodep->argsp()) puts(", "); + iterateAndNextNull(nodep->argsp()); + puts(")"); } virtual void visit(AstCMethodHard* nodep) override { iterate(nodep->fromp()); @@ -594,11 +595,11 @@ public: } void checkMaxWords(AstNode* nodep) { - if (nodep->widthWords() > VL_TO_STRING_MAX_WORDS) { + if (nodep->widthWords() > VL_VALUE_STRING_MAX_WORDS) { nodep->v3error( "String of " << nodep->width() - << " bits exceeds hardcoded limit VL_TO_STRING_MAX_WORDS in verilatedos.h"); + << " bits exceeds hardcoded limit VL_VALUE_STRING_MAX_WORDS in verilatedos.h"); } } virtual void visit(AstFOpen* nodep) override { @@ -974,8 +975,13 @@ public: if (nodep->lhsp()->isWide()) { visit(VN_CAST(nodep, NodeUniop)); } else { + AstVarRef* const vrefp = VN_CAST(nodep->lhsp(), VarRef); + const int widthPow2 = vrefp ? vrefp->varp()->dtypep()->widthPow2() + : nodep->lhsp()->dtypep()->widthPow2(); + UASSERT_OBJ(widthPow2 > 1, nodep, + "Reduction over single bit value should have been folded"); putbs("VL_REDXOR_"); - puts(cvtToStr(nodep->lhsp()->dtypep()->widthPow2())); + puts(cvtToStr(widthPow2)); puts("("); iterateAndNextNull(nodep->lhsp()); puts(")"); @@ -1019,13 +1025,6 @@ public: puts(cvtToStr(nodep->fileline()->lineno())); puts(")"); } - virtual void visit(AstCNew* nodep) override { - puts("std::make_shared<" + prefixNameProtect(nodep->dtypep()) + ">("); - puts("vlSymsp"); // TODO make this part of argsp, and eliminate when unnecessary - if (nodep->argsp()) puts(", "); - iterateAndNextNull(nodep->argsp()); - puts(")"); - } virtual void visit(AstNewCopy* nodep) override { puts("std::make_shared<" + prefixNameProtect(nodep->dtypep()) + ">("); puts("*"); // i.e. make into a reference @@ -1112,15 +1111,16 @@ public: // Terminals virtual void visit(AstVarRef* nodep) override { const AstVar* const varp = nodep->varp(); - if (isConstPoolMod(varp->user4p())) { + const AstNodeModule* const varModp = EmitCParentModule::get(varp); + if (isConstPoolMod(varModp)) { // Reference to constant pool variable puts(topClassName() + "__ConstPool__"); } else if (varp->isStatic()) { // Access static variable via the containing class - puts(prefixNameProtect(varp->user4p()) + "::"); - } else if (VN_IS(varp->user4p(), Class) && varp->user4p() != m_modp) { + puts(prefixNameProtect(varModp) + "::"); + } else if (VN_IS(varModp, Class) && varModp != m_modp) { // Superclass member reference - puts(prefixNameProtect(varp->user4p()) + "::"); + puts(prefixNameProtect(varModp) + "::"); } else if (!nodep->selfPointer().empty()) { emitDereference(nodep->selfPointerProtect(m_useSelfForThis)); } @@ -1134,7 +1134,9 @@ public: puts(funcNameProtect(funcp)); } virtual void visit(AstConst* nodep) override { - if (nodep->isWide()) { + if (m_emitConstInit) { + EmitCConstInit::visit(nodep); + } else if (nodep->isWide()) { UASSERT_OBJ(m_wideTempRefp, nodep, "Wide Constant w/ no temp"); emitConstant(nodep, m_wideTempRefp, ""); m_wideTempRefp = nullptr; // We used it, barf if set it a second time @@ -1213,37 +1215,22 @@ public: m_blkChangeDetVec.push_back(nodep); } - // Just iterate - virtual void visit(AstNetlist* nodep) override { iterateChildren(nodep); } - virtual void visit(AstTopScope* nodep) override { iterateChildren(nodep); } - virtual void visit(AstScope* nodep) override { iterateChildren(nodep); } - // NOPs - virtual void visit(AstTypedef*) override {} - virtual void visit(AstPragma*) override {} - virtual void visit(AstCell*) override {} // Handled outside the Visit class - virtual void visit(AstVar*) override {} // Handled outside the Visit class - virtual void visit(AstNodeText*) override {} // Handled outside the Visit class - virtual void visit(AstTraceDecl*) override {} // Handled outside the Visit class - virtual void visit(AstTraceInc*) override {} // Handled outside the Visit class - virtual void visit(AstCFile*) override {} // Handled outside the Visit class - virtual void visit(AstCellInline*) override {} // Handled outside visit (in EmitCSyms) - virtual void visit(AstCUse*) override {} // Handled outside the Visit class // Default virtual void visit(AstNode* nodep) override { puts(string("\n???? // ") + nodep->prettyTypeName() + "\n"); iterateChildren(nodep); + // LCOV_EXCL_START if (!v3Global.opt.lintOnly()) { // An internal problem, so suppress nodep->v3fatalSrc("Unknown node type reached emitter: " << nodep->prettyTypeName()); } + // LCOV_EXCL_STOP } EmitCFunc() : m_lazyDecls(*this) { - m_suppressSemi = false; m_wideTempRefp = nullptr; m_labelNum = 0; m_splitSize = 0; - m_splitFilenum = 0; } EmitCFunc(AstNode* nodep, V3OutCFile* ofp, bool trackText = false) : EmitCFunc{} { diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp new file mode 100644 index 000000000..74dce02c8 --- /dev/null +++ b/src/V3EmitCHeaders.cpp @@ -0,0 +1,340 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Emit C++ for tree +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Global.h" +#include "V3EmitC.h" +#include "V3EmitCConstInit.h" + +#include +#include + +//###################################################################### +// Internal EmitC implementation + +class EmitCHeader final : public EmitCConstInit { + // METHODS + VL_DEBUG_FUNC; // Declare debug() + + void decorateFirst(bool& first, const string& str) { + if (first) { + putsDecoration(str); + first = false; + } + } + void emitCellDecls(const AstNodeModule* modp) { + bool first = true; + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstCell* const cellp = VN_CAST_CONST(nodep, Cell)) { + decorateFirst(first, "// CELLS\n"); + puts(prefixNameProtect(cellp->modp()) + "* " + cellp->nameProtect() + ";\n"); + } + } + } + void emitDesignVarDecls(const AstNodeModule* modp) { + bool first = true; + std::vector varList; + bool lastAnon = false; // initial value is not important, but is used + + const auto emitCurrentList = [this, &first, &varList, &lastAnon]() { + if (varList.empty()) return; + + decorateFirst(first, "\n// DESIGN SPECIFIC STATE\n"); + + if (lastAnon) { // Output as anons + const int anonMembers = varList.size(); + const int lim = v3Global.opt.compLimitMembers(); + int anonL3s = 1; + int anonL2s = 1; + int anonL1s = 1; + if (anonMembers > (lim * lim * lim)) { + anonL3s = (anonMembers + (lim * lim * lim) - 1) / (lim * lim * lim); + anonL2s = lim; + anonL1s = lim; + } else if (anonMembers > (lim * lim)) { + anonL2s = (anonMembers + (lim * lim) - 1) / (lim * lim); + anonL1s = lim; + } else if (anonMembers > lim) { + anonL1s = (anonMembers + lim - 1) / lim; + } + if (anonL1s != 1) + puts("// Anonymous structures to workaround compiler member-count bugs\n"); + auto it = varList.cbegin(); + for (int l3 = 0; l3 < anonL3s && it != varList.cend(); ++l3) { + if (anonL3s != 1) puts("struct {\n"); + for (int l2 = 0; l2 < anonL2s && it != varList.cend(); ++l2) { + if (anonL2s != 1) puts("struct {\n"); + for (int l1 = 0; l1 < anonL1s && it != varList.cend(); ++l1) { + if (anonL1s != 1) puts("struct {\n"); + for (int l0 = 0; l0 < lim && it != varList.cend(); ++l0) { + emitVarDecl(*it); + ++it; + } + if (anonL1s != 1) puts("};\n"); + } + if (anonL2s != 1) puts("};\n"); + } + if (anonL3s != 1) puts("};\n"); + } + // Leftovers, just in case off by one error somewhere above + for (; it != varList.cend(); ++it) emitVarDecl(*it); + } else { // Output as nonanons + for (const auto& pair : varList) emitVarDecl(pair); + } + + varList.clear(); + }; + + // Emit variables in consecutive anon and non-anon batches + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { + if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp()) { + const bool anon = isAnonOk(varp); + if (anon != lastAnon) emitCurrentList(); + lastAnon = anon; + varList.emplace_back(varp); + } + } + } + + // Emit final batch + emitCurrentList(); + } + void emitInternalVarDecls(const AstNodeModule* modp) { + if (!VN_IS(modp, Class)) { + putsDecoration("\n// INTERNAL VARIABLES\n"); + puts(symClassName() + "* vlSymsp; // Symbol table\n"); + } + } + void emitParamDecls(const AstNodeModule* modp) { + bool first = true; + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* const varp = VN_CAST(nodep, Var)) { + if (varp->isParam()) { + decorateFirst(first, "\n// PARAMETERS\n"); + UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?"); + // Only C++ LiteralTypes can be constexpr + const bool canBeConstexpr = varp->dtypep()->isLiteralType(); + puts("static "); + puts(canBeConstexpr ? "constexpr " : "const "); + puts(varp->dtypep()->cType(varp->nameProtect(), false, false)); + if (canBeConstexpr) { + puts(" = "); + iterate(varp->valuep()); + } + puts(";\n"); + } + } + } + } + void emitCtorDtorDecls(const AstNodeModule* modp) { + if (!VN_IS(modp, Class)) { // Classes use CFuncs with isConstructor/isDestructor + const string& name = prefixNameProtect(modp); + putsDecoration("\n// CONSTRUCTORS\n"); + puts(name + "(const char* name);\n"); + puts("~" + name + "();\n"); + puts("VL_UNCOPYABLE(" + name + ");\n"); + } + } + void emitInternalMethodDecls(const AstNodeModule* modp) { + bool first = true; + const string section = "\n// INTERNAL METHODS\n"; + + if (!VN_IS(modp, Class)) { + decorateFirst(first, section); + puts("void " + protect("__Vconfigure") + "(" + symClassName() + + "* symsp, bool first);\n"); + } + + if (v3Global.opt.coverage()) { + decorateFirst(first, section); + puts("void __vlCoverInsert("); + puts(v3Global.opt.threads() ? "std::atomic" : "uint32_t"); + puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); + puts("const char* hierp, const char* pagep, const char* commentp, const char* " + "linescovp);\n"); + } + + if (v3Global.opt.savable()) { + decorateFirst(first, section); + puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n"); + puts("void " + protect("__Vdeserialize") + "(VerilatedDeserialize& os);\n"); + } + } + void emitEnums(const AstNodeModule* modp) { + bool first = true; + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + const AstTypedef* const tdefp = VN_CAST_CONST(nodep, Typedef); + if (!tdefp) continue; + if (!tdefp->attrPublic()) continue; + const AstEnumDType* const edtypep + = VN_CAST(tdefp->dtypep()->skipRefToEnump(), EnumDType); + if (!edtypep) continue; + decorateFirst(first, "\n// ENUMS (that were declared public)\n"); + if (edtypep->width() > 64) { + putsDecoration("// enum " + tdefp->nameProtect() + " ignored: Too wide for C++\n"); + } else { + puts("enum " + tdefp->name() + " {\n"); + for (const AstEnumItem* itemp = edtypep->itemsp(); itemp; + itemp = VN_CAST(itemp->nextp(), EnumItem)) { + puts(itemp->nameProtect()); + puts(" = "); + iterate(itemp->valuep()); + if (VN_IS(itemp->nextp(), EnumItem)) puts(","); + puts("\n"); + } + puts("};\n"); + } + } + } + void emitFuncDecls(const AstNodeModule* modp, bool inClassBody) { + std::vector funcsp; + + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstCFunc* const funcp = VN_CAST_CONST(nodep, CFunc)) { + if (funcp->dpiImportPrototype()) // Declared in __Dpi.h + continue; + if (funcp->dpiExportDispatcher()) // Declared in __Dpi.h + continue; + if (funcp->isMethod() != inClassBody) // Only methods go inside class + continue; + if (funcp->isMethod() && funcp->isLoose()) // Loose methods are declared lazily + continue; + funcsp.push_back(funcp); + } + } + + std::stable_sort(funcsp.begin(), funcsp.end(), [](const AstNode* ap, const AstNode* bp) { + return ap->name() < bp->name(); + }); + + for (const AstCFunc* const funcp : funcsp) { + if (inClassBody) ofp()->putsPrivate(funcp->declPrivate()); + emitCFuncDecl(funcp, modp); + } + } + void emitAll(const AstNodeModule* modp) { + // Include files required by this AstNodeModule + if (const AstClass* const classp = VN_CAST_CONST(modp, Class)) { + if (classp->extendsp()) + puts("#include \"" + + prefixNameProtect(classp->extendsp()->classp()->classOrPackagep()) + + ".h\"\n"); + } + emitModCUse(modp, VUseType::INT_INCLUDE); + + // Forward declarations required by this AstNodeModule + puts("\nclass " + symClassName() + ";\n"); + emitModCUse(modp, VUseType::INT_FWD_CLASS); + + // From `systemc_header + emitTextSection(modp, AstType::atScHdr); + + // Open class body {{{ + if (const AstClass* const classp = VN_CAST_CONST(modp, Class)) { + puts("class "); + puts(prefixNameProtect(modp)); + if (classp->extendsp()) { + puts(" : public "); + puts(prefixNameProtect(classp->extendsp()->classp())); + } + } else { + puts("VL_MODULE(" + prefixNameProtect(modp) + ")"); + } + puts(" {\n"); + ofp()->resetPrivate(); + ofp()->putsPrivate(false); // public: + + // Emit all class body contents + emitCellDecls(modp); + emitEnums(modp); + emitDesignVarDecls(modp); + emitInternalVarDecls(modp); + emitParamDecls(modp); + emitCtorDtorDecls(modp); + emitInternalMethodDecls(modp); + emitFuncDecls(modp, /* inClassBody: */ true); + + // From `systemc_interface + emitTextSection(modp, AstType::atScInt); + + // Close class body + if (!VN_IS(modp, Class)) { + puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n"); + } else { + puts("};\n"); + } + // }}} + + // Emit out of class function declarations + puts("\n"); + emitFuncDecls(modp, /* inClassBody: */ false); + } + + explicit EmitCHeader(const AstNodeModule* modp) { + UINFO(5, " Emitting header for " << prefixNameProtect(modp) << endl); + + // Open output file + const string filename = v3Global.opt.makeDir() + "/" + prefixNameProtect(modp) + ".h"; + newCFile(filename, /* slow: */ false, /* source: */ false); + m_ofp = v3Global.opt.systemC() ? new V3OutScFile(filename) : new V3OutCFile(filename); + + ofp()->putsHeader(); + puts("// DESCRIPTION: Verilator output: Design internal header\n"); + puts("// See " + topClassName() + ".h for the primary calling header\n"); + + ofp()->putsGuard(); + + // Include files + puts("\n"); + ofp()->putsIntTopInclude(); + puts("#include \"verilated.h\"\n"); + if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n"); + if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n"); + if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n"); + + emitAll(modp); + + if (const AstClassPackage* const packagep = VN_CAST_CONST(modp, ClassPackage)) { + // Put the non-static class implementation in same h file for speed + emitAll(packagep->classp()); + } + + ofp()->putsEndGuard(); + + // Close output file + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + } + virtual ~EmitCHeader() override = default; + +public: + static void main(const AstNodeModule* modp) { EmitCHeader emitCHeader(modp); } +}; + +//###################################################################### +// EmitC class functions + +void V3EmitC::emitcHeaders() { + UINFO(2, __FUNCTION__ << ": " << endl); + + // Process each module in turn + for (const AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) { + if (VN_IS(nodep, Class)) continue; // Declared with the ClassPackage + EmitCHeader::main(VN_CAST_CONST(nodep, NodeModule)); + } +} diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp new file mode 100644 index 000000000..03b84a273 --- /dev/null +++ b/src/V3EmitCImp.cpp @@ -0,0 +1,914 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Emit C++ for tree +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Global.h" +#include "V3EmitC.h" +#include "V3Ast.h" +#include "V3EmitCFunc.h" +#include "V3String.h" +#include "V3UniqueNames.h" + +#include +#include +#include + +//###################################################################### +// Visitor that gathers the headers required by an AstCFunc + +class EmitCGatherDependencies final : AstNVisitor { + // Ordered set, as it is used as a key in another map. + std::set m_dependencies; // Header names to be included in output C++ file + + // METHODS + void addSymsDependency() { m_dependencies.insert(EmitCBaseVisitor::symClassName()); } + void addModDependency(const AstNodeModule* modp) { + if (const AstClass* const classp = VN_CAST_CONST(modp, Class)) { + m_dependencies.insert(EmitCBaseVisitor::prefixNameProtect(classp->classOrPackagep())); + } else { + m_dependencies.insert(EmitCBaseVisitor::prefixNameProtect(modp)); + } + } + void addDTypeDependency(const AstNodeDType* nodep) { + if (const AstClassRefDType* const dtypep = VN_CAST_CONST(nodep, ClassRefDType)) { + m_dependencies.insert( + EmitCBaseVisitor::prefixNameProtect(dtypep->classp()->classOrPackagep())); + } + } + void addSelfDependency(const string& selfPointer, AstNode* nodep) { + if (selfPointer.empty()) { + // No self pointer (e.g.: function locals, const pool values, loose static methods), + // so no dependency + } else if (VString::startsWith(selfPointer, "this")) { + // Dereferencing 'this', we need the definition of this module, which is also the + // module that contains the variable. + addModDependency(EmitCParentModule::get(nodep)); + } else { + // Must be an absolute reference + UASSERT_OBJ(selfPointer.find("vlSymsp") != string::npos, nodep, + "Unknown self pointer: '" << selfPointer << "'"); + // Dereferencing vlSymsp, so we need it's definition... + m_dependencies.insert(EmitCBaseVisitor::symClassName()); + } + } + + // VISITORS + virtual void visit(AstCCall* nodep) override { + addSelfDependency(nodep->selfPointer(), nodep->funcp()); + iterateChildrenConst(nodep); + } + virtual void visit(AstCNew* nodep) override { + addDTypeDependency(nodep->dtypep()); + iterateChildrenConst(nodep); + } + virtual void visit(AstCMethodCall* nodep) override { + addDTypeDependency(nodep->fromp()->dtypep()); + iterateChildrenConst(nodep); + } + virtual void visit(AstNewCopy* nodep) override { + addDTypeDependency(nodep->dtypep()); + iterateChildrenConst(nodep); + } + virtual void visit(AstMemberSel* nodep) override { + addDTypeDependency(nodep->fromp()->dtypep()); + iterateChildrenConst(nodep); + } + virtual void visit(AstNodeVarRef* nodep) override { + addSelfDependency(nodep->selfPointer(), nodep->varp()); + iterateChildrenConst(nodep); + } + virtual void visit(AstCoverDecl* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstCoverInc* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstDumpCtl* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstScopeName* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstPrintTimeScale* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstTimeFormat* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstNodeSimpleText* nodep) override { + if (nodep->text().find("vlSymsp") != string::npos) { + m_dependencies.insert(EmitCBaseVisitor::symClassName()); + } + iterateChildrenConst(nodep); + } + virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } + + // CONSTRUCTOR + explicit EmitCGatherDependencies(AstCFunc* cfuncp) { + // Strictly speaking, for loose methods, we could get away with just a forward + // declaration of the receiver class, but their body very likely includes at least one + // relative reference, so we are probably not loosing much. + addModDependency(EmitCParentModule::get(cfuncp)); + iterate(cfuncp); + } + +public: + static const std::set gather(AstCFunc* cfuncp) { + EmitCGatherDependencies visitor{cfuncp}; + return std::move(visitor.m_dependencies); + } +}; + +//###################################################################### +// Internal EmitC implementation + +class EmitCImp final : EmitCFunc { + // MEMBERS + const AstNodeModule* const m_fileModp; // Files names/headers constructed using this module + const bool m_slow; // Creating __Slow file + const std::set* m_requiredHeadersp; // Header files required by output file + std::string m_subFileName; // substring added to output filenames + V3UniqueNames m_uniqueNames; // For generating unique file names + + // METHODS + void openNextOutputFile(const std::set& headers, const string& subFileName) { + UASSERT(!m_ofp, "Output file already open"); + + splitSizeReset(); // Reset file size tracking + m_lazyDecls.reset(); // Need to emit new lazy declarations + + if (v3Global.opt.lintOnly()) { + // Unfortunately we have some lint checks here, so we can't just skip processing. + // We should move them to a different stage. + const string filename = VL_DEV_NULL; + newCFile(filename, /* slow: */ m_slow, /* source: */ true); + m_ofp = new V3OutCFile(filename); + } else { + string filename = v3Global.opt.makeDir() + "/" + prefixNameProtect(m_fileModp); + if (!subFileName.empty()) { + filename += "__" + subFileName; + filename = m_uniqueNames.get(filename); + } + if (m_slow) filename += "__Slow"; + filename += ".cpp"; + newCFile(filename, /* slow: */ m_slow, /* source: */ true); + m_ofp = v3Global.opt.systemC() ? new V3OutScFile(filename) : new V3OutCFile(filename); + } + + ofp()->putsHeader(); + puts("// DESCRIPTION: Verilator output: Design implementation internals\n"); + puts("// See " + topClassName() + ".h for the primary calling header\n"); + + // Include files + puts("\n#include \"verilated.h\"\n"); + if (v3Global.dpi()) puts("#include \"verilated_dpi.h\"\n"); + puts("\n"); + for (const string& name : headers) puts("#include \"" + name + ".h\"\n"); + + emitTextSection(m_modp, AstType::atScImpHdr); + } + + void emitStaticVarDefns(const AstNodeModule* modp) { + // Emit static variable definitions + const string modName = prefixNameProtect(modp); + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { + if (varp->isStatic()) { + puts(varp->vlArgType(true, false, false, modName)); + puts(";\n"); + } + } + } + } + void emitParamDefns(const AstNodeModule* modp) { + const string modName = prefixNameProtect(modp); + bool first = true; + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { + if (varp->isParam()) { + if (first) { + puts("\n"); + putsDecoration("// Parameter definitions for " + modName + "\n"); + first = false; + } + UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?"); + // Only C++ LiteralTypes can be constexpr + const bool canBeConstexpr = varp->dtypep()->isLiteralType(); + puts(canBeConstexpr ? "constexpr " : "const "); + const string scopedName = modName + "::" + varp->nameProtect(); + puts(varp->dtypep()->cType(scopedName, false, false)); + if (!canBeConstexpr) { + puts(" = "); + emitConstInit(varp->valuep()); + } + puts(";\n"); + } + } + } + if (!first) puts("\n"); + } + void emitCtorImp(const AstNodeModule* modp) { + const string modName = prefixNameProtect(modp); + + puts("\n"); + m_lazyDecls.emit("void " + modName + "__", protect("_ctor_var_reset"), + "(" + modName + "* vlSelf);"); + puts("\n"); + + puts(modName + "::" + modName + "(const char* _vcname__)\n"); + puts(" : VerilatedModule(_vcname__)\n"); + + ofp()->indentInc(); + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { + if (const AstBasicDType* const dtypep + = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) { + if (dtypep->keyword().isMTaskState()) { + puts(", "); + puts(varp->nameProtect()); + puts("("); + iterate(varp->valuep()); + puts(")\n"); + } else if (varp->isIO() && varp->isSc()) { + puts(", "); + puts(varp->nameProtect()); + puts("("); + putsQuoted(varp->nameProtect()); + puts(")\n"); + } + } + } + } + ofp()->indentDec(); + + puts(" {\n"); + + putsDecoration("// Reset structure values\n"); + puts(modName + "__" + protect("_ctor_var_reset") + "(this);\n"); + emitTextSection(modp, AstType::atScCtor); + + puts("}\n"); + } + void emitConfigureImp(const AstNodeModule* modp) { + const string modName = prefixNameProtect(modp); + + if (v3Global.opt.coverage()) { + puts("\n"); + m_lazyDecls.emit("void " + modName + "__", protect("_configure_coverage"), + "(" + modName + "* vlSelf, bool first);"); + } + + puts("\nvoid " + modName + "::" + protect("__Vconfigure") + "(" + symClassName() + + "* _vlSymsp, bool first) {\n"); + puts("if (false && first) {} // Prevent unused\n"); + puts("this->vlSymsp = _vlSymsp;\n"); // First, as later stuff needs it. + if (v3Global.opt.coverage()) { + puts(modName + "__" + protect("_configure_coverage") + "(this, first);\n"); + } + puts("}\n"); + splitSizeInc(10); + } + void emitCoverageImp() { + if (v3Global.opt.coverage()) { + puts("\n// Coverage\n"); + // Rather than putting out VL_COVER_INSERT calls directly, we do it via this + // function. This gets around gcc slowness constructing all of the template + // arguments. + puts("void " + prefixNameProtect(m_modp) + "::__vlCoverInsert("); + puts(v3Global.opt.threads() ? "std::atomic" : "uint32_t"); + puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); + puts("const char* hierp, const char* pagep, const char* commentp, const char* " + "linescovp) " + "{\n"); + if (v3Global.opt.threads()) { + puts("assert(sizeof(uint32_t) == sizeof(std::atomic));\n"); + puts("uint32_t* count32p = reinterpret_cast(countp);\n"); + } else { + puts("uint32_t* count32p = countp;\n"); + } + // static doesn't need save-restore as is constant + puts("static uint32_t fake_zero_count = 0;\n"); + // Used for second++ instantiation of identical bin + puts("if (!enable) count32p = &fake_zero_count;\n"); + puts("*count32p = 0;\n"); + puts("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), count32p,"); + puts(" \"filename\",filenamep,"); + puts(" \"lineno\",lineno,"); + puts(" \"column\",column,\n"); + // Need to move hier into scopes and back out if do this + // puts( "\"hier\",std::string(vlSymsp->name())+hierp,"); + puts("\"hier\",std::string(name())+hierp,"); + puts(" \"page\",pagep,"); + puts(" \"comment\",commentp,"); + puts(" (linescovp[0] ? \"linescov\" : \"\"), linescovp);\n"); + puts("}\n"); + splitSizeInc(10); + } + } + void emitDestructorImp(const AstNodeModule* modp) { + puts("\n"); + puts(prefixNameProtect(modp) + "::~" + prefixNameProtect(modp) + "() {\n"); + emitTextSection(modp, AstType::atScDtor); + puts("}\n"); + splitSizeInc(10); + } + void emitSavableImp(const AstNodeModule* modp) { + if (v3Global.opt.savable()) { + puts("\n// Savable\n"); + for (int de = 0; de < 2; ++de) { + const string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; + const string funcname = de ? "__Vdeserialize" : "__Vserialize"; + const string op = de ? ">>" : "<<"; + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + puts("void " + prefixNameProtect(modp) + "::" + protect(funcname) + "(" + classname + + "& os) {\n"); + // Place a computed checksum to ensure proper structure save/restore formatting + // OK if this hash includes some things we won't dump, since + // just looking for loading the wrong model + VHashSha256 hash; + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* varp = VN_CAST(nodep, Var)) { + hash.insert(varp->name()); + hash.insert(varp->dtypep()->width()); + } + } + ofp()->printf("vluint64_t __Vcheckval = 0x%" VL_PRI64 "xULL;\n", + static_cast(hash.digestUInt64())); + if (de) { + puts("os.readAssert(__Vcheckval);\n"); + } else { + 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 + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* varp = VN_CAST(nodep, Var)) { + if (varp->isIO() && modp->isTop() && optSystemC()) { + // System C top I/O doesn't need loading, as the + // lower level subinst code does it. + } else if (varp->isParam()) { + } else if (varp->isStatic() && varp->isConst()) { + } else { + int vects = 0; + AstNodeDType* elementp = varp->dtypeSkipRefp(); + for (AstUnpackArrayDType* arrayp = VN_CAST(elementp, UnpackArrayDType); + arrayp; arrayp = VN_CAST(elementp, UnpackArrayDType)) { + const int vecnum = vects++; + UASSERT_OBJ(arrayp->hi() >= arrayp->lo(), varp, + "Should have swapped msb & lsb earlier."); + const string ivar = string("__Vi") + cvtToStr(vecnum); + puts("for (int __Vi" + cvtToStr(vecnum) + "=" + cvtToStr(0)); + puts("; " + ivar + "<" + cvtToStr(arrayp->elementsConst())); + puts("; ++" + ivar + ") {\n"); + elementp = arrayp->subDTypep()->skipRefp(); + } + const AstBasicDType* const basicp = elementp->basicp(); + // Do not save MTask state, only matters within an evaluation + if (basicp && basicp->keyword().isMTaskState()) continue; + // Want to detect types that are represented as arrays + // (i.e. packed types of more than 64 bits). + if (elementp->isWide() + && !(basicp && basicp->keyword() == AstBasicDTypeKwd::STRING)) { + const int vecnum = vects++; + const string ivar = string("__Vi") + cvtToStr(vecnum); + puts("for (int __Vi" + cvtToStr(vecnum) + "=" + cvtToStr(0)); + puts("; " + ivar + "<" + cvtToStr(elementp->widthWords())); + puts("; ++" + ivar + ") {\n"); + } + puts("os" + op + varp->nameProtect()); + for (int v = 0; v < vects; ++v) puts("[__Vi" + cvtToStr(v) + "]"); + puts(";\n"); + for (int v = 0; v < vects; ++v) puts("}\n"); + } + } + } + + puts("}\n"); + } + } + } + // Predicate to check if we actually need to emit anything into the common implementation file. + // Used to avoid creating empty output files. + bool hasCommonImp(const AstNodeModule* modp) const { + // Nothing to emit if no module! + if (!modp) return false; + // We always need the slow file + if (m_slow) return true; + // The fast file is only required when we have ScImp nodes + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (VN_IS(nodep, ScImp)) return true; + } + return false; + } + // Actually emit common implementation contents for given AstNodeModule + void doCommonImp(const AstNodeModule* modp) { + if (m_slow) { + emitStaticVarDefns(modp); + if (!VN_IS(modp, Class)) { + emitParamDefns(modp); + emitCtorImp(modp); + emitConfigureImp(modp); + emitDestructorImp(modp); + } + emitSavableImp(modp); + emitCoverageImp(); + } else { + // From `systemc_implementation + emitTextSection(modp, AstType::atScImp); + } + } + void emitCommonImp(const AstNodeModule* modp) { + const AstClass* const classp + = VN_IS(modp, ClassPackage) ? VN_CAST_CONST(modp, ClassPackage)->classp() : nullptr; + + if (hasCommonImp(modp) || hasCommonImp(classp)) { + std::set headers; + headers.insert(prefixNameProtect(m_fileModp)); + headers.insert(symClassName()); + + openNextOutputFile(headers, ""); + + doCommonImp(modp); + if (classp) { + VL_RESTORER(m_modp); + m_modp = classp; + doCommonImp(classp); + } + + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + } + } + void emitCFuncImp(const AstNodeModule* modp) { + // Partition functions based on which module definitions they require, by building a + // map from "AstNodeModules whose definitions are required" -> "functions that need + // them" + std::map, std::vector> depSet2funcps; + + const auto gather = [this, &depSet2funcps](const AstNodeModule* modp) { + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) { + // TRACE_* and DPI handled elsewhere + if (funcp->isTrace()) continue; + if (funcp->dpiImportPrototype()) continue; + if (funcp->dpiExportDispatcher()) continue; + if (funcp->slow() != m_slow) continue; + const auto& depSet = EmitCGatherDependencies::gather(funcp); + depSet2funcps[depSet].push_back(funcp); + } + } + }; + + gather(modp); + if (const AstClassPackage* const packagep = VN_CAST_CONST(modp, ClassPackage)) { + gather(packagep->classp()); + } + + // Emit all functions in each dependency set into separate files + for (const auto& pair : depSet2funcps) { + m_requiredHeadersp = &pair.first; + // Compute the hash of the dependencies, so we can add it to the filenames to + // disambiguate them + V3Hash hash; + for (const string& name : *m_requiredHeadersp) { hash += name; } + m_subFileName = "DepSet_" + hash.toString(); + // Open output file + openNextOutputFile(*m_requiredHeadersp, m_subFileName); + // Emit functions in this dependency set + for (AstCFunc* const funcp : pair.second) { + VL_RESTORER(m_modp); + m_modp = EmitCParentModule::get(funcp); + iterate(funcp); + } + // Close output file + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + } + } + + // VISITORS + virtual void visit(AstCFunc* nodep) override { + if (splitNeeded()) { + // Splitting file, so using parallel build. + v3Global.useParallelBuild(true); + // Close old file + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + // Open a new file + openNextOutputFile(*m_requiredHeadersp, m_subFileName); + } + + EmitCFunc::visit(nodep); + } + + explicit EmitCImp(const AstNodeModule* modp, bool slow) + : m_fileModp{modp} + , m_slow{slow} { + UINFO(5, " Emitting implementation of " << prefixNameProtect(modp) << endl); + + m_modp = modp; + + // Emit implementation of this module, if this is an AstClassPackage, then put the + // corresponding AstClass implementation in the same file as often optimziations are + // possible when both are seen by the compiler + // TODO: is the above comment still true? + + // Emit implementations of common parts + emitCommonImp(modp); + + // Emit implementations of all AstCFunc + emitCFuncImp(modp); + } + virtual ~EmitCImp() override = default; + +public: + static void main(const AstNodeModule* modp, bool slow) { EmitCImp{modp, slow}; } +}; + +//###################################################################### +// Tracing routines + +class EmitCTrace final : EmitCFunc { + // NODE STATE/TYPES + // Cleared on netlist + // AstNode::user1() -> int. Enum number + AstUser1InUse m_inuser1; + + // MEMBERS + const bool m_slow; // Making slow file + int m_enumNum = 0; // Enumeration number (whole netlist) + V3UniqueNames m_uniqueNames; // For generating unique file names + + // METHODS + void openNextOutputFile() { + UASSERT(!m_ofp, "Output file already open"); + + splitSizeReset(); // Reset file size tracking + m_lazyDecls.reset(); // Need to emit new lazy declarations + + string filename + = (v3Global.opt.makeDir() + "/" + topClassName() + "_" + protect("_Trace")); + filename = m_uniqueNames.get(filename); + if (m_slow) filename += "__Slow"; + filename += ".cpp"; + + AstCFile* cfilep = newCFile(filename, m_slow, true /*source*/); + cfilep->support(true); + + if (optSystemC()) { + m_ofp = new V3OutScFile(filename); + } else { + m_ofp = new V3OutCFile(filename); + } + m_ofp->putsHeader(); + m_ofp->puts("// DESCR" + "IPTION: Verilator output: Tracing implementation internals\n"); + + // Includes + puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n"); + puts("#include \"" + symClassName() + ".h\"\n"); + puts("\n"); + } + + bool emitTraceIsScBv(AstTraceInc* nodep) { + const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef); + if (!varrefp) return false; + AstVar* varp = varrefp->varp(); + return varp->isSc() && varp->isScBv(); + } + + bool emitTraceIsScBigUint(AstTraceInc* nodep) { + const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef); + if (!varrefp) return false; + AstVar* varp = varrefp->varp(); + return varp->isSc() && varp->isScBigUint(); + } + + bool emitTraceIsScUint(AstTraceInc* nodep) { + const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef); + if (!varrefp) return false; + AstVar* varp = varrefp->varp(); + return varp->isSc() && varp->isScUint(); + } + + void emitTraceInitOne(AstTraceDecl* nodep, int enumNum) { + if (nodep->dtypep()->basicp()->isDouble()) { + puts("tracep->declDouble"); + } else if (nodep->isWide()) { + puts("tracep->declArray"); + } else if (nodep->isQuad()) { + puts("tracep->declQuad"); + } else if (nodep->bitRange().ranged()) { + puts("tracep->declBus"); + } else { + puts("tracep->declBit"); + } + + puts("(c+" + cvtToStr(nodep->code())); + if (nodep->arrayRange().ranged()) puts("+i*" + cvtToStr(nodep->widthWords())); + puts(","); + if (nodep->isScoped()) puts("Verilated::catName(scopep,"); + putsQuoted(VIdProtect::protectWordsIf(nodep->showname(), nodep->protect())); + if (nodep->isScoped()) puts(",(int)scopet,\" \")"); + // Direction + if (v3Global.opt.traceFormat().fst()) { + puts("," + cvtToStr(enumNum)); + // fstVarDir + if (nodep->declDirection().isInoutish()) { + puts(",FST_VD_INOUT"); + } else if (nodep->declDirection().isWritable()) { + puts(",FST_VD_OUTPUT"); + } else if (nodep->declDirection().isNonOutput()) { + puts(",FST_VD_INPUT"); + } else { + puts(", FST_VD_IMPLICIT"); + } + // + // fstVarType + const AstVarType vartype = nodep->varType(); + const AstBasicDTypeKwd kwd = nodep->declKwd(); + string fstvt; + // Doubles have special decoding properties, so must indicate if a double + if (nodep->dtypep()->basicp()->isDouble()) { + if (vartype == AstVarType::GPARAM || vartype == AstVarType::LPARAM) { + fstvt = "FST_VT_VCD_REAL_PARAMETER"; + } else { + fstvt = "FST_VT_VCD_REAL"; + } + } + // clang-format off + else if (vartype == AstVarType::GPARAM) { fstvt = "FST_VT_VCD_PARAMETER"; } + else if (vartype == AstVarType::LPARAM) { fstvt = "FST_VT_VCD_PARAMETER"; } + else if (vartype == AstVarType::SUPPLY0) { fstvt = "FST_VT_VCD_SUPPLY0"; } + else if (vartype == AstVarType::SUPPLY1) { fstvt = "FST_VT_VCD_SUPPLY1"; } + else if (vartype == AstVarType::TRI0) { fstvt = "FST_VT_VCD_TRI0"; } + else if (vartype == AstVarType::TRI1) { fstvt = "FST_VT_VCD_TRI1"; } + else if (vartype == AstVarType::TRIWIRE) { fstvt = "FST_VT_VCD_TRI"; } + else if (vartype == AstVarType::WIRE) { fstvt = "FST_VT_VCD_WIRE"; } + else if (vartype == AstVarType::PORT) { fstvt = "FST_VT_VCD_WIRE"; } + // + else if (kwd == AstBasicDTypeKwd::INTEGER) { fstvt = "FST_VT_VCD_INTEGER"; } + else if (kwd == AstBasicDTypeKwd::BIT) { fstvt = "FST_VT_SV_BIT"; } + else if (kwd == AstBasicDTypeKwd::LOGIC) { fstvt = "FST_VT_SV_LOGIC"; } + else if (kwd == AstBasicDTypeKwd::INT) { fstvt = "FST_VT_SV_INT"; } + else if (kwd == AstBasicDTypeKwd::SHORTINT) { fstvt = "FST_VT_SV_SHORTINT"; } + else if (kwd == AstBasicDTypeKwd::LONGINT) { fstvt = "FST_VT_SV_LONGINT"; } + else if (kwd == AstBasicDTypeKwd::BYTE) { fstvt = "FST_VT_SV_BYTE"; } + else { fstvt = "FST_VT_SV_BIT"; } + // clang-format on + // + // Not currently supported + // FST_VT_VCD_EVENT + // FST_VT_VCD_PORT + // FST_VT_VCD_SHORTREAL + // FST_VT_VCD_REALTIME + // FST_VT_VCD_SPARRAY + // FST_VT_VCD_TRIAND + // FST_VT_VCD_TRIOR + // FST_VT_VCD_TRIREG + // FST_VT_VCD_WAND + // FST_VT_VCD_WOR + // FST_VT_SV_ENUM + // FST_VT_GEN_STRING + puts("," + fstvt); + } + // Range + if (nodep->arrayRange().ranged()) { + puts(", true,(i+" + cvtToStr(nodep->arrayRange().lo()) + ")"); + } else { + puts(", false,-1"); + } + if (!nodep->dtypep()->basicp()->isDouble() && nodep->bitRange().ranged()) { + puts(", " + cvtToStr(nodep->bitRange().left()) + "," + + cvtToStr(nodep->bitRange().right())); + } + puts(");"); + } + + int emitTraceDeclDType(AstNodeDType* nodep) { + // Return enum number or -1 for none + if (v3Global.opt.traceFormat().fst()) { + // Skip over refs-to-refs, but stop before final ref so can get data type name + // Alternatively back in V3Width we could push enum names from upper typedefs + if (AstEnumDType* enump = VN_CAST(nodep->skipRefToEnump(), EnumDType)) { + int enumNum = enump->user1(); + if (!enumNum) { + enumNum = ++m_enumNum; + enump->user1(enumNum); + int nvals = 0; + puts("{\n"); + puts("const char* " + protect("__VenumItemNames") + "[]\n"); + puts("= {"); + for (AstEnumItem* itemp = enump->itemsp(); itemp; + itemp = VN_CAST(itemp->nextp(), EnumItem)) { + if (++nvals > 1) puts(", "); + putbs("\"" + itemp->prettyName() + "\""); + } + puts("};\n"); + nvals = 0; + puts("const char* " + protect("__VenumItemValues") + "[]\n"); + puts("= {"); + for (AstEnumItem* itemp = enump->itemsp(); itemp; + itemp = VN_CAST(itemp->nextp(), EnumItem)) { + AstConst* constp = VN_CAST(itemp->valuep(), Const); + if (++nvals > 1) puts(", "); + putbs("\"" + constp->num().displayed(nodep, "%0b") + "\""); + } + puts("};\n"); + puts("tracep->declDTypeEnum(" + cvtToStr(enumNum) + ", \"" + + enump->prettyName() + "\", " + cvtToStr(nvals) + ", " + + cvtToStr(enump->widthMin()) + ", " + protect("__VenumItemNames") + ", " + + protect("__VenumItemValues") + ");\n"); + puts("}\n"); + } + return enumNum; + } + } + return -1; + } + + void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) { + iterateAndNextNull(nodep->precondsp()); + const string func = nodep->full() ? "full" : "chg"; + bool emitWidth = true; + if (nodep->dtypep()->basicp()->isDouble()) { + puts("tracep->" + func + "Double"); + emitWidth = false; + } else if (nodep->isWide() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep)) { + puts("tracep->" + func + "WData"); + } else if (nodep->isQuad()) { + puts("tracep->" + func + "QData"); + } else if (nodep->declp()->widthMin() > 16) { + puts("tracep->" + func + "IData"); + } else if (nodep->declp()->widthMin() > 8) { + puts("tracep->" + func + "SData"); + } else if (nodep->declp()->widthMin() > 1) { + puts("tracep->" + func + "CData"); + } else { + puts("tracep->" + func + "Bit"); + emitWidth = false; + } + + const uint32_t offset = (arrayindex < 0) ? 0 : (arrayindex * nodep->declp()->widthWords()); + const uint32_t code = nodep->declp()->code() + offset; + puts(v3Global.opt.trueTraceThreads() && !nodep->full() ? "(base+" : "(oldp+"); + puts(cvtToStr(code - nodep->baseCode())); + puts(","); + emitTraceValue(nodep, arrayindex); + if (emitWidth) puts("," + cvtToStr(nodep->declp()->widthMin())); + puts(");\n"); + } + + void emitTraceValue(AstTraceInc* nodep, int arrayindex) { + if (AstVarRef* const varrefp = VN_CAST(nodep->valuep(), VarRef)) { + AstVar* varp = varrefp->varp(); + puts("("); + if (emitTraceIsScBigUint(nodep)) { + puts("(vluint32_t*)"); + } else if (emitTraceIsScBv(nodep)) { + puts("VL_SC_BV_DATAP("); + } + iterate(varrefp); // Put var name out + // Tracing only supports 1D arrays + if (nodep->declp()->arrayRange().ranged()) { + if (arrayindex == -2) { + puts("[i]"); + } else if (arrayindex == -1) { + puts("[0]"); + } else { + puts("[" + cvtToStr(arrayindex) + "]"); + } + } + if (varp->isSc()) puts(".read()"); + if (emitTraceIsScUint(nodep)) { + puts(nodep->isQuad() ? ".to_uint64()" : ".to_uint()"); + } else if (emitTraceIsScBigUint(nodep)) { + puts(".get_raw()"); + } else if (emitTraceIsScBv(nodep)) { + puts(")"); + } + puts(")"); + } else { + puts("("); + iterate(nodep->valuep()); + puts(")"); + } + } + + // VISITORS + using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning + virtual void visit(AstCFunc* nodep) override { + if (!nodep->isTrace()) return; + if (nodep->slow() != m_slow) return; + + if (splitNeeded()) { + // Splitting file, so using parallel build. + v3Global.useParallelBuild(true); + // Close old file + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + // Open a new file + openNextOutputFile(); + } + + EmitCFunc::visit(nodep); + } + virtual void visit(AstTraceDecl* nodep) override { + const int enumNum = emitTraceDeclDType(nodep->dtypep()); + if (nodep->arrayRange().ranged()) { + puts("{int i; for (i=0; i<" + cvtToStr(nodep->arrayRange().elements()) + "; i++) {\n"); + emitTraceInitOne(nodep, enumNum); + puts("}}\n"); + } else { + emitTraceInitOne(nodep, enumNum); + puts("\n"); + } + } + virtual void visit(AstTraceInc* nodep) override { + if (nodep->declp()->arrayRange().ranged()) { + // It traces faster if we unroll the loop + for (int i = 0; i < nodep->declp()->arrayRange().elements(); i++) { + emitTraceChangeOne(nodep, i); + } + } else { + emitTraceChangeOne(nodep, -1); + } + } + + explicit EmitCTrace(AstNodeModule* modp, bool slow) + : m_slow{slow} { + m_modp = modp; + // Open output file + openNextOutputFile(); + // Emit functions + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) { iterate(funcp); } + } + // Close output file + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + } + virtual ~EmitCTrace() override = default; + +public: + static void main(AstNodeModule* modp, bool slow) { EmitCTrace{modp, slow}; } +}; + +//###################################################################### +// EmitC class functions + +void V3EmitC::emitcImp() { + UINFO(2, __FUNCTION__ << ": " << endl); + // Make parent module pointers available. + EmitCParentModule emitCParentModule; + + // Process each module in turn + for (const AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) { + if (VN_IS(nodep, Class)) continue; // Imped with ClassPackage + const AstNodeModule* const modp = VN_CAST_CONST(nodep, NodeModule); + EmitCImp::main(modp, /* slow: */ true); + EmitCImp::main(modp, /* slow: */ false); + } + + // Emit trace routines (currently they can only exist in the top module) + if (v3Global.opt.trace() && !v3Global.opt.lintOnly()) { + EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true); + EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false); + } +} + +void V3EmitC::emitcFiles() { + UINFO(2, __FUNCTION__ << ": " << endl); + for (AstNodeFile* filep = v3Global.rootp()->filesp(); filep; + filep = VN_CAST(filep->nextp(), NodeFile)) { + AstCFile* cfilep = VN_CAST(filep, CFile); + if (cfilep && cfilep->tblockp()) { + V3OutCFile of(cfilep->name()); + of.puts("// DESCR" + "IPTION: Verilator generated C++\n"); + EmitCFunc visitor(cfilep->tblockp(), &of, true); + } + } +} diff --git a/src/V3EmitCMain.cpp b/src/V3EmitCMain.cpp index 493aca407..b0475fe40 100644 --- a/src/V3EmitCMain.cpp +++ b/src/V3EmitCMain.cpp @@ -35,14 +35,14 @@ class EmitCMain final : EmitCBaseVisitor { public: // CONSTRUCTORS - explicit EmitCMain(AstNetlist*) { emitInt(); } + EmitCMain() { emitInt(); } private: // MAIN METHOD void emitInt() { const string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__main.cpp"; newCFile(filename, false /*slow*/, true /*source*/); - V3OutCFile cf(filename); + V3OutCFile cf{filename}; m_ofp = &cf; // Not defining main_time/vl_time_stamp, so @@ -93,6 +93,8 @@ private: puts("topp->final();\n"); puts("return 0;\n"); puts("}\n"); + + m_ofp = nullptr; } }; @@ -101,5 +103,5 @@ private: void V3EmitCMain::emit() { UINFO(2, __FUNCTION__ << ": " << endl); - EmitCMain(v3Global.rootp()); + { EmitCMain visitor; } } diff --git a/src/V3EmitCMake.cpp b/src/V3EmitCMake.cpp index 82ea3c1f8..890634a86 100644 --- a/src/V3EmitCMake.cpp +++ b/src/V3EmitCMake.cpp @@ -78,8 +78,8 @@ class CMakeEmitter final { } static void emitOverallCMake() { - const std::unique_ptr of( - V3File::new_ofstream(v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + ".cmake")); + const std::unique_ptr of{ + V3File::new_ofstream(v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + ".cmake")}; const string name = v3Global.opt.prefix(); *of << "# Verilated -*- CMake -*-\n"; diff --git a/src/V3EmitCModel.cpp b/src/V3EmitCModel.cpp index 6b67f6fad..140f03140 100644 --- a/src/V3EmitCModel.cpp +++ b/src/V3EmitCModel.cpp @@ -20,11 +20,13 @@ #include "V3Global.h" #include "V3EmitC.h" #include "V3EmitCFunc.h" +#include "V3UniqueNames.h" #include #include class EmitCModel final : public EmitCFunc { + V3UniqueNames m_uniqueNames; // For generating unique file names // METHODS VL_DEBUG_FUNC; @@ -54,7 +56,7 @@ class EmitCModel final : public EmitCFunc { // Include files puts("\n"); ofp()->putsIntTopInclude(); - puts("#include \"verilated_heavy.h\"\n"); + puts("#include \"verilated.h\"\n"); if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n"); if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n"); if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n"); @@ -90,7 +92,7 @@ class EmitCModel final : public EmitCFunc { for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { if (varp->isPrimaryIO()) { // - emitVarDecl(varp, "", /* asRef: */ true); + emitVarDecl(varp, /* asRef: */ true); } } } @@ -272,7 +274,7 @@ class EmitCModel final : public EmitCFunc { const int vecnum = vects++; UASSERT_OBJ(arrayp->hi() >= arrayp->lo(), varp, "Should have swapped msb & lsb earlier."); - const string ivar = string("__Vi") + cvtToStr(vecnum); + const string ivar = std::string{"__Vi"} + cvtToStr(vecnum); puts("for (int __Vi" + cvtToStr(vecnum) + "=" + cvtToStr(arrayp->lo())); puts("; " + ivar + "<=" + cvtToStr(arrayp->hi())); @@ -310,8 +312,10 @@ class EmitCModel final : public EmitCFunc { const string topModNameProtected = prefixNameProtect(modp); putsDecoration("// Evaluate till stable\n"); - puts("int __VclockLoop = 0;\n"); - puts("QData __Vchange = 1;\n"); + if (v3Global.rootp()->changeRequest()) { + puts("int __VclockLoop = 0;\n"); + puts("QData __Vchange = 1;\n"); + } if (v3Global.opt.trace()) puts("vlSymsp->__Vm_activity = true;\n"); puts("do {\n"); puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+ "); @@ -320,29 +324,34 @@ class EmitCModel final : public EmitCFunc { if (initial) puts(topModNameProtected + "__" + protect("_eval_settle") + "(&(vlSymsp->TOP));\n"); puts(topModNameProtected + "__" + protect("_eval") + "(&(vlSymsp->TOP));\n"); - puts("if (VL_UNLIKELY(++__VclockLoop > " + cvtToStr(v3Global.opt.convergeLimit()) - + ")) {\n"); - puts("// About to fail, so enable debug to see what's not settling.\n"); - puts("// Note you must run make with OPT=-DVL_DEBUG for debug prints.\n"); - puts("int __Vsaved_debug = Verilated::debug();\n"); - puts("Verilated::debug(1);\n"); - puts("__Vchange = " + topModNameProtected + "__" + protect("_change_request") - + "(&(vlSymsp->TOP));\n"); - puts("Verilated::debug(__Vsaved_debug);\n"); - puts("VL_FATAL_MT("); - putsQuoted(protect(modp->fileline()->filename())); - puts(", "); - puts(cvtToStr(modp->fileline()->lineno())); - puts(", \"\",\n"); - puts("\"Verilated model didn't "); - if (initial) puts("DC "); - puts("converge\\n\"\n"); - puts("\"- See https://verilator.org/warn/DIDNOTCONVERGE\");\n"); - puts("} else {\n"); - puts("__Vchange = " + topModNameProtected + "__" + protect("_change_request") - + "(&(vlSymsp->TOP));\n"); - puts("}\n"); - puts("} while (VL_UNLIKELY(__Vchange));\n"); + if (v3Global.rootp()->changeRequest()) { + puts("if (VL_UNLIKELY(++__VclockLoop > " + cvtToStr(v3Global.opt.convergeLimit()) + + ")) {\n"); + puts("// About to fail, so enable debug to see what's not settling.\n"); + puts("// Note you must run make with OPT=-DVL_DEBUG for debug prints.\n"); + puts("int __Vsaved_debug = Verilated::debug();\n"); + puts("Verilated::debug(1);\n"); + puts("__Vchange = " + topModNameProtected + "__" + protect("_change_request") + + "(&(vlSymsp->TOP));\n"); + puts("Verilated::debug(__Vsaved_debug);\n"); + puts("VL_FATAL_MT("); + putsQuoted(protect(modp->fileline()->filename())); + puts(", "); + puts(cvtToStr(modp->fileline()->lineno())); + puts(", \"\",\n"); + puts("\"Verilated model didn't "); + if (initial) puts("DC "); + puts("converge\\n\"\n"); + puts("\"- See https://verilator.org/warn/DIDNOTCONVERGE\");\n"); + puts("} else {\n"); + puts("__Vchange = " + topModNameProtected + "__" + protect("_change_request") + + "(&(vlSymsp->TOP));\n"); + puts("}\n"); + } + puts("} while (" + + (v3Global.rootp()->changeRequest() ? std::string{"VL_UNLIKELY(__Vchange)"} + : std::string{"0"}) + + ");\n"); } void emitStandardMethods(AstNodeModule* modp) { @@ -358,8 +367,10 @@ class EmitCModel final : public EmitCFunc { puts("void " + topModNameProtected + "__" + protect("_eval_initial") + selfDecl + ";\n"); puts("void " + topModNameProtected + "__" + protect("_eval_settle") + selfDecl + ";\n"); puts("void " + topModNameProtected + "__" + protect("_eval") + selfDecl + ";\n"); - puts("QData " + topModNameProtected + "__" + protect("_change_request") + selfDecl - + ";\n"); + if (v3Global.rootp()->changeRequest()) { + puts("QData " + topModNameProtected + "__" + protect("_change_request") + selfDecl + + ";\n"); + } puts("#ifdef VL_DEBUG\n"); puts("void " + topModNameProtected + "__" + protect("_eval_debug_assertions") + selfDecl + ";\n"); @@ -484,12 +495,12 @@ class EmitCModel final : public EmitCFunc { putSectionDelimiter("Trace configuration"); // Forward declaration - puts("\nvoid " + topModNameProtected + "__" + protect("traceInitTop") + "(" + puts("\nvoid " + topModNameProtected + "__" + protect("trace_init_top") + "(" + topModNameProtected + "* vlSelf, " + v3Global.opt.traceClassBase() + "* tracep);\n"); // Static helper function - puts("\nstatic void " + protect("traceInit") + "(void* voidSelf, " + puts("\nstatic void " + protect("trace_init") + "(void* voidSelf, " + v3Global.opt.traceClassBase() + "* tracep, uint32_t code) {\n"); putsDecoration("// Callback from tracep->open()\n"); puts(voidSelfAssign(modp)); @@ -502,20 +513,20 @@ class EmitCModel final : public EmitCFunc { puts("vlSymsp->__Vm_baseCode = code;\n"); puts("tracep->module(vlSymsp->name());\n"); puts("tracep->scopeEscape(' ');\n"); - puts(topModNameProtected + "__" + protect("traceInitTop") + "(vlSelf, tracep);\n"); + puts(topModNameProtected + "__" + protect("trace_init_top") + "(vlSelf, tracep);\n"); puts("tracep->scopeEscape('.');\n"); // Restore so later traced files won't break puts("}\n"); // Forward declaration - puts("\nvoid " + topModNameProtected + "__" + protect("traceRegister") + "(" + puts("\nvoid " + topModNameProtected + "__" + protect("trace_register") + "(" + topModNameProtected + "* vlSelf, " + v3Global.opt.traceClassBase() + "* tracep);\n"); // ::trace puts("\nvoid " + topClassName() + "::trace("); puts(v3Global.opt.traceClassBase() + "C* tfp, int, int) {\n"); - puts("tfp->spTrace()->addInitCb(&" + protect("traceInit") + ", &(vlSymsp->TOP));\n"); - puts(topModNameProtected + "__" + protect("traceRegister") + puts("tfp->spTrace()->addInitCb(&" + protect("trace_init") + ", &(vlSymsp->TOP));\n"); + puts(topModNameProtected + "__" + protect("trace_register") + "(&(vlSymsp->TOP), tfp->spTrace());\n"); puts("}\n"); } @@ -584,12 +595,13 @@ class EmitCModel final : public EmitCFunc { } if (!m_ofp) { - const string filename = v3Global.opt.makeDir() + "/" + topClassName() - + "__Dpi_Export_" + cvtToStr(splitFilenumInc() - 1) - + ".cpp"; + string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi_Export"; + filename = m_uniqueNames.get(filename); + filename += ".cpp"; newCFile(filename, /* slow: */ false, /* source: */ true); - m_ofp = v3Global.opt.systemC() ? new V3OutScFile(filename) - : new V3OutCFile(filename); + m_ofp = v3Global.opt.systemC() ? new V3OutScFile{filename} + : new V3OutCFile{filename}; + splitSizeReset(); // Reset file size tracking m_lazyDecls.reset(); m_ofp->putsHeader(); puts( diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 412773feb..fc430a369 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -389,7 +389,7 @@ void EmitCSyms::emitSymHdr() { puts("\n"); ofp()->putsIntTopInclude(); - puts("#include \"verilated_heavy.h\"\n"); + puts("#include \"verilated.h\"\n"); if (v3Global.needTraceDumper()) { puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n"); } @@ -631,7 +631,7 @@ void EmitCSyms::emitSymImp() { emitSymImpPreamble(); if (v3Global.opt.savable()) { - for (int de = 0; de < 2; ++de) { + for (const bool& de : {false, true}) { const string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; const string funcname = de ? "__Vdeserialize" : "__Vserialize"; const string op = de ? ">>" : "<<"; @@ -670,7 +670,7 @@ void EmitCSyms::emitSymImp() { + topClassName() + "* modelp)\n"); puts(" : VerilatedSyms{contextp}\n"); puts(" // Setup internal state of the Syms class\n"); - puts(" , __Vm_modelp(modelp)\n"); + puts(" , __Vm_modelp{modelp}\n"); if (v3Global.opt.mtasks()) { // TODO -- For now each model creates its own ThreadPool here, @@ -694,9 +694,9 @@ void EmitCSyms::emitSymImp() { // 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. - puts(" , __Vm_threadPoolp(new VlThreadPool(_vm_contextp__, " + puts(" , __Vm_threadPoolp{new VlThreadPool{_vm_contextp__, " + cvtToStr(v3Global.opt.threads() - 1) + ", " + cvtToStr(v3Global.opt.profThreads()) - + "))\n"); + + "}}\n"); } puts(" // Setup module instances\n"); @@ -857,13 +857,8 @@ void EmitCSyms::emitSymImp() { putsQuoted(protect(it->second.m_varBasePretty)); std::string varName; - varName += (protectIf(scopep->nameDotless(), scopep->protect()) + "."); - - if (varp->isParam()) { - varName += protect("var_" + varp->name()); - } else { - varName += protect(varp->name()); - } + varName += protectIf(scopep->nameDotless(), scopep->protect()) + "."; + varName += protect(varp->name()); if (varp->isParam()) { if (varp->vlEnumType() == "VLVT_STRING") { @@ -945,6 +940,9 @@ void EmitCSyms::emitDpiHdr() { puts("// Verilator includes this file in all generated .cpp files that use DPI functions.\n"); puts("// Manually include this file where DPI .c import functions are declared to ensure\n"); puts("// the C functions match the expectations of the DPI imports.\n"); + + ofp()->putsGuard(); + puts("\n"); puts("#include \"svdpi.h\"\n"); puts("\n"); @@ -975,6 +973,8 @@ void EmitCSyms::emitDpiHdr() { puts("#ifdef __cplusplus\n"); puts("}\n"); puts("#endif\n"); + + ofp()->putsEndGuard(); } //###################################################################### diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index 23e89fea8..b27ef2fbb 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -80,7 +80,7 @@ public: of.puts("\n### Object file lists...\n"); for (int support = 0; support < 3; ++support) { - for (int slow = 0; slow < 2; ++slow) { + for (const bool& slow : {false, true}) { if (support == 2) { of.puts("# Global classes, need linked once per executable"); } else if (support) { diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index aad3f2348..28d36222f 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -831,7 +831,7 @@ void V3EmitV::verilogForTree(AstNode* nodep, std::ostream& os) { EmitVStreamVisi void V3EmitV::verilogPrefixedTree(AstNode* nodep, std::ostream& os, const string& prefix, int flWidth, AstSenTree* domainp, bool user3mark) { - EmitVPrefixedVisitor(nodep, os, prefix, flWidth, domainp, user3mark); + EmitVPrefixedVisitor{nodep, os, prefix, flWidth, domainp, user3mark}; } void V3EmitV::emitvFiles() { @@ -843,7 +843,7 @@ void V3EmitV::emitvFiles() { V3OutVFile of(vfilep->name()); of.puts("// DESCR" "IPTION: Verilator generated Verilog\n"); - EmitVFileVisitor visitor(vfilep->tblockp(), &of, true, false); + EmitVFileVisitor visitor{vfilep->tblockp(), &of, true, false}; } } } @@ -853,5 +853,5 @@ void V3EmitV::debugEmitV(const string& stage) { const string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__" + stage + ".v"; V3OutVFile of(filename); - EmitVFileVisitor visitor(v3Global.rootp(), &of, true, true); + EmitVFileVisitor visitor{v3Global.rootp(), &of, true, true}; } diff --git a/src/V3EmitXml.cpp b/src/V3EmitXml.cpp index 1e38e5552..01af7930a 100644 --- a/src/V3EmitXml.cpp +++ b/src/V3EmitXml.cpp @@ -112,12 +112,67 @@ class EmitXmlFileVisitor final : public AstNVisitor { putsQuoted(nodep->origName()); outputChildrenEnd(nodep, "instance"); } + virtual void visit(AstNodeIf* nodep) override { + outputTag(nodep, "if"); + puts(">\n"); + iterateAndNextNull(nodep->op1p()); + puts("\n"); + iterateAndNextNull(nodep->op2p()); + puts("\n"); + if (nodep->op3p()) { + puts("\n"); + iterateAndNextNull(nodep->op3p()); + puts("\n"); + } + puts("\n"); + } + virtual void visit(AstWhile* nodep) override { + outputTag(nodep, "while"); + puts(">\n"); + puts("\n"); + iterateAndNextNull(nodep->op1p()); + puts("\n"); + if (nodep->op2p()) { + puts("\n"); + iterateAndNextNull(nodep->op2p()); + puts("\n"); + } + if (nodep->op3p()) { + puts("\n"); + iterateAndNextNull(nodep->op3p()); + puts("\n"); + } + if (nodep->op4p()) { + puts("\n"); + iterateAndNextNull(nodep->op4p()); + puts("\n"); + } + puts("\n"); + } virtual void visit(AstNetlist* nodep) override { puts("\n"); iterateChildren(nodep); puts("\n"); } - virtual void visit(AstConstPool*) override {} + virtual void visit(AstConstPool* nodep) override { + if (!v3Global.opt.xmlOnly()) { + puts("\n"); + iterateChildren(nodep); + puts("\n"); + } + } + virtual void visit(AstInitArray* nodep) override { + puts("\n"); + const AstInitArray::KeyItemMap& map = nodep->map(); + for (AstInitArray::KeyItemMap::const_iterator it = map.begin(); it != map.end(); ++it) { + puts("first)); + puts("\">\n"); + iterateChildren(it->second); + puts("\n"); + } + puts("\n"); + } virtual void visit(AstNodeModule* nodep) override { outputTag(nodep, ""); puts(" origName="); @@ -195,6 +250,12 @@ class EmitXmlFileVisitor final : public AstNVisitor { putsQuoted(nodep->dotted()); outputChildrenEnd(nodep, ""); } + virtual void visit(AstNodeCCall* nodep) override { + outputTag(nodep, ""); + puts(" func="); + putsQuoted(nodep->funcp()->name()); + outputChildrenEnd(nodep, ""); + } // Data types virtual void visit(AstBasicDType* nodep) override { @@ -203,6 +264,7 @@ class EmitXmlFileVisitor final : public AstNVisitor { puts(" left=\"" + cvtToStr(nodep->left()) + "\""); puts(" right=\"" + cvtToStr(nodep->right()) + "\""); } + if (nodep->isSigned()) { puts(" signed=\"true\""); } puts("/>\n"); } virtual void visit(AstIfaceRefDType* nodep) override { @@ -319,6 +381,7 @@ private: // VISITORS virtual void visit(AstConstPool*) override {} + virtual void visit(AstNodeModule* nodep) override { if (nodep->level() >= 0 && nodep->level() <= 2) { // ==2 because we don't add wrapper when in XML mode @@ -392,10 +455,10 @@ void V3EmitXml::emitxml() { } { std::stringstream sstr; - ModuleFilesXmlVisitor moduleFilesVisitor(v3Global.rootp(), sstr); - HierCellsXmlVisitor cellsVisitor(v3Global.rootp(), sstr); + ModuleFilesXmlVisitor moduleFilesVisitor{v3Global.rootp(), sstr}; + HierCellsXmlVisitor cellsVisitor{v3Global.rootp(), sstr}; of.puts(sstr.str()); } - EmitXmlFileVisitor visitor(v3Global.rootp(), &of); + EmitXmlFileVisitor visitor{v3Global.rootp(), &of}; of.puts("\n"); } diff --git a/src/V3Error.h b/src/V3Error.h index 870893c8e..cac812106 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -417,7 +417,11 @@ inline void v3errorEndFatal(std::ostringstream& sstr) { #define VL_DEBUG_FUNC \ static int debug() { \ static int level = -1; \ - if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); \ + if (VL_UNLIKELY(level < 0)) { \ + const int debugSrcLevel = v3Global.opt.debugSrcLevel(__FILE__); \ + if (!v3Global.opt.available()) return debugSrcLevel; \ + level = debugSrcLevel; \ + } \ return level; \ } diff --git a/src/V3Expand.cpp b/src/V3Expand.cpp index ca6353204..d62930a47 100644 --- a/src/V3Expand.cpp +++ b/src/V3Expand.cpp @@ -64,49 +64,49 @@ private: } } - int longOrQuadWidth(AstNode* nodep) { + static int longOrQuadWidth(AstNode* nodep) { return (nodep->width() + (VL_EDATASIZE - 1)) & ~(VL_EDATASIZE - 1); } - V3Number notWideMask(AstNode* nodep) { + static V3Number notWideMask(AstNode* nodep) { return V3Number(nodep, VL_EDATASIZE, ~VL_MASK_E(nodep->widthMin())); } - V3Number wordMask(AstNode* nodep) { + static V3Number wordMask(AstNode* nodep) { if (nodep->isWide()) { - return V3Number(nodep, VL_EDATASIZE, VL_MASK_E(nodep->widthMin())); + return V3Number{nodep, VL_EDATASIZE, VL_MASK_E(nodep->widthMin())}; } else { - V3Number mask(nodep, longOrQuadWidth(nodep)); + V3Number mask{nodep, longOrQuadWidth(nodep)}; mask.setMask(nodep->widthMin()); return mask; } } - void insertBefore(AstNode* placep, AstNode* newp) { + static void insertBefore(AstNode* placep, AstNode* newp) { newp->user1(1); // Already processed, don't need to re-iterate AstNRelinker linker; placep->unlinkFrBack(&linker); newp->addNext(placep); linker.relink(newp); } - void replaceWithDelete(AstNode* nodep, AstNode* newp) { + static void replaceWithDelete(AstNode* nodep, AstNode* newp) { newp->user1(1); // Already processed, don't need to re-iterate nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); } - AstNode* newWordAssign(AstNodeAssign* placep, int word, AstNode* lhsp, AstNode* rhsp) { - AstAssign* newp = new AstAssign(placep->fileline(), - new AstWordSel(placep->fileline(), lhsp->cloneTree(true), - new AstConst(placep->fileline(), word)), - rhsp); - return newp; + static AstNode* newWordAssign(AstNodeAssign* placep, int word, AstNode* lhsp, AstNode* rhsp) { + FileLine* const fl = placep->fileline(); + return new AstAssign{fl, + new AstWordSel{fl, lhsp->cloneTree(true), + new AstConst{fl, static_cast(word)}}, + rhsp}; } - void addWordAssign(AstNodeAssign* placep, int word, AstNode* lhsp, AstNode* rhsp) { + static void addWordAssign(AstNodeAssign* placep, int word, AstNode* lhsp, AstNode* rhsp) { insertBefore(placep, newWordAssign(placep, word, lhsp, rhsp)); } - void addWordAssign(AstNodeAssign* placep, int word, AstNode* rhsp) { + static void addWordAssign(AstNodeAssign* placep, int word, AstNode* rhsp) { addWordAssign(placep, word, placep->lhsp(), rhsp); } - void fixCloneLvalue(AstNode* nodep) { + static void fixCloneLvalue(AstNode* nodep) { // In AstSel transforms, we call clone() on VarRefs that were lvalues, // but are now being used on the RHS of the assignment if (VN_IS(nodep, VarRef)) VN_CAST(nodep, VarRef)->access(VAccess::READ); @@ -117,84 +117,87 @@ private: if (nodep->op4p()) fixCloneLvalue(nodep->op4p()); } - AstNode* newAstWordSelClone(AstNode* nodep, int word) { + static AstNode* newAstWordSelClone(AstNode* nodep, int word) { // Get the specified word number from a wide array // Or, if it's a long/quad, do appropriate conversion to wide // Concat may pass negative word numbers, that means it wants a zero + FileLine* const fl = nodep->fileline(); if (nodep->isWide() && word >= 0 && word < nodep->widthWords()) { - return new AstWordSel(nodep->fileline(), nodep->cloneTree(true), - new AstConst(nodep->fileline(), word)); + return new AstWordSel{fl, nodep->cloneTree(true), + new AstConst{fl, static_cast(word)}}; } else if (nodep->isQuad() && word == 0) { - AstNode* quadfromp = nodep->cloneTree(true); + AstNode* const quadfromp = nodep->cloneTree(true); quadfromp->dtypeSetBitUnsized(VL_QUADSIZE, quadfromp->widthMin(), VSigning::UNSIGNED); - return new AstCCast(nodep->fileline(), quadfromp, VL_EDATASIZE); + return new AstCCast{fl, quadfromp, VL_EDATASIZE}; } else if (nodep->isQuad() && word == 1) { - AstNode* quadfromp = nodep->cloneTree(true); + AstNode* const quadfromp = nodep->cloneTree(true); quadfromp->dtypeSetBitUnsized(VL_QUADSIZE, quadfromp->widthMin(), VSigning::UNSIGNED); - return new AstCCast(nodep->fileline(), - new AstShiftR(nodep->fileline(), quadfromp, - new AstConst(nodep->fileline(), VL_EDATASIZE), - VL_EDATASIZE), - VL_EDATASIZE); + return new AstCCast{ + fl, new AstShiftR{fl, quadfromp, new AstConst{fl, VL_EDATASIZE}, VL_EDATASIZE}, + VL_EDATASIZE}; } else if (!nodep->isWide() && !nodep->isQuad() && word == 0) { return nodep->cloneTree(true); } else { // Out of bounds - return new AstConst(nodep->fileline(), 0); + return new AstConst{fl, 0}; } } - AstNode* newWordGrabShift(FileLine* fl, int word, AstNode* lhsp, int shift) { + static AstNode* newWordGrabShift(FileLine* fl, int word, AstNode* lhsp, int shift) { // Extract the expression to grab the value for the specified word, if it's the shift // of shift bits from lhsp AstNode* newp; // Negative word numbers requested for lhs when it's "before" what we want. // We get a 0 then. const int othword = word - shift / VL_EDATASIZE; - AstNode* llowp = newAstWordSelClone(lhsp, othword); + AstNode* const llowp = newAstWordSelClone(lhsp, othword); if (const int loffset = VL_BITBIT_E(shift)) { - AstNode* lhip = newAstWordSelClone(lhsp, othword - 1); + AstNode* const lhip = newAstWordSelClone(lhsp, othword - 1); const int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword - newp = new AstOr( + newp = new AstOr{ fl, - new AstAnd(fl, new AstConst(fl, AstConst::SizedEData(), VL_MASK_E(loffset)), - new AstShiftR(fl, lhip, new AstConst(fl, nbitsonright), VL_EDATASIZE)), - new AstAnd(fl, new AstConst(fl, AstConst::SizedEData(), ~VL_MASK_E(loffset)), - new AstShiftL(fl, llowp, new AstConst(fl, loffset), VL_EDATASIZE))); + new AstAnd{fl, new AstConst{fl, AstConst::SizedEData(), VL_MASK_E(loffset)}, + new AstShiftR{fl, lhip, + new AstConst{fl, static_cast(nbitsonright)}, + VL_EDATASIZE}}, + new AstAnd{fl, new AstConst{fl, AstConst::SizedEData(), ~VL_MASK_E(loffset)}, + new AstShiftL{fl, llowp, + new AstConst{fl, static_cast(loffset)}, + VL_EDATASIZE}}}; } else { newp = llowp; } return newp; } - AstNode* newWordSel(FileLine* fl, AstNode* fromp, AstNode* lsbp, int wordAdder) { + static AstNode* newWordSel(FileLine* fl, AstNode* fromp, AstNode* lsbp, + uint32_t wordOffset = 0) { // Return equation to get the VL_BITWORD of a constant or non-constant UASSERT_OBJ(fromp->isWide(), fromp, "Only need AstWordSel on wide from's"); - if (wordAdder >= fromp->widthWords()) { + if (wordOffset >= static_cast(fromp->widthWords())) { // e.g. "logic [95:0] var[0]; logic [0] sel; out = var[sel];" // Squash before C++ to avoid getting a C++ compiler warning // (even though code would be unreachable as presumably a // AstCondBound is protecting above this node. - return new AstConst(fl, AstConst::SizedEData(), 0); + return new AstConst{fl, AstConst::SizedEData(), 0}; } else { AstNode* wordp; + FileLine* const lfl = lsbp->fileline(); if (VN_IS(lsbp, Const)) { - wordp = new AstConst(lsbp->fileline(), - wordAdder + VL_BITWORD_E(VN_CAST(lsbp, Const)->toUInt())); + wordp + = new AstConst{lfl, wordOffset + VL_BITWORD_E(VN_CAST(lsbp, Const)->toUInt())}; } else { - wordp = new AstShiftR(lsbp->fileline(), lsbp->cloneTree(true), - new AstConst(lsbp->fileline(), VL_EDATASIZE_LOG2), - VL_EDATASIZE); - if (wordAdder != 0) { - wordp = new AstAdd(lsbp->fileline(), - // This is indexing a arraysel, so a 32 bit constant is fine - new AstConst(lsbp->fileline(), wordAdder), wordp); + wordp = new AstShiftR{lfl, lsbp->cloneTree(true), + new AstConst{lfl, VL_EDATASIZE_LOG2}, VL_EDATASIZE}; + if (wordOffset + != 0) { // This is indexing a arraysel, so a 32 bit constant is fine + wordp = new AstAdd{lfl, new AstConst{lfl, wordOffset}, wordp}; } } - return new AstWordSel(fl, fromp, wordp); + return new AstWordSel{fl, fromp, wordp}; } } - AstNode* dropCondBound(AstNode* nodep) { + static AstNode* dropCondBound(AstNode* nodep) { // Experimental only... // If there's a CONDBOUND safety to keep arrays in bounds, // we're going to AND it to a value that always fits inside a @@ -205,13 +208,14 @@ private: return nodep; } - AstNode* newSelBitBit(AstNode* lsbp) { + static AstNode* newSelBitBit(AstNode* lsbp) { // Return equation to get the VL_BITBIT of a constant or non-constant + FileLine* const fl = lsbp->fileline(); if (VN_IS(lsbp, Const)) { - return new AstConst(lsbp->fileline(), VL_BITBIT_E(VN_CAST(lsbp, Const)->toUInt())); + return new AstConst{fl, VL_BITBIT_E(VN_CAST(lsbp, Const)->toUInt())}; } else { - return new AstAnd(lsbp->fileline(), new AstConst(lsbp->fileline(), VL_EDATASIZE - 1), - dropCondBound(lsbp)->cloneTree(true)); + return new AstAnd{fl, new AstConst{fl, VL_EDATASIZE - 1}, + dropCondBound(lsbp)->cloneTree(true)}; } } @@ -225,10 +229,10 @@ private: rhsp->v3warn(E_UNSUPPORTED, // LCOV_EXCL_LINE // impossible? "Unsupported: 4-state numbers in this context"); } - for (int w = 0; w < nodep->widthWords(); w++) { - addWordAssign( - nodep, w, - new AstConst(nodep->fileline(), AstConst::SizedEData(), rhsp->num().edataWord(w))); + FileLine* const fl = nodep->fileline(); + for (int w = 0; w < nodep->widthWords(); ++w) { + addWordAssign(nodep, w, + new AstConst{fl, AstConst::SizedEData(), rhsp->num().edataWord(w)}); } return true; } @@ -236,7 +240,7 @@ private: bool expandWide(AstNodeAssign* nodep, AstVarRef* rhsp) { UINFO(8, " Wordize ASSIGN(VARREF) " << nodep << endl); if (!doExpand(nodep)) return false; - for (int w = 0; w < nodep->widthWords(); w++) { + for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, newAstWordSelClone(rhsp, w)); } return true; @@ -246,7 +250,7 @@ private: UASSERT_OBJ(!VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType), nodep, "ArraySel with unpacked arrays should have been removed in V3Slice"); if (!doExpand(nodep)) return false; - for (int w = 0; w < nodep->widthWords(); w++) { + for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, newAstWordSelClone(rhsp, w)); } return true; @@ -255,9 +259,9 @@ private: UINFO(8, " Wordize ASSIGN(NOT) " << nodep << endl); // -> {for each_word{ ASSIGN(WORDSEL(wide,#),NOT(WORDSEL(lhs,#))) }} if (!doExpand(nodep)) return false; - for (int w = 0; w < nodep->widthWords(); w++) { - addWordAssign(nodep, w, - new AstNot(rhsp->fileline(), newAstWordSelClone(rhsp->lhsp(), w))); + FileLine* const fl = rhsp->fileline(); + for (int w = 0; w < nodep->widthWords(); ++w) { + addWordAssign(nodep, w, new AstNot{fl, newAstWordSelClone(rhsp->lhsp(), w)}); } return true; } @@ -265,30 +269,33 @@ private: bool expandWide(AstNodeAssign* nodep, AstAnd* rhsp) { UINFO(8, " Wordize ASSIGN(AND) " << nodep << endl); if (!doExpand(nodep)) return false; - for (int w = 0; w < nodep->widthWords(); w++) { + FileLine* const fl = nodep->fileline(); + for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, - new AstAnd(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w), - newAstWordSelClone(rhsp->rhsp(), w))); + new AstAnd{fl, newAstWordSelClone(rhsp->lhsp(), w), + newAstWordSelClone(rhsp->rhsp(), w)}); } return true; } bool expandWide(AstNodeAssign* nodep, AstOr* rhsp) { UINFO(8, " Wordize ASSIGN(OR) " << nodep << endl); if (!doExpand(nodep)) return false; - for (int w = 0; w < nodep->widthWords(); w++) { + FileLine* const fl = nodep->fileline(); + for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, - new AstOr(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w), - newAstWordSelClone(rhsp->rhsp(), w))); + new AstOr{fl, newAstWordSelClone(rhsp->lhsp(), w), + newAstWordSelClone(rhsp->rhsp(), w)}); } return true; } bool expandWide(AstNodeAssign* nodep, AstXor* rhsp) { UINFO(8, " Wordize ASSIGN(XOR) " << nodep << endl); if (!doExpand(nodep)) return false; - for (int w = 0; w < nodep->widthWords(); w++) { + FileLine* const fl = nodep->fileline(); + for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, - new AstXor(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w), - newAstWordSelClone(rhsp->rhsp(), w))); + new AstXor{fl, newAstWordSelClone(rhsp->lhsp(), w), + newAstWordSelClone(rhsp->rhsp(), w)}); } return true; } @@ -296,11 +303,12 @@ private: bool expandWide(AstNodeAssign* nodep, AstNodeCond* rhsp) { UINFO(8, " Wordize ASSIGN(COND) " << nodep << endl); if (!doExpand(nodep)) return false; - for (int w = 0; w < nodep->widthWords(); w++) { + FileLine* const fl = nodep->fileline(); + for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, - new AstCond(nodep->fileline(), rhsp->condp()->cloneTree(true), + new AstCond{fl, rhsp->condp()->cloneTree(true), newAstWordSelClone(rhsp->expr1p(), w), - newAstWordSelClone(rhsp->expr2p(), w))); + newAstWordSelClone(rhsp->expr2p(), w)}); } return true; } @@ -312,7 +320,7 @@ private: if (nodep->isWide()) { // See under ASSIGN(EXTEND) } else { - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* const lhsp = nodep->lhsp()->unlinkFrBack(); AstNode* newp = lhsp; if (nodep->isQuad()) { if (lhsp->isQuad()) { @@ -321,7 +329,7 @@ private: nodep->v3fatalSrc("extending larger thing into smaller?"); } else { UINFO(8, " EXTEND(q<-l) " << nodep << endl); - newp = new AstCCast(nodep->fileline(), lhsp, nodep); + newp = new AstCCast{nodep->fileline(), lhsp, nodep}; } } else { // Long UASSERT_OBJ(!(lhsp->isQuad() || lhsp->isWide()), nodep, @@ -348,97 +356,83 @@ private: // Selection amounts // Check for constant shifts & save some constification work later. // Grab lowest bit(s) - AstNode* lowwordp = newWordSel(nodep->fromp()->fileline(), - nodep->fromp()->cloneTree(true), nodep->lsbp(), 0); + FileLine* const nfl = nodep->fileline(); + FileLine* const lfl = nodep->lsbp()->fileline(); + FileLine* const ffl = nodep->fromp()->fileline(); + AstNode* lowwordp = newWordSel(ffl, nodep->fromp()->cloneTree(true), nodep->lsbp()); if (nodep->isQuad() && !lowwordp->isQuad()) { - lowwordp = new AstCCast(nodep->fileline(), lowwordp, nodep); + lowwordp = new AstCCast{nfl, lowwordp, nodep}; } - AstNode* lowp = new AstShiftR(nodep->fileline(), lowwordp, newSelBitBit(nodep->lsbp()), - nodep->width()); + AstNode* const lowp + = new AstShiftR{nfl, lowwordp, newSelBitBit(nodep->lsbp()), nodep->width()}; // If > 1 bit, we might be crossing the word boundary AstNode* midp = nullptr; - V3Number zero(nodep, longOrQuadWidth(nodep)); if (nodep->widthConst() > 1) { const uint32_t midMsbOffset = std::min(nodep->widthConst(), VL_EDATASIZE) - 1; - AstNode* const midMsbp - = new AstAdd(nodep->lsbp()->fileline(), - new AstConst(nodep->lsbp()->fileline(), midMsbOffset), - nodep->lsbp()->cloneTree(false)); + AstNode* const midMsbp = new AstAdd{lfl, new AstConst{lfl, midMsbOffset}, + nodep->lsbp()->cloneTree(false)}; AstNode* midwordp = // SEL(from,[midwordnum]) - newWordSel(nodep->fromp()->fileline(), nodep->fromp()->cloneTree(true), - midMsbp, 0); + newWordSel(ffl, nodep->fromp()->cloneTree(true), midMsbp, 0); // newWordSel clones the index, so delete it VL_DO_DANGLING(midMsbp->deleteTree(), midMsbp); if (nodep->isQuad() && !midwordp->isQuad()) { - midwordp = new AstCCast(nodep->fileline(), midwordp, nodep); + midwordp = new AstCCast{nfl, midwordp, nodep}; } - AstNode* const midshiftp - = new AstSub(nodep->lsbp()->fileline(), - new AstConst(nodep->lsbp()->fileline(), VL_EDATASIZE), - newSelBitBit(nodep->lsbp())); + AstNode* const midshiftp = new AstSub{lfl, new AstConst{lfl, VL_EDATASIZE}, + newSelBitBit(nodep->lsbp())}; // If we're selecting bit zero, then all 32 bits in the mid word // get shifted << by 32 bits, so ignore them. - midp = new AstCond( - nodep->fileline(), + const V3Number zero{nodep, longOrQuadWidth(nodep)}; + midp = new AstCond{ + nfl, // lsb % VL_EDATASIZE == 0 ? - new AstEq(nodep->fileline(), new AstConst(nodep->fileline(), 0), - newSelBitBit(nodep->lsbp())), + new AstEq{nfl, new AstConst{nfl, 0}, newSelBitBit(nodep->lsbp())}, // 0 : - new AstConst(nodep->fileline(), zero), + new AstConst{nfl, zero}, // midword >> (VL_EDATASIZE - (lbs % VL_EDATASIZE)) - new AstShiftL(nodep->fileline(), midwordp, midshiftp, nodep->width())); + new AstShiftL{nfl, midwordp, midshiftp, nodep->width()}}; } // If > 32 bits, we might be crossing the second word boundary AstNode* hip = nullptr; if (nodep->widthConst() > VL_EDATASIZE) { const uint32_t hiMsbOffset = nodep->widthConst() - 1; - AstNode* const hiMsbp - = new AstAdd(nodep->lsbp()->fileline(), - new AstConst(nodep->lsbp()->fileline(), hiMsbOffset), - nodep->lsbp()->cloneTree(false)); + AstNode* const hiMsbp = new AstAdd{lfl, new AstConst{lfl, hiMsbOffset}, + nodep->lsbp()->cloneTree(false)}; AstNode* hiwordp = // SEL(from,[hiwordnum]) - newWordSel(nodep->fromp()->fileline(), nodep->fromp()->cloneTree(true), hiMsbp, - 0); + newWordSel(ffl, nodep->fromp()->cloneTree(true), hiMsbp); // newWordSel clones the index, so delete it VL_DO_DANGLING(hiMsbp->deleteTree(), hiMsbp); if (nodep->isQuad() && !hiwordp->isQuad()) { - hiwordp = new AstCCast(nodep->fileline(), hiwordp, nodep); + hiwordp = new AstCCast{nfl, hiwordp, nodep}; } - AstNode* const hishiftp - = new AstCond(nodep->fileline(), - // lsb % VL_EDATASIZE == 0 ? - new AstEq(nodep->fileline(), new AstConst(nodep->fileline(), 0), - newSelBitBit(nodep->lsbp())), - // VL_EDATASIZE : - new AstConst(nodep->lsbp()->fileline(), VL_EDATASIZE), - // 64 - (lbs % VL_EDATASIZE) - new AstSub(nodep->lsbp()->fileline(), - new AstConst(nodep->lsbp()->fileline(), 64), - newSelBitBit(nodep->lsbp()))); - hip = new AstShiftL(nodep->fileline(), hiwordp, hishiftp, nodep->width()); + AstNode* const hishiftp = new AstCond{ + nfl, + // lsb % VL_EDATASIZE == 0 ? + new AstEq{nfl, new AstConst{nfl, 0}, newSelBitBit(nodep->lsbp())}, + // VL_EDATASIZE : + new AstConst{lfl, VL_EDATASIZE}, + // 64 - (lbs % VL_EDATASIZE) + new AstSub{lfl, new AstConst{lfl, 64}, newSelBitBit(nodep->lsbp())}}; + hip = new AstShiftL{nfl, hiwordp, hishiftp, nodep->width()}; } AstNode* newp = lowp; - if (midp) newp = new AstOr(nodep->fileline(), midp, newp); - if (hip) newp = new AstOr(nodep->fileline(), hip, newp); + if (midp) newp = new AstOr{nfl, midp, newp}; + if (hip) newp = new AstOr{nfl, hip, newp}; newp->dtypeFrom(nodep); VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } else { // Long/Quad from Long/Quad UINFO(8, " SEL->SHIFT " << nodep << endl); + FileLine* const fl = nodep->fileline(); AstNode* fromp = nodep->fromp()->unlinkFrBack(); - AstNode* lsbp = nodep->lsbp()->unlinkFrBack(); - if (nodep->isQuad() && !fromp->isQuad()) { - fromp = new AstCCast(nodep->fileline(), fromp, nodep); - } - AstNode* newp = new AstShiftR( - nodep->fileline(), fromp, dropCondBound(lsbp), - fromp->width()); // {large}>>32 requires 64-bit shift operation; then cast + AstNode* const lsbp = nodep->lsbp()->unlinkFrBack(); + if (nodep->isQuad() && !fromp->isQuad()) { fromp = new AstCCast{fl, fromp, nodep}; } + // {large}>>32 requires 64-bit shift operation; then cast + AstNode* newp = new AstShiftR{fl, fromp, dropCondBound(lsbp), fromp->width()}; newp->dtypeFrom(fromp); - if (!nodep->isQuad() && fromp->isQuad()) { - newp = new AstCCast(newp->fileline(), newp, nodep); - } + if (!nodep->isQuad() && fromp->isQuad()) { newp = new AstCCast{fl, newp, nodep}; } newp->dtypeFrom(nodep); VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } @@ -450,34 +444,33 @@ private: if (VN_IS(rhsp->lsbp(), Const) && VL_BITBIT_E(rhsp->lsbConst()) == 0) { const int lsb = rhsp->lsbConst(); UINFO(8, " Wordize ASSIGN(SEL,align) " << nodep << endl); - for (int w = 0; w < nodep->widthWords(); w++) { + for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, newAstWordSelClone(rhsp->fromp(), w + VL_BITWORD_E(lsb))); } return true; } else { UINFO(8, " Wordize ASSIGN(EXTRACT,misalign) " << nodep << endl); - for (int w = 0; w < nodep->widthWords(); w++) { + FileLine* const nfl = nodep->fileline(); + FileLine* const rfl = rhsp->fileline(); + FileLine* const ffl = rhsp->fromp()->fileline(); + FileLine* const lfl = rhsp->lsbp()->fileline(); + for (int w = 0; w < nodep->widthWords(); ++w) { // Grab lowest bits - AstNode* lowwordp = newWordSel(rhsp->fileline(), rhsp->fromp()->cloneTree(true), - rhsp->lsbp(), w); - AstNode* lowp = new AstShiftR(rhsp->fileline(), lowwordp, - newSelBitBit(rhsp->lsbp()), VL_EDATASIZE); + AstNode* const lowwordp + = newWordSel(rfl, rhsp->fromp()->cloneTree(true), rhsp->lsbp(), w); + AstNode* const lowp + = new AstShiftR{rfl, lowwordp, newSelBitBit(rhsp->lsbp()), VL_EDATASIZE}; // Upper bits - V3Number zero(nodep, VL_EDATASIZE, 0); - AstNode* midwordp = // SEL(from,[1+wordnum]) - newWordSel(rhsp->fromp()->fileline(), rhsp->fromp()->cloneTree(true), - rhsp->lsbp(), w + 1); - AstNode* midshiftp = new AstSub( - rhsp->lsbp()->fileline(), new AstConst(rhsp->lsbp()->fileline(), VL_EDATASIZE), - newSelBitBit(rhsp->lsbp())); - AstNode* midmayp - = new AstShiftL(rhsp->fileline(), midwordp, midshiftp, VL_EDATASIZE); - AstNode* midp - = new AstCond(rhsp->fileline(), - new AstEq(rhsp->fileline(), new AstConst(rhsp->fileline(), 0), - newSelBitBit(rhsp->lsbp())), - new AstConst(rhsp->fileline(), zero), midmayp); - AstNode* newp = new AstOr(nodep->fileline(), midp, lowp); + const V3Number zero{nodep, VL_EDATASIZE, 0}; + AstNode* const midwordp = // SEL(from,[1+wordnum]) + newWordSel(ffl, rhsp->fromp()->cloneTree(true), rhsp->lsbp(), w + 1); + AstNode* const midshiftp + = new AstSub{lfl, new AstConst{lfl, VL_EDATASIZE}, newSelBitBit(rhsp->lsbp())}; + AstNode* const midmayp = new AstShiftL{rfl, midwordp, midshiftp, VL_EDATASIZE}; + AstNode* const midp = new AstCond{ + rfl, new AstEq{rfl, new AstConst{rfl, 0}, newSelBitBit(rhsp->lsbp())}, + new AstConst{rfl, zero}, midmayp}; + AstNode* const newp = new AstOr{nfl, midp, lowp}; addWordAssign(nodep, w, newp); } return true; @@ -491,106 +484,95 @@ private: // rhsp: may be allones and can remove AND NOT gate // lsbp: constant or variable // Yuk. + FileLine* const nfl = nodep->fileline(); + FileLine* const lfl = lhsp->fileline(); const bool destwide = lhsp->fromp()->isWide(); const bool ones = nodep->rhsp()->isAllOnesV(); if (VN_IS(lhsp->lsbp(), Const)) { // The code should work without this constant test, but it won't // constify as nicely as we'd like. AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - AstNode* destp = lhsp->fromp()->unlinkFrBack(); + AstNode* const destp = lhsp->fromp()->unlinkFrBack(); const int lsb = lhsp->lsbConst(); const int msb = lhsp->msbConst(); - V3Number maskset(nodep, destp->widthMin()); + V3Number maskset{nodep, destp->widthMin()}; for (int bit = lsb; bit < (msb + 1); bit++) maskset.setBit(bit, 1); - V3Number maskold(nodep, destp->widthMin()); + V3Number maskold{nodep, destp->widthMin()}; maskold.opNot(maskset); if (destwide) { UINFO(8, " ASSIGNSEL(const,wide) " << nodep << endl); - for (int w = 0; w < destp->widthWords(); w++) { + for (int w = 0; w < destp->widthWords(); ++w) { if (w >= VL_BITWORD_E(lsb) && w <= VL_BITWORD_E(msb)) { // else we would just be setting it to the same exact value AstNode* oldvalp = newAstWordSelClone(destp, w); fixCloneLvalue(oldvalp); if (!ones) { - oldvalp - = new AstAnd(lhsp->fileline(), - new AstConst(lhsp->fileline(), AstConst::SizedEData(), - maskold.edataWord(w)), - oldvalp); + oldvalp = new AstAnd{ + lfl, + new AstConst{lfl, AstConst::SizedEData(), maskold.edataWord(w)}, + oldvalp}; } // Appropriate word of new value to insert: - AstNode* newp = newWordGrabShift(lhsp->fileline(), w, rhsp, lsb); + AstNode* newp = newWordGrabShift(lfl, 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); + V3Number cleanmask{nodep, VL_EDATASIZE}; cleanmask.setMask(VL_BITBIT_E(destp->widthMin())); - newp = new AstAnd(lhsp->fileline(), newp, - new AstConst(lhsp->fileline(), cleanmask)); + newp = new AstAnd{lfl, newp, new AstConst{lfl, cleanmask}}; } - addWordAssign(nodep, w, destp, new AstOr(lhsp->fileline(), oldvalp, newp)); + addWordAssign(nodep, w, destp, new AstOr{lfl, oldvalp, newp}); } } VL_DO_DANGLING(rhsp->deleteTree(), rhsp); VL_DO_DANGLING(destp->deleteTree(), destp); } else { UINFO(8, " ASSIGNSEL(const,narrow) " << nodep << endl); - if (destp->isQuad() && !rhsp->isQuad()) { - rhsp = new AstCCast(nodep->fileline(), rhsp, nodep); - } + if (destp->isQuad() && !rhsp->isQuad()) { rhsp = new AstCCast{nfl, rhsp, nodep}; } AstNode* oldvalp = destp->cloneTree(true); fixCloneLvalue(oldvalp); - if (!ones) { - oldvalp = new AstAnd(lhsp->fileline(), new AstConst(lhsp->fileline(), maskold), - oldvalp); - } + if (!ones) { oldvalp = new AstAnd{lfl, new AstConst{lfl, maskold}, oldvalp}; } // 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()); + 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); + AstNode* const shifted = new AstShiftL{ + lfl, rhsp, new AstConst{lfl, static_cast(lsb)}, destp->width()}; + AstNode* const cleaned = new AstAnd{lfl, shifted, new AstConst{lfl, cleanmask}}; + AstNode* const newp = new AstAssign{nfl, destp, new AstOr{lfl, oldvalp, cleaned}}; insertBefore(nodep, newp); } return true; } else { // non-const select offset if (destwide && lhsp->widthConst() == 1) { UINFO(8, " ASSIGNSEL(varlsb,wide,1bit) " << nodep << endl); - AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - AstNode* destp = lhsp->fromp()->unlinkFrBack(); - AstNode* oldvalp - = newWordSel(lhsp->fileline(), destp->cloneTree(true), lhsp->lsbp(), 0); + AstNode* const rhsp = nodep->rhsp()->unlinkFrBack(); + AstNode* const destp = lhsp->fromp()->unlinkFrBack(); + AstNode* oldvalp = newWordSel(lfl, destp->cloneTree(true), lhsp->lsbp()); fixCloneLvalue(oldvalp); if (!ones) { - oldvalp = new AstAnd( - lhsp->fileline(), - new AstNot( - lhsp->fileline(), - new AstShiftL(lhsp->fileline(), new AstConst(nodep->fileline(), 1), - // newSelBitBit may exceed the MSB of this variable. - // That's ok as we'd just AND with a larger value, - // but oldval would clip the upper bits to sanity - newSelBitBit(lhsp->lsbp()), VL_EDATASIZE)), - oldvalp); + oldvalp = new AstAnd{ + lfl, + new AstNot{ + lfl, new AstShiftL{lfl, new AstConst{nfl, 1}, + // newSelBitBit may exceed the MSB of this variable. + // That's ok as we'd just AND with a larger value, + // but oldval would clip the upper bits to sanity + newSelBitBit(lhsp->lsbp()), VL_EDATASIZE}}, + oldvalp}; } // Restrict the shift amount to 0-31, see bug804. - AstNode* shiftp = new AstAnd(nodep->fileline(), lhsp->lsbp()->cloneTree(true), - new AstConst(nodep->fileline(), VL_EDATASIZE - 1)); - AstNode* newp - = new AstOr(lhsp->fileline(), oldvalp, - new AstShiftL(lhsp->fileline(), rhsp, shiftp, VL_EDATASIZE)); - newp = new AstAssign(nodep->fileline(), - newWordSel(nodep->fileline(), destp, lhsp->lsbp(), 0), newp); + AstNode* const shiftp = new AstAnd{nfl, lhsp->lsbp()->cloneTree(true), + new AstConst{nfl, VL_EDATASIZE - 1}}; + AstNode* const newp = new AstAssign{ + nfl, newWordSel(nfl, destp, lhsp->lsbp()), + new AstOr{lfl, oldvalp, new AstShiftL{lfl, rhsp, shiftp, VL_EDATASIZE}}}; insertBefore(nodep, newp); return true; } else if (destwide) { @@ -609,40 +591,35 @@ private: UINFO(8, " ASSIGNSEL(varlsb,narrow) " << nodep << endl); // nodep->dumpTree(cout, "- old: "); AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - AstNode* destp = lhsp->fromp()->unlinkFrBack(); + AstNode* const destp = lhsp->fromp()->unlinkFrBack(); AstNode* oldvalp = destp->cloneTree(true); fixCloneLvalue(oldvalp); - V3Number maskwidth(nodep, destp->widthMin()); + V3Number maskwidth{nodep, destp->widthMin()}; for (int bit = 0; bit < lhsp->widthConst(); bit++) maskwidth.setBit(bit, 1); - if (destp->isQuad() && !rhsp->isQuad()) { - rhsp = new AstCCast(nodep->fileline(), rhsp, nodep); - } + if (destp->isQuad() && !rhsp->isQuad()) { rhsp = new AstCCast{nfl, rhsp, nodep}; } if (!ones) { - oldvalp = new AstAnd( - lhsp->fileline(), - new AstNot(lhsp->fileline(), - new AstShiftL(lhsp->fileline(), - new AstConst(nodep->fileline(), maskwidth), - lhsp->lsbp()->cloneTree(true), destp->width())), - oldvalp); + oldvalp = new AstAnd{ + lfl, + new AstNot{lfl, + new AstShiftL{lfl, new AstConst{nfl, maskwidth}, + lhsp->lsbp()->cloneTree(true), destp->width()}}, + oldvalp}; } - AstNode* newp = new AstShiftL(lhsp->fileline(), rhsp, - lhsp->lsbp()->cloneTree(true), destp->width()); + AstNode* newp + = new AstShiftL{lfl, 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); + V3Number cleanmask{nodep, destp->widthMin() + 1}; cleanmask.setMask(destp->widthMin()); - newp = new AstAnd(lhsp->fileline(), newp, - new AstConst(lhsp->fileline(), cleanmask)); + newp = new AstAnd{lfl, newp, new AstConst{lfl, cleanmask}}; } - newp = new AstAssign(nodep->fileline(), destp, - new AstOr(lhsp->fileline(), oldvalp, newp)); + newp = new AstAssign{nfl, destp, new AstOr{lfl, oldvalp, newp}}; // newp->dumpTree(cout, "- new: "); insertBefore(nodep, newp); return true; @@ -657,20 +634,14 @@ private: // See under ASSIGN(WIDE) } else { UINFO(8, " CONCAT " << nodep << endl); + FileLine* const fl = nodep->fileline(); AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - const int rhsshift = rhsp->widthMin(); - if (nodep->isQuad() && !lhsp->isQuad()) { - lhsp = new AstCCast(nodep->fileline(), lhsp, nodep); - } - if (nodep->isQuad() && !rhsp->isQuad()) { - rhsp = new AstCCast(nodep->fileline(), rhsp, nodep); - } - AstNode* newp = new AstOr(nodep->fileline(), - new AstShiftL(nodep->fileline(), lhsp, - new AstConst(nodep->fileline(), rhsshift), - nodep->width()), - rhsp); + const uint32_t rhsshift = rhsp->widthMin(); + if (nodep->isQuad() && !lhsp->isQuad()) { lhsp = new AstCCast{fl, lhsp, nodep}; } + if (nodep->isQuad() && !rhsp->isQuad()) { rhsp = new AstCCast{fl, rhsp, nodep}; } + AstNode* const newp = new AstOr{ + fl, new AstShiftL{fl, lhsp, new AstConst{fl, rhsshift}, nodep->width()}, rhsp}; newp->dtypeFrom(nodep); // Unsigned VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } @@ -678,17 +649,17 @@ private: bool expandWide(AstNodeAssign* nodep, AstConcat* rhsp) { UINFO(8, " Wordize ASSIGN(CONCAT) " << nodep << endl); if (!doExpand(rhsp)) return false; + FileLine* const fl = rhsp->fileline(); // Lhs or Rhs may be word, long, or quad. // newAstWordSelClone nicely abstracts the difference. const int rhsshift = rhsp->rhsp()->widthMin(); // Sometimes doing the words backwards is preferable. // When we have x={x,foo} backwards is better, when x={foo,x} forward is better // However V3Subst tends to rip this up, so not worth optimizing now. - for (int w = 0; w < rhsp->widthWords(); w++) { + for (int w = 0; w < rhsp->widthWords(); ++w) { addWordAssign(nodep, w, - new AstOr(rhsp->fileline(), - newWordGrabShift(rhsp->fileline(), w, rhsp->lhsp(), rhsshift), - newAstWordSelClone(rhsp->rhsp(), w))); + new AstOr{fl, newWordGrabShift(fl, w, rhsp->lhsp(), rhsshift), + newAstWordSelClone(rhsp->rhsp(), w)}); } return true; } @@ -699,29 +670,29 @@ private: if (nodep->isWide()) { // See under ASSIGN(WIDE) } else { + FileLine* const fl = nodep->fileline(); AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); AstNode* newp; const int lhswidth = lhsp->widthMin(); if (lhswidth == 1) { UINFO(8, " REPLICATE(w1) " << nodep << endl); - newp = new AstNegate(nodep->fileline(), lhsp); + newp = new AstNegate{fl, lhsp}; } else { UINFO(8, " REPLICATE " << nodep << endl); const AstConst* constp = VN_CAST(nodep->rhsp(), Const); UASSERT_OBJ(constp, nodep, "Replication value isn't a constant. Checked earlier!"); uint32_t times = constp->toUInt(); - if (nodep->isQuad() && !lhsp->isQuad()) { - lhsp = new AstCCast(nodep->fileline(), lhsp, nodep); - } + if (nodep->isQuad() && !lhsp->isQuad()) { lhsp = new AstCCast{fl, lhsp, nodep}; } newp = lhsp->cloneTree(true); for (unsigned repnum = 1; repnum < times; repnum++) { const int rhsshift = repnum * lhswidth; - newp = new AstOr(nodep->fileline(), - new AstShiftL(nodep->fileline(), lhsp->cloneTree(true), - new AstConst(nodep->fileline(), rhsshift), - nodep->width()), - newp); + newp = new AstOr{ + fl, + new AstShiftL{fl, lhsp->cloneTree(true), + new AstConst{fl, static_cast(rhsshift)}, + nodep->width()}, + newp}; newp->dtypeFrom(nodep); // Unsigned } VL_DO_DANGLING(lhsp->deleteTree(), lhsp); // Never used @@ -733,23 +704,23 @@ private: bool expandWide(AstNodeAssign* nodep, AstReplicate* rhsp) { UINFO(8, " Wordize ASSIGN(REPLICATE) " << nodep << endl); if (!doExpand(rhsp)) return false; - AstNode* lhsp = rhsp->lhsp(); + FileLine* const fl = nodep->fileline(); + AstNode* const lhsp = rhsp->lhsp(); const int lhswidth = lhsp->widthMin(); - const AstConst* constp = VN_CAST(rhsp->rhsp(), Const); + const AstConst* const constp = VN_CAST(rhsp->rhsp(), Const); UASSERT_OBJ(constp, rhsp, "Replication value isn't a constant. Checked earlier!"); - uint32_t times = constp->toUInt(); - for (int w = 0; w < rhsp->widthWords(); w++) { + const uint32_t times = constp->toUInt(); + for (int w = 0; w < rhsp->widthWords(); ++w) { AstNode* newp; if (lhswidth == 1) { - newp = new AstNegate(nodep->fileline(), lhsp->cloneTree(true)); - newp->dtypeSetLogicSized(VL_EDATASIZE, - VSigning::UNSIGNED); // Replicate always unsigned + newp = new AstNegate{fl, lhsp->cloneTree(true)}; + // Replicate always unsigned + newp->dtypeSetLogicSized(VL_EDATASIZE, VSigning::UNSIGNED); } else { newp = newAstWordSelClone(lhsp, w); + FileLine* const rfl = rhsp->fileline(); for (unsigned repnum = 1; repnum < times; repnum++) { - newp = new AstOr( - nodep->fileline(), - newWordGrabShift(rhsp->fileline(), w, lhsp, lhswidth * repnum), newp); + newp = new AstOr{fl, newWordGrabShift(rfl, w, lhsp, lhswidth * repnum), newp}; } } addWordAssign(nodep, w, newp); @@ -762,11 +733,12 @@ private: iterateChildren(nodep); UINFO(8, " Wordize ChangeXor " << nodep << endl); // -> (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}} + FileLine* const fl = nodep->fileline(); AstNode* newp = nullptr; - for (int w = 0; w < nodep->lhsp()->widthWords(); w++) { - AstNode* eqp = new AstXor(nodep->fileline(), newAstWordSelClone(nodep->lhsp(), w), - newAstWordSelClone(nodep->rhsp(), w)); - newp = (newp == nullptr) ? eqp : (new AstOr(nodep->fileline(), newp, eqp)); + for (int w = 0; w < nodep->lhsp()->widthWords(); ++w) { + AstNode* const eqp = new AstXor{fl, newAstWordSelClone(nodep->lhsp(), w), + newAstWordSelClone(nodep->rhsp(), w)}; + newp = newp ? new AstOr{fl, newp, eqp} : eqp; } VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } @@ -777,19 +749,17 @@ private: if (nodep->lhsp()->isWide()) { UINFO(8, " Wordize EQ/NEQ " << nodep << endl); // -> (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}} + FileLine* const fl = nodep->fileline(); AstNode* newp = nullptr; - for (int w = 0; w < nodep->lhsp()->widthWords(); w++) { - AstNode* eqp = new AstXor(nodep->fileline(), newAstWordSelClone(nodep->lhsp(), w), - newAstWordSelClone(nodep->rhsp(), w)); - newp = (newp == nullptr) ? eqp : (new AstOr(nodep->fileline(), newp, eqp)); + for (int w = 0; w < nodep->lhsp()->widthWords(); ++w) { + AstNode* const eqp = new AstXor{fl, newAstWordSelClone(nodep->lhsp(), w), + newAstWordSelClone(nodep->rhsp(), w)}; + newp = newp ? new AstOr{fl, newp, eqp} : eqp; } if (VN_IS(nodep, Neq)) { - newp - = new AstNeq(nodep->fileline(), - new AstConst(nodep->fileline(), AstConst::SizedEData(), 0), newp); + newp = new AstNeq{fl, new AstConst{fl, AstConst::SizedEData(), 0}, newp}; } else { - newp = new AstEq(nodep->fileline(), - new AstConst(nodep->fileline(), AstConst::SizedEData(), 0), newp); + newp = new AstEq{fl, new AstConst{fl, AstConst::SizedEData(), 0}, newp}; } VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } @@ -800,57 +770,52 @@ private: virtual void visit(AstRedOr* nodep) override { if (nodep->user1SetOnce()) return; // Process once iterateChildren(nodep); + FileLine* const fl = nodep->fileline(); if (nodep->lhsp()->isWide()) { UINFO(8, " Wordize REDOR " << nodep << endl); // -> (0!={or{for each_word{WORDSEL(lhs,#)}}} AstNode* newp = nullptr; - for (int w = 0; w < nodep->lhsp()->widthWords(); w++) { - AstNode* eqp = newAstWordSelClone(nodep->lhsp(), w); - newp = (newp == nullptr) ? eqp : (new AstOr(nodep->fileline(), newp, eqp)); + for (int w = 0; w < nodep->lhsp()->widthWords(); ++w) { + AstNode* const eqp = newAstWordSelClone(nodep->lhsp(), w); + newp = newp ? new AstOr{fl, newp, eqp} : eqp; } - newp = new AstNeq(nodep->fileline(), - new AstConst(nodep->fileline(), AstConst::SizedEData(), 0), newp); + newp = new AstNeq{fl, new AstConst{fl, AstConst::SizedEData(), 0}, newp}; VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } else { UINFO(8, " REDOR->EQ " << nodep << endl); - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); - AstNode* newp = new AstNeq(nodep->fileline(), - new AstConst(nodep->fileline(), AstConst::WidthedValue(), - longOrQuadWidth(nodep), 0), - lhsp); + AstNode* const lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* const newp = new AstNeq{ + fl, new AstConst{fl, AstConst::WidthedValue(), longOrQuadWidth(nodep), 0}, lhsp}; VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } } virtual void visit(AstRedAnd* nodep) override { if (nodep->user1SetOnce()) return; // Process once iterateChildren(nodep); + FileLine* const fl = nodep->fileline(); if (nodep->lhsp()->isWide()) { UINFO(8, " Wordize REDAND " << nodep << endl); // -> (0!={and{for each_word{WORDSEL(lhs,#)}}} AstNode* newp = nullptr; - for (int w = 0; w < nodep->lhsp()->widthWords(); w++) { + for (int w = 0; w < nodep->lhsp()->widthWords(); ++w) { AstNode* eqp = newAstWordSelClone(nodep->lhsp(), w); if (w == nodep->lhsp()->widthWords() - 1) { // Rather than doing a (slowish) ==##, we OR in the // bits that aren't part of the mask - eqp = new AstOr(nodep->fileline(), - new AstConst(nodep->fileline(), notWideMask(nodep->lhsp())), + eqp = new AstOr{fl, new AstConst{fl, notWideMask(nodep->lhsp())}, // Bug in cppcheck // cppcheck-suppress memleak - eqp); + eqp}; } - newp = (newp == nullptr) ? eqp : (new AstAnd(nodep->fileline(), newp, eqp)); + newp = newp ? new AstAnd{fl, newp, eqp} : eqp; } - newp = new AstEq( - nodep->fileline(), - new AstConst(nodep->fileline(), AstConst::SizedEData(), VL_MASK_E(VL_EDATASIZE)), - newp); + newp = new AstEq{fl, new AstConst{fl, AstConst::SizedEData(), VL_MASK_E(VL_EDATASIZE)}, + newp}; VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } else { UINFO(8, " REDAND->EQ " << nodep << endl); - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); - AstNode* newp = new AstEq(nodep->fileline(), - new AstConst(nodep->fileline(), wordMask(lhsp)), lhsp); + AstNode* const lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* const newp = new AstEq{fl, new AstConst{fl, wordMask(lhsp)}, lhsp}; VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } } @@ -860,12 +825,13 @@ private: if (nodep->lhsp()->isWide()) { UINFO(8, " Wordize REDXOR " << nodep << endl); // -> (0!={redxor{for each_word{XOR(WORDSEL(lhs,#))}}} + FileLine* const fl = nodep->fileline(); AstNode* newp = nullptr; - for (int w = 0; w < nodep->lhsp()->widthWords(); w++) { - AstNode* eqp = newAstWordSelClone(nodep->lhsp(), w); - newp = (newp == nullptr) ? eqp : (new AstXor(nodep->fileline(), newp, eqp)); + for (int w = 0; w < nodep->lhsp()->widthWords(); ++w) { + AstNode* const eqp = newAstWordSelClone(nodep->lhsp(), w); + newp = newp ? new AstXor{fl, newp, eqp} : eqp; } - newp = new AstRedXor(nodep->fileline(), newp); + newp = new AstRedXor{fl, newp}; UINFO(8, " Wordize REDXORnew " << newp << endl); VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } @@ -892,30 +858,30 @@ private: && ((VN_IS(nodep->lhsp(), VarRef) || VN_IS(nodep->lhsp(), ArraySel))) && !AstVar::scVarRecurse(nodep->lhsp()) // Need special function for SC && !AstVar::scVarRecurse(nodep->rhsp())) { - if (AstConst* rhsp = VN_CAST(nodep->rhsp(), Const)) { + if (AstConst* const rhsp = VN_CAST(nodep->rhsp(), Const)) { did = expandWide(nodep, rhsp); - } else if (AstVarRef* rhsp = VN_CAST(nodep->rhsp(), VarRef)) { + } else if (AstVarRef* const rhsp = VN_CAST(nodep->rhsp(), VarRef)) { did = expandWide(nodep, rhsp); - } else if (AstSel* rhsp = VN_CAST(nodep->rhsp(), Sel)) { + } else if (AstSel* const rhsp = VN_CAST(nodep->rhsp(), Sel)) { did = expandWide(nodep, rhsp); - } else if (AstArraySel* rhsp = VN_CAST(nodep->rhsp(), ArraySel)) { + } else if (AstArraySel* const rhsp = VN_CAST(nodep->rhsp(), ArraySel)) { did = expandWide(nodep, rhsp); - } else if (AstConcat* rhsp = VN_CAST(nodep->rhsp(), Concat)) { + } else if (AstConcat* const rhsp = VN_CAST(nodep->rhsp(), Concat)) { did = expandWide(nodep, rhsp); - } else if (AstReplicate* rhsp = VN_CAST(nodep->rhsp(), Replicate)) { + } else if (AstReplicate* const rhsp = VN_CAST(nodep->rhsp(), Replicate)) { did = expandWide(nodep, rhsp); - } else if (AstAnd* rhsp = VN_CAST(nodep->rhsp(), And)) { + } else if (AstAnd* const rhsp = VN_CAST(nodep->rhsp(), And)) { did = expandWide(nodep, rhsp); - } else if (AstOr* rhsp = VN_CAST(nodep->rhsp(), Or)) { + } else if (AstOr* const rhsp = VN_CAST(nodep->rhsp(), Or)) { did = expandWide(nodep, rhsp); - } else if (AstNot* rhsp = VN_CAST(nodep->rhsp(), Not)) { + } else if (AstNot* const rhsp = VN_CAST(nodep->rhsp(), Not)) { did = expandWide(nodep, rhsp); - } else if (AstXor* rhsp = VN_CAST(nodep->rhsp(), Xor)) { + } else if (AstXor* const rhsp = VN_CAST(nodep->rhsp(), Xor)) { did = expandWide(nodep, rhsp); - } else if (AstNodeCond* rhsp = VN_CAST(nodep->rhsp(), NodeCond)) { + } else if (AstNodeCond* const rhsp = VN_CAST(nodep->rhsp(), NodeCond)) { did = expandWide(nodep, rhsp); } - } else if (AstSel* lhsp = VN_CAST(nodep->lhsp(), Sel)) { + } else if (AstSel* const lhsp = VN_CAST(nodep->lhsp(), Sel)) { did = expandLhs(nodep, lhsp); } // Cleanup common code @@ -945,6 +911,6 @@ public: void V3Expand::expandAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { ExpandVisitor visitor(nodep); } // Destruct before checking + { ExpandVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("expand", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3File.cpp b/src/V3File.cpp index fe9550838..f26e60c67 100644 --- a/src/V3File.cpp +++ b/src/V3File.cpp @@ -145,7 +145,7 @@ V3FileDependImp dependImp; // Depend implementation class // V3FileDependImp inline void V3FileDependImp::writeDepend(const string& filename) { - const std::unique_ptr ofp(V3File::new_ofstream(filename)); + const std::unique_ptr ofp{V3File::new_ofstream(filename)}; if (ofp->fail()) v3fatal("Can't write " << filename); for (const DependFile& i : m_filenameList) { @@ -178,7 +178,7 @@ inline std::vector V3FileDependImp::getAllDeps() const { } inline void V3FileDependImp::writeTimes(const string& filename, const string& cmdlineIn) { - const std::unique_ptr ofp(V3File::new_ofstream(filename)); + const std::unique_ptr ofp{V3File::new_ofstream(filename)}; if (ofp->fail()) v3fatal("Can't write " << filename); const string cmdline = stripQuotes(cmdlineIn); @@ -213,7 +213,7 @@ inline void V3FileDependImp::writeTimes(const string& filename, const string& cm } inline bool V3FileDependImp::checkTimes(const string& filename, const string& cmdlineIn) { - const std::unique_ptr ifp(V3File::new_ifstream_nodepend(filename)); + const std::unique_ptr ifp{V3File::new_ifstream_nodepend(filename)}; if (ifp->fail()) { UINFO(2, " --check-times failed: no input " << filename << endl); return false; diff --git a/src/V3File.h b/src/V3File.h index 785a2d0c9..08abc7607 100644 --- a/src/V3File.h +++ b/src/V3File.h @@ -38,7 +38,7 @@ public: return new_ifstream_nodepend(filename); } static std::ifstream* new_ifstream_nodepend(const string& filename) { - return new std::ifstream(filename.c_str()); + return new std::ifstream{filename.c_str()}; } static std::ofstream* new_ofstream(const string& filename, bool append = false) { addTgtDepend(filename); @@ -47,9 +47,9 @@ public: static std::ofstream* new_ofstream_nodepend(const string& filename, bool append = false) { createMakeDirFor(filename); if (append) { - return new std::ofstream(filename.c_str(), std::ios::app); + return new std::ofstream{filename.c_str(), std::ios::app}; } else { - return new std::ofstream(filename.c_str()); + return new std::ofstream{filename.c_str()}; } } static FILE* new_fopen_w(const string& filename) { diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp index 867248bb3..9e36482a9 100644 --- a/src/V3Gate.cpp +++ b/src/V3Gate.cpp @@ -570,7 +570,7 @@ void GateVisitor::optimizeSignals(bool allowMultiIn) { AstNode* logicp = logicVertexp->nodep(); if (logicVertexp->reducible()) { // Can we eliminate? - GateOkVisitor okVisitor(logicp, vvertexp->isClock(), false); + GateOkVisitor okVisitor{logicp, vvertexp->isClock(), false}; const bool multiInputs = okVisitor.rhsVarRefs().size() > 1; // Was it ok? bool doit = okVisitor.isSimple(); @@ -884,7 +884,7 @@ public: void GateVisitor::optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode* consumerp) { if (debug() >= 5) consumerp->dumpTree(cout, " elimUsePre: "); - GateElimVisitor elimVisitor(consumerp, varscp, substp, nullptr); + GateElimVisitor elimVisitor{consumerp, varscp, substp, nullptr}; if (elimVisitor.didReplace()) { if (debug() >= 9) consumerp->dumpTree(cout, " elimUseCns: "); // Caution: Can't let V3Const change our handle to consumerp, such as by @@ -1148,7 +1148,7 @@ private: UASSERT_OBJ(vvertexp->dedupable(), vvertexp->varScp(), "GateLogicVertex* visit should have returned nullptr " "if consumer var vertex is not dedupable."); - GateOkVisitor okVisitor(lvertexp->nodep(), false, true); + GateOkVisitor okVisitor{lvertexp->nodep(), false, true}; if (okVisitor.isSimple()) { AstVarScope* dupVarScopep = dupVarRefp->varScopep(); GateVarVertex* dupVvertexp @@ -1226,7 +1226,7 @@ public: void GateVisitor::dedupe() { AstNode::user2ClearTree(); - GateDedupeGraphVisitor deduper(&m_graph); + GateDedupeGraphVisitor deduper{&m_graph}; // Traverse starting from each of the clocks UINFO(9, "Gate dedupe() clocks:\n"); for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { @@ -1367,7 +1367,7 @@ public: void GateVisitor::mergeAssigns() { UINFO(6, "mergeAssigns\n"); - GateMergeAssignsGraphVisitor merger(&m_graph); + GateMergeAssignsGraphVisitor merger{&m_graph}; for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { if (GateVarVertex* vvertexp = dynamic_cast(itp)) { merger.mergeAssignsTree(vvertexp); @@ -1553,7 +1553,7 @@ public: void GateVisitor::decomposeClkVectors() { UINFO(9, "Starting clock decomposition" << endl); AstNode::user2ClearTree(); - GateClkDecompGraphVisitor decomposer(&m_graph); + GateClkDecompGraphVisitor decomposer{&m_graph}; for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { if (GateVarVertex* vertp = dynamic_cast(itp)) { AstVarScope* vsp = vertp->varScp(); @@ -1601,8 +1601,8 @@ public: void V3Gate::gateAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { - GateVisitor visitor(nodep); - GateDeassignVisitor deassign(nodep); + GateVisitor visitor{nodep}; + GateDeassignVisitor deassign{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("gate", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3GenClk.cpp b/src/V3GenClk.cpp index dc6dc2ec9..1845a95b3 100644 --- a/src/V3GenClk.cpp +++ b/src/V3GenClk.cpp @@ -56,6 +56,19 @@ private: if (vscp->user2p()) { return VN_CAST(vscp->user2p(), VarScope); } else { + // In order to create a __VinpClk* for a signal, it needs to be marked circular. + // The DPI export trigger is never marked circular by V3Order (see comments in + // OrderVisitor::nodeMarkCircular). The only other place where one might mark + // a node circular is in this pass (V3GenClk), if the signal is assigned but was + // previously used as a clock. The DPI export trigger is only ever assigned in + // a DPI export called from outside eval, or from a DPI import, which are not + // discovered by GenClkReadVisitor (note that impure tasks - i.e.: those setting + // non-local variables - cannot be no-inline, see V3Task), hence the DPI export + // trigger should never be marked circular. Note that ordering should still be + // correct as there will be a change detect on any signals set from a DPI export + // that might have dependents scheduled earlier. + UASSERT_OBJ(vscp != v3Global.rootp()->dpiExportTriggerp(), vscp, + "DPI export trigger should not need __VinpClk"); AstVar* varp = vscp->varp(); string newvarname = "__VinpClk__" + vscp->scopep()->nameDotless() + "__" + varp->name(); @@ -147,7 +160,7 @@ private: { // Make the new clock signals and replace any activate references // See rename, it does some AstNode::userClearTree()'s - GenClkRenameVisitor visitor(nodep, m_topModp); + GenClkRenameVisitor visitor{nodep, m_topModp}; } } virtual void visit(AstNodeModule* nodep) override { @@ -222,6 +235,6 @@ public: void V3GenClk::genClkAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { GenClkReadVisitor visitor(nodep); } // Destruct before checking + { GenClkReadVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("genclk", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Global.h b/src/V3Global.h index b0a35f9e0..2eba07885 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -53,8 +53,8 @@ template class VRestorer { public: explicit VRestorer(T& permr) - : m_ref(permr) - , m_saved(permr) {} + : m_ref{permr} + , m_saved{permr} {} ~VRestorer() { m_ref = m_saved; } VL_UNCOPYABLE(VRestorer); }; diff --git a/src/V3Graph.cpp b/src/V3Graph.cpp index 71070cee2..6cbe5dc65 100644 --- a/src/V3Graph.cpp +++ b/src/V3Graph.cpp @@ -314,7 +314,7 @@ void V3Graph::dumpDotFilePrefixedAlways(const string& nameComment, bool colorAsS void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const { // This generates a file used by graphviz, https://www.graphviz.org // "hardcoded" parameters: - const std::unique_ptr logp(V3File::new_ofstream(filename)); + const std::unique_ptr logp{V3File::new_ofstream(filename)}; if (logp->fail()) v3fatal("Can't write " << filename); // Header diff --git a/src/V3GraphDfa.h b/src/V3GraphDfa.h index 79e6e465e..8550bdad4 100644 --- a/src/V3GraphDfa.h +++ b/src/V3GraphDfa.h @@ -122,8 +122,8 @@ public: // CONSTRUCTORS DfaEdge(DfaGraph* graphp, DfaVertex* fromp, DfaVertex* top, const DfaInput& input) : V3GraphEdge{graphp, fromp, top, 1} - , m_input(input) - , m_complement(false) {} + , m_input{input} + , m_complement{false} {} DfaEdge(DfaGraph* graphp, DfaVertex* fromp, DfaVertex* top, const DfaEdge* copyfrom) : V3GraphEdge{graphp, fromp, top, copyfrom->weight()} , m_input{copyfrom->input()} diff --git a/src/V3Hash.cpp b/src/V3Hash.cpp index 35d924771..65059102a 100644 --- a/src/V3Hash.cpp +++ b/src/V3Hash.cpp @@ -19,10 +19,17 @@ #include #include #include +#include V3Hash::V3Hash(const std::string& val) : m_value{static_cast(std::hash{}(val))} {} std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) { - return os << std::hex << std::setw(8) << std::setfill('0') << rhs.value(); + return os << 'h' << std::hex << std::setw(8) << std::setfill('0') << rhs.value(); +} + +std::string V3Hash::toString() const { + std::ostringstream os; + os << *this; + return os.str(); } diff --git a/src/V3Hash.h b/src/V3Hash.h index 06223567c..ef00d0a83 100644 --- a/src/V3Hash.h +++ b/src/V3Hash.h @@ -46,6 +46,7 @@ public: // METHODS uint32_t value() const { return m_value; } + std::string toString() const; // OPERATORS // Comparisons diff --git a/src/V3Hasher.cpp b/src/V3Hasher.cpp index 775cee846..b83f8386a 100644 --- a/src/V3Hasher.cpp +++ b/src/V3Hasher.cpp @@ -488,11 +488,11 @@ public: // V3Hasher methods V3Hash V3Hasher::operator()(AstNode* nodep) const { - if (!nodep->user4()) { HasherVisitor visitor(nodep); } + if (!nodep->user4()) { HasherVisitor visitor{nodep}; } return V3Hash(nodep->user4()); } V3Hash V3Hasher::uncachedHash(const AstNode* nodep) { - HasherVisitor visitor(nodep); + HasherVisitor visitor{nodep}; return visitor.finalHash(); } diff --git a/src/V3HierBlock.cpp b/src/V3HierBlock.cpp index a11323d8e..dc7ba5a95 100644 --- a/src/V3HierBlock.cpp +++ b/src/V3HierBlock.cpp @@ -208,7 +208,7 @@ string V3HierBlock::vFileIfNecessary() const { } void V3HierBlock::writeCommandArgsFile(bool forCMake) const { - std::unique_ptr of(V3File::new_ofstream(commandArgsFileName(forCMake))); + std::unique_ptr of{V3File::new_ofstream(commandArgsFileName(forCMake))}; *of << "--cc\n"; if (!forCMake) { @@ -398,7 +398,7 @@ void V3HierBlockPlan::writeCommandArgsFiles(bool forCMake) const { it->second->writeCommandArgsFile(forCMake); } // For the top module - std::unique_ptr of(V3File::new_ofstream(topCommandArgsFileName(forCMake))); + std::unique_ptr of{V3File::new_ofstream(topCommandArgsFileName(forCMake))}; if (!forCMake) { // Load wrappers first not to be overwritten by the original HDL for (const_iterator it = begin(); it != end(); ++it) { diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index 01b62ae81..680eacfc6 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -548,7 +548,7 @@ private: // Clear var markings and find cell cross references AstNode::user2ClearTree(); AstNode::user4ClearTree(); - { InlineCollectVisitor(nodep->modp()); } // {} to destroy visitor immediately + { InlineCollectVisitor{nodep->modp()}; } // {} to destroy visitor immediately // Create data for dotted variable resolution AstCellInline* inlinep = new AstCellInline(nodep->fileline(), nodep->name(), nodep->modp()->origName(), @@ -591,7 +591,7 @@ private: && pinNewVarp->direction() == VDirection::OUTPUT); } // Cleanup var names, etc, to not conflict - { InlineRelinkVisitor(newmodp, m_modp, nodep); } + { InlineRelinkVisitor{newmodp, m_modp, nodep}; } // Move statements to top module if (debug() >= 9) newmodp->dumpTree(cout, "fixmod:"); AstNode* stmtsp = newmodp->stmtsp(); @@ -713,8 +713,8 @@ void V3Inline::inlineAll(AstNetlist* nodep) { AstUser1InUse m_inuser1; // output of InlineMarkVisitor, // input to InlineVisitor. // Scoped to clean up temp userN's - { InlineMarkVisitor mvisitor(nodep); } - { InlineVisitor visitor(nodep); } + { InlineMarkVisitor mvisitor{nodep}; } + { InlineVisitor visitor{nodep}; } // Remove all modules that were inlined // V3Dead will also clean them up, but if we have debug on, it's a good // idea to avoid dumping the hugely exploded tree. @@ -725,6 +725,6 @@ void V3Inline::inlineAll(AstNetlist* nodep) { VL_DO_DANGLING(modp->unlinkFrBack()->deleteTree(), modp); } } - { InlineIntfRefVisitor crvisitor(nodep); } + { InlineIntfRefVisitor crvisitor{nodep}; } V3Global::dumpCheckGlobalTree("inline", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index ff4a2961c..97508c9f3 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -487,12 +487,12 @@ private: static AstNode* extendOrSel(FileLine* fl, AstNode* rhsp, AstNode* cmpWidthp) { if (cmpWidthp->width() > rhsp->width()) { - rhsp = (rhsp->isSigned() ? static_cast(new AstExtendS(fl, rhsp)) - : static_cast(new AstExtend(fl, rhsp))); + rhsp = (rhsp->isSigned() ? static_cast(new AstExtendS{fl, rhsp}) + : static_cast(new AstExtend{fl, rhsp})); // Need proper widthMin, which may differ from AstSel created above rhsp->dtypeFrom(cmpWidthp); } else if (cmpWidthp->width() < rhsp->width()) { - rhsp = new AstSel(fl, rhsp, 0, cmpWidthp->width()); + rhsp = new AstSel{fl, rhsp, 0, cmpWidthp->width()}; // Need proper widthMin, which may differ from AstSel created above rhsp->dtypeFrom(cmpWidthp); } @@ -610,12 +610,12 @@ void V3Inst::checkOutputShort(AstPin* nodep) { void V3Inst::instAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { InstVisitor visitor(nodep); } // Destruct before checking + { InstVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("inst", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } void V3Inst::dearrayAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { InstDeVisitor visitor(nodep); } // Destruct before checking + { InstDeVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("dearray", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3InstrCount.cpp b/src/V3InstrCount.cpp index b26628cbf..49e67e0d7 100644 --- a/src/V3InstrCount.cpp +++ b/src/V3InstrCount.cpp @@ -294,7 +294,7 @@ private: }; uint32_t V3InstrCount::count(AstNode* nodep, bool assertNoDups, std::ostream* osp) { - InstrCountVisitor visitor(nodep, assertNoDups, osp); + InstrCountVisitor visitor{nodep, assertNoDups, osp}; if (osp) InstrCountDumpVisitor dumper(nodep, osp); return visitor.instrCount(); } diff --git a/src/V3Life.cpp b/src/V3Life.cpp index c3dafa9f7..af09be5ea 100644 --- a/src/V3Life.cpp +++ b/src/V3Life.cpp @@ -461,12 +461,12 @@ private: virtual void visit(AstCFunc* nodep) override { if (nodep->entryPoint()) { // Usage model 1: Simulate all C code, doing lifetime analysis - LifeVisitor visitor(nodep, m_statep); + LifeVisitor visitor{nodep, m_statep}; } } virtual void visit(AstNodeProcedure* nodep) override { // Usage model 2: Cleanup basic blocks - LifeVisitor visitor(nodep, m_statep); + LifeVisitor visitor{nodep, m_statep}; } virtual void visit(AstVar*) override {} // Accelerate virtual void visit(AstNodeStmt*) override {} // Accelerate @@ -489,7 +489,7 @@ void V3Life::lifeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { LifeState state; - LifeTopVisitor visitor(nodep, &state); + LifeTopVisitor visitor{nodep, &state}; } // Destruct before checking V3Global::dumpCheckGlobalTree("life", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3LifePost.cpp b/src/V3LifePost.cpp index 86bfe48b3..f8306c38f 100644 --- a/src/V3LifePost.cpp +++ b/src/V3LifePost.cpp @@ -271,7 +271,7 @@ private: squashAssignposts(); // Replace any node4p varscopes with the new scope - LifePostElimVisitor visitor(nodep); + LifePostElimVisitor visitor{nodep}; } virtual void visit(AstVarRef* nodep) override { // Consumption/generation of a variable, @@ -348,6 +348,6 @@ public: void V3LifePost::lifepostAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); // Mark redundant AssignPost - { LifePostDlyVisitor visitor(nodep); } // Destruct before checking + { LifePostDlyVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("life_post", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index 0713c4d70..c6ee709b3 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -523,5 +523,5 @@ public: void V3LinkCells::link(AstNetlist* nodep, VInFilter* filterp, V3ParseSym* parseSymp) { UINFO(4, __FUNCTION__ << ": " << endl); - LinkCellsVisitor visitor(nodep, filterp, parseSymp); + LinkCellsVisitor visitor{nodep, filterp, parseSymp}; } diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 33a576ebc..d8d13d9c5 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -165,7 +165,7 @@ public: void dump(const string& nameComment = "linkdot", bool force = false) { if (debug() >= 6 || force) { const string filename = v3Global.debugFilename(nameComment) + ".txt"; - const std::unique_ptr logp(V3File::new_ofstream(filename)); + const std::unique_ptr logp{V3File::new_ofstream(filename)}; if (logp->fail()) v3fatal("Can't write " << filename); std::ostream& os = *logp; m_syms.dump(os); @@ -1766,7 +1766,7 @@ void LinkDotState::computeIfaceModSyms() { for (const auto& itr : m_ifaceModSyms) { AstIface* nodep = itr.first; VSymEnt* symp = itr.second; - LinkDotIfaceVisitor(nodep, symp, this); + LinkDotIfaceVisitor{nodep, symp, this}; } m_ifaceModSyms.clear(); } @@ -3042,13 +3042,13 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot.tree")); } LinkDotState state(rootp, step); - LinkDotFindVisitor visitor(rootp, &state); + LinkDotFindVisitor visitor{rootp, &state}; if (LinkDotState::debug() >= 5 || v3Global.opt.dumpTree() >= 9) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-find.tree")); } if (step == LDS_PRIMARY || step == LDS_PARAMED) { // Initial link stage, resolve parameters - LinkDotParamVisitor visitors(rootp, &state); + LinkDotParamVisitor visitors{rootp, &state}; if (LinkDotState::debug() >= 5 || v3Global.opt.dumpTree() >= 9) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-param.tree")); } @@ -3056,7 +3056,7 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) { } else if (step == LDS_SCOPED) { // Well after the initial link when we're ready to operate on the flat design, // process AstScope's. This needs to be separate pass after whole hierarchy graph created. - LinkDotScopeVisitor visitors(rootp, &state); + LinkDotScopeVisitor visitors{rootp, &state}; v3Global.assertScoped(true); if (LinkDotState::debug() >= 5 || v3Global.opt.dumpTree() >= 9) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-scoped.tree")); @@ -3069,5 +3069,5 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) { state.computeIfaceVarSyms(); state.computeScopeAliases(); state.dump(); - LinkDotResolveVisitor visitorb(rootp, &state); + LinkDotResolveVisitor visitorb{rootp, &state}; } diff --git a/src/V3LinkInc.cpp b/src/V3LinkInc.cpp index 00e9ef6ea..2317218e7 100644 --- a/src/V3LinkInc.cpp +++ b/src/V3LinkInc.cpp @@ -62,7 +62,6 @@ private: AstNode* m_insStmtp = nullptr; // Where to insert statement bool m_unsupportedHere = false; // Used to detect where it's not supported yet -private: void insertBeforeStmt(AstNode* nodep, AstNode* newp) { // Return node that must be visited, if any // See also AstNode::addBeforeStmt; this predates that function @@ -246,6 +245,6 @@ public: void V3LinkInc::linkIncrements(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { LinkIncVisitor bvisitor(nodep); } // Destruct before checking + { LinkIncVisitor bvisitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("linkInc", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index c81bf09f8..5d4a35425 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -287,6 +287,6 @@ public: void V3LinkJump::linkJump(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { LinkJumpVisitor bvisitor(nodep); } // Destruct before checking + { LinkJumpVisitor bvisitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("link", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index ae87110db..9d9fadace 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -298,12 +298,12 @@ public: void V3LinkLValue::linkLValue(AstNetlist* nodep) { UINFO(4, __FUNCTION__ << ": " << endl); - { LinkLValueVisitor visitor(nodep, VAccess::NOCHANGE); } // Destruct before checking + { LinkLValueVisitor visitor{nodep, VAccess::NOCHANGE}; } // Destruct before checking V3Global::dumpCheckGlobalTree("linklvalue", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } void V3LinkLValue::linkLValueSet(AstNode* nodep) { // Called by later link functions when it is known a node needs // to be converted to a lvalue. UINFO(9, __FUNCTION__ << ": " << endl); - LinkLValueVisitor visitor(nodep, VAccess::WRITE); + LinkLValueVisitor visitor{nodep, VAccess::WRITE}; } diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index 52a0ac057..d83627ef3 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -62,7 +62,7 @@ void V3LinkLevel::modSortByLevel() { "--top-module to select top." << V3Error::warnContextNone()); for (AstNode* alsop : tops) { - std::cout << secp->warnMore() << "... Top module " << alsop->prettyNameQ() << endl + std::cerr << secp->warnMore() << "... Top module " << alsop->prettyNameQ() << endl << alsop->warnContextSecondary(); } } @@ -100,10 +100,16 @@ void V3LinkLevel::timescaling(const ModVec& mods) { if (unit.isNone()) unit = VTimescale(VTimescale::TS_DEFAULT); v3Global.rootp()->timeunit(unit); + bool dunitTimed = false; // $unit had a timeunit + if (const AstPackage* const upkgp = v3Global.rootp()->dollarUnitPkgp()) { + if (!upkgp->timeunit().isNone()) dunitTimed = true; + } + for (AstNodeModule* nodep : mods) { if (!v3Global.opt.timeOverrideUnit().isNone()) nodep->timeunit(unit); if (nodep->timeunit().isNone()) { if (modTimedp // Got previous + && !dunitTimed && ( // unit doesn't already include an override v3Global.opt.timeOverrideUnit().isNone() && v3Global.opt.timeDefaultUnit().isNone()) diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 310a5ff37..22cc38dc8 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -656,6 +656,6 @@ public: void V3LinkParse::linkParse(AstNetlist* rootp) { UINFO(4, __FUNCTION__ << ": " << endl); - { LinkParseVisitor visitor(rootp); } // Destruct before checking + { LinkParseVisitor visitor{rootp}; } // Destruct before checking V3Global::dumpCheckGlobalTree("linkparse", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index e8713e179..70067cd79 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -52,6 +52,7 @@ private: AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside AstNodeCoverOrAssert* m_assertp = nullptr; // Current assertion int m_senitemCvtNum = 0; // Temporary signal counter + bool m_underGenerate = false; // Under GenFor/GenIf // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -111,6 +112,7 @@ private: virtual void visit(AstNodeFTask* nodep) override { // NodeTask: Remember its name for later resolution + if (m_underGenerate) nodep->underGenerate(true); // Remember the existing symbol table scope if (m_classp) { if (nodep->name() == "pre_randomize" || nodep->name() == "post_randomize") { @@ -520,6 +522,17 @@ private: // virtual void visit(AstModport* nodep) override { ... } // We keep Modport's themselves around for XML dump purposes + virtual void visit(AstGenFor* nodep) override { + VL_RESTORER(m_underGenerate); + m_underGenerate = true; + iterateChildren(nodep); + } + virtual void visit(AstGenIf* nodep) override { + VL_RESTORER(m_underGenerate); + m_underGenerate = true; + iterateChildren(nodep); + } + virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } public: @@ -573,8 +586,8 @@ public: void V3LinkResolve::linkResolve(AstNetlist* rootp) { UINFO(4, __FUNCTION__ << ": " << endl); { - LinkResolveVisitor visitor(rootp); - LinkBotupVisitor visitorb(rootp); + LinkResolveVisitor visitor{rootp}; + LinkBotupVisitor visitorb{rootp}; } // Destruct before checking V3Global::dumpCheckGlobalTree("linkresolve", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3Localize.cpp b/src/V3Localize.cpp index 0563c9866..deb1f301e 100644 --- a/src/V3Localize.cpp +++ b/src/V3Localize.cpp @@ -201,6 +201,6 @@ public: void V3Localize::localizeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { LocalizeVisitor visitor(nodep); } // Destruct before checking + { LocalizeVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("localize", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3MergeCond.cpp b/src/V3MergeCond.cpp index 0cb527c42..57b5dc511 100644 --- a/src/V3MergeCond.cpp +++ b/src/V3MergeCond.cpp @@ -40,6 +40,8 @@ // 'lhs = cond & value' is actually 'lhs = cond ? value : 1'd0' // 'lhs = cond' is actually 'lhs = cond ? 1'd1 : 1'd0'. // +// Also merges consecutive AstNodeIf statements with the same condition. +// //************************************************************************* #include "config_build.h" @@ -52,36 +54,38 @@ //###################################################################### +enum class Mergeable { + YES, // Tree can be merged + NO_COND_ASSIGN, // Tree cannot be merged because it contains an assignment to a condition + NO_IMPURE // Tree cannot be merged because it contains an impure node +}; + class CheckMergeableVisitor final : public AstNVisitor { private: // STATE - bool m_mergeable - = false; // State tracking whether tree being processed is a mergeable condition + bool m_condAssign = false; // Does this tree contain an assignment to a condition variable?? + bool m_impure = false; // Does this tree contain an impure node? // METHODS VL_DEBUG_FUNC; // Declare debug() - void clearMergeable(const AstNode* nodep, const char* reason) { - UASSERT_OBJ(m_mergeable, nodep, "Should have short-circuited traversal"); - m_mergeable = false; - UINFO(9, "Clearing mergeable on " << nodep << " due to " << reason << endl); - } - // VISITORS virtual void visit(AstNode* nodep) override { - if (!m_mergeable) return; + if (m_impure) return; // Clear if node is impure if (!nodep->isPure()) { - clearMergeable(nodep, "impure"); + UINFO(9, "Not mergeable due to impure node" << nodep << endl); + m_impure = true; return; } iterateChildrenConst(nodep); } virtual void visit(AstVarRef* nodep) override { - if (!m_mergeable) return; + if (m_impure || m_condAssign) return; // Clear if it's an LValue referencing a marked variable if (nodep->access().isWriteOrRW() && nodep->varp()->user1()) { - clearMergeable(nodep, "might modify condition"); + UINFO(9, "Not mergeable due assignment to condition" << nodep << endl); + m_condAssign = true; } } @@ -91,10 +95,17 @@ public: // Return false if this node should not be merged at all because: // - It contains an impure expression // - It contains an LValue referencing the condition - bool operator()(const AstNodeAssign* node) { - m_mergeable = true; - iterateChildrenConst(const_cast(node)); - return m_mergeable; + Mergeable operator()(const AstNode* node) { + m_condAssign = false; + m_impure = false; + iterateChildrenConst(const_cast(node)); + if (m_impure) { // Impure is stronger than cond assign + return Mergeable::NO_IMPURE; + } else if (m_condAssign) { + return Mergeable::NO_COND_ASSIGN; + } else { + return Mergeable::YES; + } } }; @@ -109,7 +120,7 @@ private: public: // Remove marks from AstVars (clear user1) - void clear() { AstNode::user1ClearTree(); } + static void clear() { AstNode::user1ClearTree(); } // Mark all AstVars referenced by setting user1 void mark(AstNode* node) { iterate(node); } @@ -118,8 +129,10 @@ public: class MergeCondVisitor final : public AstNVisitor { private: // NODE STATE - // AstVar::user1 -> Flag set for variables referenced by m_mgCondp + // AstVar::user1 -> Flag set for variables referenced by m_mgCondp + // AstNode::user2 -> Flag marking node as included in merge because cheap to duplicate AstUser1InUse m_user1InUse; + AstUser2InUse m_user2InUse; // STATE VDouble0 m_statMerges; // Statistic tracking @@ -142,7 +155,7 @@ private: // it is in a supported position, which are: // - RHS is the Cond // - RHS is And(Const, Cond). This And is inserted often by V3Clean. - AstNodeCond* extractCond(AstNode* rhsp) { + static AstNodeCond* extractCond(AstNode* rhsp) { if (AstNodeCond* const condp = VN_CAST(rhsp, NodeCond)) { return condp; } else if (AstAnd* const andp = VN_CAST(rhsp, And)) { @@ -153,16 +166,57 @@ private: return nullptr; } - // Apply (_ & 1'b1). This is necessary because this pass is after V3Clean, - // and sometimes we have an AstAnd with a 1-bit condition on one side, but - // a more than 1-bit value on the other side, so we need to keep only the - // LSB. Ideally we would only do this iff the node is known not to be 1-bit - // wide, but working that out here is a bit difficult. As this masking is - // rarely required (only when trying to merge a "cond & value" with an - // earlier ternary), we will just always mask it for safety. - AstNode* maskLsb(AstNode* nodep) { + // Predicate to check if an expression yields only 0 or 1 (i.e.: a 1-bit value) + static bool yieldsOneOrZero(const AstNode* nodep) { + UASSERT_OBJ(!nodep->isWide(), nodep, "Cannot handle wide nodes"); + if (const AstConst* const constp = VN_CAST_CONST(nodep, Const)) { + return constp->num().toUQuad() <= 1; + } + if (const AstVarRef* const vrefp = VN_CAST_CONST(nodep, VarRef)) { + AstVar* const varp = vrefp->varp(); + return varp->widthMin() == 1 && !varp->dtypep()->isSigned(); + } + if (const AstShiftR* const shiftp = VN_CAST_CONST(nodep, ShiftR)) { + // Shift right by width - 1 or more + if (const AstConst* const constp = VN_CAST_CONST(shiftp->rhsp(), Const)) { + const AstVarRef* const vrefp = VN_CAST_CONST(shiftp->lhsp(), VarRef); + const int width = vrefp && !vrefp->varp()->dtypep()->isSigned() + ? vrefp->varp()->widthMin() + : shiftp->width(); + if (constp->toSInt() >= width - 1) return true; + } + return false; + } + if (VN_IS(nodep, Eq) || VN_IS(nodep, Neq) || VN_IS(nodep, Lt) || VN_IS(nodep, Lte) + || VN_IS(nodep, Gt) || VN_IS(nodep, Gte)) { + return true; + } + if (const AstNodeBiop* const biopp = VN_CAST_CONST(nodep, NodeBiop)) { + if (VN_IS(nodep, And)) + return yieldsOneOrZero(biopp->lhsp()) || yieldsOneOrZero(biopp->rhsp()); + if (VN_IS(nodep, Or) || VN_IS(nodep, Xor)) + return yieldsOneOrZero(biopp->lhsp()) && yieldsOneOrZero(biopp->rhsp()); + return false; + } + if (const AstNodeCond* const condp = VN_CAST_CONST(nodep, NodeCond)) { + return yieldsOneOrZero(condp->expr1p()) && yieldsOneOrZero(condp->expr2p()); + } + if (const AstCCast* const castp = VN_CAST_CONST(nodep, CCast)) { + // Cast never sign extends + return yieldsOneOrZero(castp->lhsp()); + } + return false; + } + + // Apply (1'b1 & _) cleaning mask if necessary. This is required because this pass is after + // V3Clean, and sometimes we have an AstAnd with a 1-bit condition on one side, but a more + // than 1-bit value on the other side, so we need to keep only the LSB. + static AstNode* maskLsb(AstNode* nodep) { + if (yieldsOneOrZero(nodep)) return nodep; + // Otherwise apply masking AstNode* const maskp = new AstConst(nodep->fileline(), AstConst::BitTrue()); - return new AstAnd(nodep->fileline(), nodep, maskp); + // Mask on left, as conventional + return new AstAnd(nodep->fileline(), maskp, nodep); } // Fold the RHS expression assuming the given condition state. Unlink bits @@ -179,35 +233,59 @@ private: } if (AstAnd* const andp = VN_CAST(rhsp, And)) { UASSERT_OBJ(andp->rhsp() == condp, rhsp, "Should not try to fold this"); - return new AstAnd(andp->fileline(), andp->lhsp()->cloneTree(false), resp); + return new AstAnd{andp->fileline(), andp->lhsp()->cloneTree(false), resp}; } } else if (AstAnd* const andp = VN_CAST(rhsp, And)) { if (andp->lhsp()->sameTree(m_mgCondp)) { return condTrue ? maskLsb(andp->rhsp()->unlinkFrBack()) - : new AstConst(rhsp->fileline(), AstConst::BitFalse()); + : new AstConst{rhsp->fileline(), AstConst::BitFalse()}; } else { UASSERT_OBJ(andp->rhsp()->sameTree(m_mgCondp), rhsp, "AstAnd doesn't hold condition expression"); return condTrue ? maskLsb(andp->lhsp()->unlinkFrBack()) - : new AstConst(rhsp->fileline(), AstConst::BitFalse()); + : new AstConst{rhsp->fileline(), AstConst::BitFalse()}; } + } else if (VN_IS(rhsp, WordSel) || VN_IS(rhsp, VarRef) || VN_IS(rhsp, Const)) { + return rhsp->cloneTree(false); } + rhsp->dumpTree("Don't know how to fold expression: "); rhsp->v3fatalSrc("Don't know how to fold expression"); } - void mergeEnd() { - UASSERT(m_mgFirstp, "mergeEnd without list"); + void mergeEnd(int lineno) { + UASSERT(m_mgFirstp, "mergeEnd without list " << lineno); + // We might want to recursively merge an AstIf. We stash it in this variable. + AstNodeIf* recursivep = nullptr; + // Drop leading cheap nodes. These were only added in the hope of finding + // an earlier reduced form, but we failed to do so. + while (m_mgFirstp->user2() && m_mgFirstp != m_mgLastp) { + AstNode* const backp = m_mgFirstp; + m_mgFirstp = m_mgFirstp->nextp(); + --m_listLenght; + UASSERT_OBJ(m_mgFirstp && m_mgFirstp->backp() == backp, m_mgLastp, + "The list should have a non-cheap element"); + } + // Drop trailing cheap nodes. These were only added in the hope of finding + // a later conditional to merge, but we failed to do so. + while (m_mgLastp->user2() && m_mgFirstp != m_mgLastp) { + AstNode* const nextp = m_mgLastp; + m_mgLastp = m_mgLastp->backp(); + --m_listLenght; + UASSERT_OBJ(m_mgLastp && m_mgLastp->nextp() == nextp, m_mgFirstp, + "Cheap assignment should not be at the front of the list"); + } // Merge if list is longer than one node if (m_mgFirstp != m_mgLastp) { UINFO(6, "MergeCond - First: " << m_mgFirstp << " Last: " << m_mgLastp << endl); ++m_statMerges; if (m_listLenght > m_statLongestList) m_statLongestList = m_listLenght; + // We need a copy of the condition in the new equivalent 'if' statement, + // and we also need to keep track of it for comparisons later. + m_mgCondp = m_mgCondp->cloneTree(false); // Create equivalent 'if' statement and insert it before the first node - AstIf* const ifp - = new AstIf(m_mgCondp->fileline(), m_mgCondp->unlinkFrBack(), nullptr, nullptr); - m_mgFirstp->replaceWith(ifp); - ifp->addNextHere(m_mgFirstp); + AstIf* const resultp = new AstIf(m_mgCondp->fileline(), m_mgCondp, nullptr, nullptr); + m_mgFirstp->addHereThisAsNext(resultp); // Unzip the list and insert under branches AstNode* nextp = m_mgFirstp; do { @@ -222,18 +300,37 @@ private: } // Count ++m_statMergedItems; - // Unlink RHS and clone to get the 2 assignments (reusing currp) - AstNodeAssign* const thenp = VN_CAST(currp, NodeAssign); - AstNode* const rhsp = thenp->rhsp()->unlinkFrBack(); - AstNodeAssign* const elsep = thenp->cloneTree(false); - // Construct the new RHSs and add to branches - thenp->rhsp(foldAndUnlink(rhsp, true)); - elsep->rhsp(foldAndUnlink(rhsp, false)); - ifp->addIfsp(thenp); - ifp->addElsesp(elsep); - // Cleanup - VL_DO_DANGLING(rhsp->deleteTree(), rhsp); + if (AstNodeAssign* const assignp = VN_CAST(currp, NodeAssign)) { + // Unlink RHS and clone to get the 2 assignments (reusing assignp) + AstNode* const rhsp = assignp->rhsp()->unlinkFrBack(); + AstNodeAssign* const thenp = assignp; + AstNodeAssign* const elsep = assignp->cloneTree(false); + // Construct the new RHSs and add to branches + thenp->rhsp(foldAndUnlink(rhsp, true)); + elsep->rhsp(foldAndUnlink(rhsp, false)); + resultp->addIfsp(thenp); + resultp->addElsesp(elsep); + // Cleanup + VL_DO_DANGLING(rhsp->deleteTree(), rhsp); + } else { + AstNodeIf* const ifp = VN_CAST(currp, NodeIf); + UASSERT_OBJ(ifp, currp, "Must be AstNodeIf"); + // Move branch contents under new if + if (AstNode* const listp = ifp->ifsp()) { + resultp->addIfsp(listp->unlinkFrBackWithNext()); + } + if (AstNode* const listp = ifp->elsesp()) { + resultp->addElsesp(listp->unlinkFrBackWithNext()); + } + // Cleanup + VL_DO_DANGLING(ifp->deleteTree(), ifp); + } } while (nextp); + // Recursively merge the resulting AstIf + recursivep = resultp; + } else if (AstNodeIf* const ifp = VN_CAST(m_mgFirstp, NodeIf)) { + // There was nothing to merge this AstNodeIf with, but try to merge it's branches + recursivep = ifp; } // Reset state m_mgFirstp = nullptr; @@ -241,82 +338,185 @@ private: m_mgLastp = nullptr; m_mgNextp = nullptr; m_markVars.clear(); + AstNode::user2ClearTree(); + // Merge recursively within the branches + if (recursivep) { + iterateAndNextNull(recursivep->ifsp()); + // Close list, if there is one at the end of the then branch + if (m_mgFirstp) mergeEnd(__LINE__); + iterateAndNextNull(recursivep->elsesp()); + // Close list, if there is one at the end of the else branch + if (m_mgFirstp) mergeEnd(__LINE__); + } } - void addToList(AstNode* nodep, AstNode* condp) { + // Check if the node can be simplified if included under the if + bool isSimplifiableNode(AstNode* nodep) { + UASSERT_OBJ(m_mgFirstp, nodep, "Cannot check with empty list"); + if (AstNodeAssign* const assignp = VN_CAST(nodep, NodeAssign)) { + // If it's an assignment to a 1-bit signal, try reduced forms + if (assignp->lhsp()->widthMin() == 1) { + // Is it a 'lhs = cond & value' or 'lhs = value & cond'? + if (AstAnd* const andp = VN_CAST(assignp->rhsp(), And)) { + if (andp->lhsp()->sameTree(m_mgCondp) || andp->rhsp()->sameTree(m_mgCondp)) { + return true; + } + } + // Is it simply 'lhs = cond'? + if (assignp->rhsp()->sameTree(m_mgCondp)) return true; + } + } + return false; + } + + // Check if this node is cheap enough that duplicating it in two branches of an + // AstIf and is hence not likely to cause a performance degradation if doing so. + bool isCheapNode(AstNode* nodep) const { + if (VN_IS(nodep, Comment)) return true; + if (AstNodeAssign* const assignp = VN_CAST(nodep, NodeAssign)) { + // Check LHS + AstNode* lhsp = assignp->lhsp(); + while (AstWordSel* const wselp = VN_CAST(lhsp, WordSel)) { + // WordSel index is not constant, so might be expensive + if (!VN_IS(wselp->bitp(), Const)) return false; + lhsp = wselp->fromp(); + } + // LHS is not a VarRef, so might be expensive + if (!VN_IS(lhsp, VarRef)) return false; + + // Check RHS + AstNode* rhsp = assignp->rhsp(); + while (AstWordSel* const wselp = VN_CAST(rhsp, WordSel)) { + // WordSel index is not constant, so might be expensive + if (!VN_IS(wselp->bitp(), Const)) return false; + rhsp = wselp->fromp(); + } + // RHS is not a VarRef or Constant so might be expensive + if (!VN_IS(rhsp, VarRef) && !VN_IS(rhsp, Const)) return false; + + // Otherwise it is a cheap assignment + return true; + } + return false; + } + + void addToList(AstNode* nodep, AstNode* condp, int line) { // Set up head of new list if node is first in list if (!m_mgFirstp) { - UASSERT_OBJ(condp, nodep, "Cannot start new list without condition"); + UASSERT_OBJ(condp, nodep, "Cannot start new list without condition " << line); m_mgFirstp = nodep; m_mgCondp = condp; m_listLenght = 0; m_markVars.mark(condp); + // Add any preceding nodes to the list that would allow us to extend the merge range + for (;;) { + AstNode* const backp = m_mgFirstp->backp(); + if (!backp || backp->nextp() != m_mgFirstp) break; // Don't move up the tree + if (m_checkMergeable(backp) != Mergeable::YES) break; + if (isSimplifiableNode(backp)) { + ++m_listLenght; + m_mgFirstp = backp; + } else if (isCheapNode(backp)) { + backp->user2(1); + ++m_listLenght; + m_mgFirstp = backp; + } else { + break; + } + } } // Add node ++m_listLenght; // Track end of list m_mgLastp = nodep; - // Set up expected next node in list. Skip over any comments, (inserted - // by V3Order before always blocks) + // Set up expected next node in list. m_mgNextp = nodep->nextp(); - while (m_mgNextp && VN_IS(m_mgNextp, Comment)) { m_mgNextp = m_mgNextp->nextp(); } // If last under parent, done with current list - if (!m_mgNextp) mergeEnd(); + if (!m_mgNextp) mergeEnd(__LINE__); + } + + // If this node is the next expected node and is helpful to add to the list, do so, + // otherwise end the current merge. Return ture if added, false if ended merge. + bool addIfHelpfulElseEndMerge(AstNode* nodep) { + UASSERT_OBJ(m_mgFirstp, nodep, "List must be open"); + if (m_mgNextp == nodep) { + if (isSimplifiableNode(nodep)) { + addToList(nodep, nullptr, __LINE__); + return true; + } + if (isCheapNode(nodep)) { + nodep->user2(1); + addToList(nodep, nullptr, __LINE__); + return true; + } + } + // Not added to list, so we are done with the current list + mergeEnd(__LINE__); + return false; + } + + bool checkOrMakeMergeable(AstNode* nodep) { + const Mergeable reason = m_checkMergeable(nodep); + // If meregeable, we are done + if (reason == Mergeable::YES) return true; + // Node not mergeable. + // If no current list, then this node is just special, move on. + if (!m_mgFirstp) return false; + // Otherwise finish current list + mergeEnd(__LINE__); + // If a tree was not mergeable due to an assignment to a condition, + // then finishing the current list makes it mergeable again. + return reason == Mergeable::NO_COND_ASSIGN; + } + + void mergeEndIfIncompatible(AstNode* nodep, AstNode* condp) { + if (m_mgFirstp && (m_mgNextp != nodep || !condp->sameTree(m_mgCondp))) { + // Node in different list, or has different condition. Finish current list. + mergeEnd(__LINE__); + } } // VISITORS virtual void visit(AstNodeAssign* nodep) override { AstNode* const rhsp = nodep->rhsp(); if (AstNodeCond* const condp = extractCond(rhsp)) { - if (!m_checkMergeable(nodep)) { - // Node not mergeable. - // If no current list, then this node is just special, move on. - if (!m_mgFirstp) return; - // Otherwise finish current list - mergeEnd(); - // Finishing the list might make the node mergeable again, e.g. - // if the reason we could not merge was due to the condition - // being assigned, so check again and stop only if still no. - if (!m_checkMergeable(nodep)) return; - } - if (m_mgFirstp && (m_mgNextp != nodep || !condp->condp()->sameTree(m_mgCondp))) { - // Node in different list, or has different condition. - // Finish current list, addToList will start a new one. - mergeEnd(); - } + // Check if mergeable + if (!checkOrMakeMergeable(nodep)) return; + // Close potentially incompatible pending merge + mergeEndIfIncompatible(nodep, condp->condp()); // Add current node - addToList(nodep, condp->condp()); + addToList(nodep, condp->condp(), __LINE__); } else if (m_mgFirstp) { - // RHS is not a conditional, but we already started a list. - // If it's a 1-bit signal, and a mergeable assignment, try reduced forms - if (m_mgNextp == nodep && rhsp->widthMin() == 1 && m_checkMergeable(nodep)) { - // Is it a 'lhs = cond & value' or 'lhs = value & cond'? - if (AstAnd* const andp = VN_CAST(rhsp, And)) { - if (andp->lhsp()->sameTree(m_mgCondp) || andp->rhsp()->sameTree(m_mgCondp)) { - addToList(nodep, nullptr); - return; - } - } - // Is it simply 'lhs = cond'? - if (rhsp->sameTree(m_mgCondp)) { - addToList(nodep, nullptr); - return; - } - } - // Not added to list, so we are done with the current list - mergeEnd(); + addIfHelpfulElseEndMerge(nodep); } } - virtual void visit(AstComment*) override {} // Skip over comments + + virtual void visit(AstNodeIf* nodep) override { + // Check if mergeable + if (!checkOrMakeMergeable(nodep)) { + // If not mergeable, try to merge the branches + iterateAndNextNull(nodep->ifsp()); + iterateAndNextNull(nodep->elsesp()); + return; + } + // Close potentially incompatible pending merge + mergeEndIfIncompatible(nodep, nodep->condp()); + // Add current node + addToList(nodep, nodep->condp(), __LINE__); + } + // For speed, only iterate what is necessary. virtual void visit(AstNetlist* nodep) override { iterateAndNextNull(nodep->modulesp()); } virtual void visit(AstNodeModule* nodep) override { iterateAndNextNull(nodep->stmtsp()); } virtual void visit(AstCFunc* nodep) override { iterateChildren(nodep); // Close list, if there is one at the end of the function - if (m_mgFirstp) mergeEnd(); + if (m_mgFirstp) mergeEnd(__LINE__); + } + virtual void visit(AstNodeStmt* nodep) override { + if (m_mgFirstp && addIfHelpfulElseEndMerge(nodep)) return; + iterateChildren(nodep); } - virtual void visit(AstNodeStmt* nodep) override { iterateChildren(nodep); } virtual void visit(AstNode* nodep) override {} public: @@ -337,6 +537,6 @@ public: void V3MergeCond::mergeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { MergeCondVisitor visitor(nodep); } + { MergeCondVisitor visitor{nodep}; } V3Global::dumpCheckGlobalTree("merge_cond", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3Name.cpp b/src/V3Name.cpp index ef90a8265..d448ad0da 100644 --- a/src/V3Name.cpp +++ b/src/V3Name.cpp @@ -142,6 +142,6 @@ public: void V3Name::nameAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { NameVisitor visitor(nodep); } // Destruct before checking + { NameVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("name", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3Number.cpp b/src/V3Number.cpp index fe0197718..595830d27 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -422,22 +422,22 @@ V3Number& V3Number::setSingleBits(char value) { } V3Number& V3Number::setAllBits0() { - for (int i = 0; i < words(); i++) { m_value[i] = {0, 0}; } + for (int i = 0; i < words(); i++) m_value[i] = {0, 0}; return *this; } V3Number& V3Number::setAllBits1() { - for (int i = 0; i < words(); i++) { m_value[i] = {~0u, 0}; } + for (int i = 0; i < words(); i++) m_value[i] = {~0U, 0}; opCleanThis(); return *this; } V3Number& V3Number::setAllBitsX() { // Use setAllBitsXRemoved if calling this based on a non-X/Z input value such as divide by zero - for (int i = 0; i < words(); i++) { m_value[i] = {~0u, ~0u}; } + for (int i = 0; i < words(); i++) m_value[i] = {~0U, ~0U}; opCleanThis(); return *this; } V3Number& V3Number::setAllBitsZ() { - for (int i = 0; i < words(); i++) { m_value[i] = {0, ~0u}; } + for (int i = 0; i < words(); i++) m_value[i] = {0, ~0U}; opCleanThis(); return *this; } @@ -895,6 +895,21 @@ uint8_t V3Number::dataByte(int byte) const { return (edataWord(byte / (VL_EDATASIZE / 8)) >> ((byte * 8) % VL_EDATASIZE)) & 0xff; } +bool V3Number::isAllZ() const { + for (int i = 0; i < width(); i++) { + if (!bitIsZ(i)) return false; + } + return true; +} +bool V3Number::isAllX() const { + uint32_t mask = hiWordMask(); + for (int i = words() - 1; i >= 0; --i) { + const ValueAndX v = m_value[i]; + if ((v.m_value & v.m_valueX) ^ mask) return false; + mask = ~0U; + } + return true; +} bool V3Number::isEqZero() const { for (int i = 0; i < words(); i++) { const ValueAndX v = m_value[i]; diff --git a/src/V3Number.h b/src/V3Number.h index 961b9c414..09b15e67b 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -319,12 +319,8 @@ public: } return false; } - bool isAllZ() const { - for (int i = 0; i < width(); i++) { - if (!bitIsZ(i)) return false; - } - return true; - } + bool isAllZ() const; + bool isAllX() const; bool isEqZero() const; bool isNeqZero() const; bool isBitsZero(int msb, int lsb) const; diff --git a/src/V3OptionParser.cpp b/src/V3OptionParser.cpp index c6eec718c..f1db000af 100644 --- a/src/V3OptionParser.cpp +++ b/src/V3OptionParser.cpp @@ -120,14 +120,12 @@ V3OptionParser::ActionIfs* V3OptionParser::find(const char* optp) { if (it != m_pimpl->m_options.end()) return it->second.get(); for (auto&& act : m_pimpl->m_options) { if (act.second->isOnOffAllowed()) { // Find starts with "-no" - const char* const nop = std::strncmp(optp, "-no", 3) ? nullptr : (optp + 3); + const char* const nop = VString::startsWith(optp, "-no") ? (optp + 3) : nullptr; if (nop && (act.first == nop || act.first == (string{"-"} + nop))) { return act.second.get(); } } else if (act.second->isPartialMatchAllowed()) { - if (!std::strncmp(optp, act.first.c_str(), act.first.length())) { - return act.second.get(); - } + if (VString::startsWith(optp, act.first)) return act.second.get(); } } return nullptr; @@ -148,7 +146,7 @@ V3OptionParser::ActionIfs& V3OptionParser::add(const std::string& opt, ARG arg) bool V3OptionParser::hasPrefixNo(const char* strp) { UASSERT(strp[0] == '-', strp << " does not start with '-'"); if (strp[1] == '-') ++strp; - return std::strncmp(strp, "-no", 3) == 0; + return VString::startsWith(strp, "-no"); } int V3OptionParser::parse(int idx, int argc, char* argv[]) { diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 3710dfd3a..6b4fc0207 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -791,6 +791,9 @@ void V3Options::notify() { if (coverage() && savable()) { cmdfl->v3error("--coverage and --savable not supported together"); } + + // Mark options as available + m_available = true; } //###################################################################### @@ -1098,6 +1101,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char DECL_OPTION("-if-depth", Set, &m_ifDepth); DECL_OPTION("-ignc", OnOff, &m_ignc); DECL_OPTION("-inline-mult", Set, &m_inlineMult); + DECL_OPTION("-instr-count-dpi", CbVal, [this, fl](int val) { + m_instrCountDpi = val; + if (m_instrCountDpi < 0) fl->v3fatal("--instr-count-dpi must be non-negative: " << val); + }); DECL_OPTION("-LDFLAGS", CbVal, callStrSetter(&V3Options::addLdLibs)); const auto setLang = [this, fl](const char* valp) { @@ -1467,7 +1474,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char i += consumed; } else { fl->v3fatal("Invalid option: " << argv[i] << parser.getSuggestion(argv[i])); - ++i; + ++i; // LCOV_EXCL_LINE } } else { // Filename @@ -1496,7 +1503,7 @@ void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) { // Read the specified -f filename and process as arguments UINFO(1, "Reading Options File " << filename << endl); - const std::unique_ptr ifp(V3File::new_ifstream(filename)); + const std::unique_ptr ifp{V3File::new_ifstream(filename)}; if (ifp->fail()) { fl->v3error("Cannot open -f command file: " + filename); return; @@ -1643,21 +1650,6 @@ string V3Options::parseFileArg(const string& optdir, const string& relfilename) //====================================================================== -//! Utility to see if we have a language extension argument and if so add it. -bool V3Options::parseLangExt(const char* swp, //!< argument text - const char* langswp, //!< option to match - const V3LangCode& lc) { //!< language code - const int len = strlen(langswp); - if (!strncmp(swp, langswp, len)) { - addLangExt(swp + len, lc); - return true; - } else { - return false; - } -} - -//====================================================================== - void V3Options::showVersion(bool verbose) { cout << version(); cout << endl; diff --git a/src/V3Options.h b/src/V3Options.h index b8b2c7082..acea42e08 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -289,6 +289,7 @@ private: int m_gateStmts = 100; // main switch: --gate-stmts int m_ifDepth = 0; // main switch: --if-depth int m_inlineMult = 2000; // main switch: --inline-mult + int m_instrCountDpi = 200; // main switch: --instr-count-dpi VOptionBool m_makeDepend; // main switch: -MMD int m_maxNumWidth = 65536; // main switch: --max-num-width int m_moduleRecursion = 100; // main switch: --module-recursion-depth @@ -361,6 +362,8 @@ private: bool m_oTable; // main switch: -Oa: lookup table creation // clang-format on + bool m_available = false; // Set to true at the end of option parsing + private: // METHODS void addArg(const string& arg); @@ -376,7 +379,6 @@ private: void coverage(bool flag) { m_coverageLine = m_coverageToggle = m_coverageUser = flag; } static bool suffixed(const string& sw, const char* arg); static string parseFileArg(const string& optdir, const string& relfilename); - bool parseLangExt(const char* swp, const char* langswp, const V3LangCode& lc); string filePathCheckOneDir(const string& modname, const string& dirname); static int stripOptionsForChildRun(const string& opt, bool forTop); @@ -403,6 +405,7 @@ public: void addVFile(const string& filename); void addForceInc(const string& filename); void notify(); + bool available() const { return m_available; } // ACCESSORS (options) bool preprocOnly() const { return m_preprocOnly; } @@ -487,6 +490,7 @@ public: int gateStmts() const { return m_gateStmts; } int ifDepth() const { return m_ifDepth; } int inlineMult() const { return m_inlineMult; } + int instrCountDpi() const { return m_instrCountDpi; } VOptionBool makeDepend() const { return m_makeDepend; } int maxNumWidth() const { return m_maxNumWidth; } int moduleRecursionDepth() const { return m_moduleRecursion; } diff --git a/src/V3Order.cpp b/src/V3Order.cpp index 011bed102..0cb9cf4d8 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -259,41 +259,23 @@ private: bool m_newClkMarked; // Flag for deciding whether a new run is needed bool m_inAss = false; // Currently inside of a assignment int m_childClkWidth = 0; // If in hasClk, width of clock signal in child - int m_rightClkWidth = 0; // Clk width on the RHS // METHODS VL_DEBUG_FUNC; // Declare debug() virtual void visit(AstNodeAssign* nodep) override { m_hasClk = false; - if (AstVarRef* varrefp = VN_CAST(nodep->rhsp(), VarRef)) { - this->visit(varrefp); - m_rightClkWidth = varrefp->width(); - if (varrefp->varp()->attrClocker() == VVarAttrClocker::CLOCKER_YES) { - if (m_inClocked) { - varrefp->v3warn( - CLKDATA, "Clock used as data (on rhs of assignment) in sequential block " - << varrefp->prettyNameQ()); - } else { - m_hasClk = true; - UINFO(5, "node is already marked as clocker " << varrefp << endl); - } - } - } else { - m_inAss = true; - m_childClkWidth = 0; - iterateAndNextNull(nodep->rhsp()); - m_rightClkWidth = m_childClkWidth; - m_inAss = false; - } - - // do the marking + m_inAss = true; + m_childClkWidth = 0; + iterateAndNextNull(nodep->rhsp()); + m_inAss = false; if (m_hasClk) { - if (nodep->lhsp()->width() > m_rightClkWidth) { + // do the marking + if (nodep->lhsp()->width() > m_childClkWidth) { nodep->v3warn(CLKDATA, "Clock is assigned to part of data signal " << nodep->lhsp()->prettyNameQ()); UINFO(4, "CLKDATA: lhs with width " << nodep->lhsp()->width() << endl); - UINFO(4, " but rhs clock with width " << m_rightClkWidth << endl); + UINFO(4, " but rhs clock with width " << m_childClkWidth << endl); return; // skip the marking } @@ -663,6 +645,7 @@ private: AstSenTree* m_comboDomainp = nullptr; // Combo activation tree AstSenTree* m_deleteDomainp = nullptr; // Delete this from tree OrderInputsVertex* m_inputsVxp = nullptr; // Top level vertex all inputs point from + OrderVarVertex* m_dpiExportTriggerVxp = nullptr; // DPI Export trigger condition vertex OrderLogicVertex* m_logicVxp = nullptr; // Current statement being tracked, nullptr=ignored AstTopScope* m_topScopep = nullptr; // Current top scope being processed AstScope* m_scopetopp = nullptr; // Scope under TOPSCOPE @@ -774,6 +757,10 @@ private: } void nodeMarkCircular(OrderVarVertex* vertexp, OrderEdge* edgep) { + // To be marked circular requires being a clock assigned in a delayed assignment, or + // having a cutable in or out edge, none of which is true for the DPI export trigger. + UASSERT(vertexp != m_dpiExportTriggerVxp, + "DPI expor trigger should not be marked circular"); AstVarScope* nodep = vertexp->varScp(); OrderLogicVertex* fromLVtxp = nullptr; OrderLogicVertex* toLVtxp = nullptr; @@ -971,6 +958,9 @@ private: // Base vertices m_activeSenVxp = nullptr; m_inputsVxp = new OrderInputsVertex(&m_graph, nullptr); + if (AstVarScope* const dpiExportTrigger = v3Global.rootp()->dpiExportTriggerp()) { + m_dpiExportTriggerVxp = newVarUserVertex(dpiExportTrigger, WV_STD); + } // iterateChildren(nodep); // Done topscope, erase extra user information @@ -1148,6 +1138,24 @@ private: } } } + virtual void visit(AstDpiExportUpdated* nodep) override { + // This is under an AstAlways, sensitive to a change in the DPI export trigger. We just + // need to add an edge to the enclosing logic vertex (the vertex for the AstAlways). + OrderVarVertex* const varVxp = newVarUserVertex(nodep->varScopep(), WV_STD); + new OrderComboCutEdge(&m_graph, m_logicVxp, varVxp); + // Only used for ordering, so we can get rid of it here + nodep->unlinkFrBack(); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } + virtual void visit(AstCCall* nodep) override { + // Calls to 'context' imported DPI function may call DPI exported functions + if (m_dpiExportTriggerVxp && nodep->funcp()->dpiImportWrapper() + && nodep->funcp()->dpiContext()) { + UASSERT_OBJ(m_logicVxp, nodep, "Call not under logic"); + new OrderEdge(&m_graph, m_logicVxp, m_dpiExportTriggerVxp, WEIGHT_NORMAL); + } + iterateChildren(nodep); + } virtual void visit(AstSenTree* nodep) override { // Having a node derived from the sentree isn't required for // correctness, it merely makes the graph better connected @@ -1177,13 +1185,13 @@ private: virtual void visit(AstAlwaysPublic* nodep) override { iterateNewStmt(nodep); } virtual void visit(AstAssignAlias* nodep) override { iterateNewStmt(nodep); } virtual void visit(AstAssignW* nodep) override { - OrderClkAssVisitor visitor(nodep); + OrderClkAssVisitor visitor{nodep}; m_inClkAss = visitor.isClkAss(); iterateNewStmt(nodep); m_inClkAss = false; } virtual void visit(AstAssignPre* nodep) override { - OrderClkAssVisitor visitor(nodep); + OrderClkAssVisitor visitor{nodep}; m_inClkAss = visitor.isClkAss(); m_inPre = true; iterateNewStmt(nodep); @@ -1191,7 +1199,7 @@ private: m_inClkAss = false; } virtual void visit(AstAssignPost* nodep) override { - OrderClkAssVisitor visitor(nodep); + OrderClkAssVisitor visitor{nodep}; m_inClkAss = visitor.isClkAss(); m_inPost = true; iterateNewStmt(nodep); @@ -1205,10 +1213,10 @@ private: iterateNewStmt(nodep); } virtual void visit(AstCFunc*) override { - // Ignore for now - // We should detect what variables are set in the function, and make - // settlement code for them, then set a global flag, so we call "settle" - // on the next evaluation loop. + // Calls to DPI exports handled with AstCCall. /* verlator public */ functions are + // ignored for now (and hence potentially mis-ordered), but could use the same or + // similar mechanism as DPI exports. Every other impure function (including those + // that may set a non-local variable) must have been inlined in V3Task. } //-------------------- virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } @@ -1545,7 +1553,7 @@ void OrderVisitor::processDomainsIterate(OrderEitherVertex* vertexp) { void OrderVisitor::processEdgeReport() { // Make report of all signal names and what clock edges they have const string filename = v3Global.debugFilename("order_edges.txt"); - const std::unique_ptr logp(V3File::new_ofstream(filename)); + const std::unique_ptr logp{V3File::new_ofstream(filename)}; if (logp->fail()) v3fatal("Can't write " << filename); // Testing emitter: V3EmitV::verilogForTree(v3Global.rootp(), *logp); @@ -1777,7 +1785,7 @@ AstActive* OrderVisitor::processMoveOneLogic(const OrderLogicVertex* lvertexp, newFuncpr->addStmtsp(nodep); if (v3Global.opt.outputSplitCFuncs()) { // Add in the number of nodes we're adding - EmitCBaseCounterVisitor visitor(nodep); + EmitCBaseCounterVisitor visitor{nodep}; newStmtsr += visitor.count(); } } @@ -2005,7 +2013,7 @@ void OrderVisitor::process() { if (false && debug()) { const string dfilename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "_INT_order"; - const std::unique_ptr logp(V3File::new_ofstream(dfilename)); + const std::unique_ptr logp{V3File::new_ofstream(dfilename)}; if (logp->fail()) v3fatal("Can't write " << dfilename); m_graph.dump(*logp); } @@ -2017,7 +2025,7 @@ void OrderVisitor::process() { void V3Order::orderAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { - OrderClkMarkVisitor markVisitor(nodep); + OrderClkMarkVisitor markVisitor{nodep}; OrderVisitor visitor; visitor.main(nodep); } // Destruct before checking diff --git a/src/V3Os.cpp b/src/V3Os.cpp index 683aa8b99..9b6e6b428 100644 --- a/src/V3Os.cpp +++ b/src/V3Os.cpp @@ -236,8 +236,8 @@ void V3Os::createDir(const string& dirname) { } void V3Os::unlinkRegexp(const string& dir, const string& regexp) { - if (DIR* dirp = opendir(dir.c_str())) { - while (struct dirent* direntp = readdir(dirp)) { + if (DIR* const dirp = opendir(dir.c_str())) { + while (struct dirent* const direntp = readdir(dirp)) { if (VString::wildmatch(direntp->d_name, regexp.c_str())) { const string fullname = dir + "/" + string(direntp->d_name); #if defined(_WIN32) || defined(__MINGW32__) @@ -273,7 +273,7 @@ string V3Os::trueRandom(size_t size) { BCRYPT_USE_SYSTEM_PREFERRED_RNG); if (!BCRYPT_SUCCESS(hr)) v3fatal("Could not acquire random data."); #else - std::ifstream is("/dev/urandom", std::ios::in | std::ios::binary); + std::ifstream is{"/dev/urandom", std::ios::in | std::ios::binary}; // This read uses the size of the buffer. // Flawfinder: ignore if (VL_UNCOVERABLE(!is.read(data, size))) { diff --git a/src/V3Param.cpp b/src/V3Param.cpp index a09dca9e4..56175a280 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -268,7 +268,7 @@ class ParamProcessor final { // METHODS VL_DEBUG_FUNC; // Declare debug() - void makeSmallNames(AstNodeModule* modp) { + static void makeSmallNames(AstNodeModule* modp) { std::vector usedLetter; usedLetter.resize(256); // Pass 1, assign first letter to each gparam's name @@ -960,26 +960,6 @@ class ParamVisitor final : public AstNVisitor { << " (IEEE 1800-2017 6.20.1): " << nodep->prettyNameQ()); } else { V3Const::constifyParamsEdit(nodep); // The variable, not just the var->init() - if (!VN_IS(nodep->valuep(), Const) - && !VN_IS(nodep->valuep(), Unbounded)) { // Complex init, like an array - // Make a new INITIAL to set the value. - // This allows the normal array/struct handling code to properly - // initialize the parameter. - nodep->addNext(new AstInitial( - nodep->fileline(), - new AstAssign(nodep->fileline(), - new AstVarRef(nodep->fileline(), nodep, VAccess::WRITE), - nodep->valuep()->cloneTree(true)))); - if (nodep->isFuncLocal()) { - // We put the initial in wrong place under a function. We - // should move the parameter out of the function and to the - // module, with appropriate dotting, but this confuses LinkDot - // (as then name isn't found later), so punt - probably can - // treat as static function variable when that is supported. - nodep->v3warn(E_UNSUPPORTED, - "Unsupported: Parameters in functions with complex assign"); - } - } } } } @@ -1223,6 +1203,6 @@ public: void V3Param::param(AstNetlist* rootp) { UINFO(2, __FUNCTION__ << ": " << endl); - { ParamVisitor visitor(rootp); } // Destruct before checking + { ParamVisitor visitor{rootp}; } // Destruct before checking V3Global::dumpCheckGlobalTree("param", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index 9a6272e45..f1c042aa8 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -108,10 +108,12 @@ void V3ParseImp::timescaleMod(FileLine* fl, AstNodeModule* modp, bool unitSet, d } } if (!unit.isNone()) { + unit = v3Global.opt.timeComputeUnit(unit); if (modp) { - modp->timeunit(v3Global.opt.timeComputeUnit(unit)); + modp->timeunit(unit); } else { - fl->v3error("timeunit/timeprecision not under a module"); + v3Global.rootp()->timeunit(unit); + unitPackage(fl)->timeunit(unit); } } v3Global.rootp()->timeprecisionMerge(fl, prec); diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index a9477921c..fd6422dce 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -301,6 +301,18 @@ public: //==== Symbol tables V3ParseSym* symp() { return m_symp; } + AstPackage* unitPackage(FileLine* fl) { + // Find one made earlier? + VSymEnt* const rootSymp = symp()->symRootp()->findIdFlat(AstPackage::dollarUnitName()); + AstPackage* pkgp; + if (!rootSymp) { + pkgp = parsep()->rootp()->dollarUnitPkgAddp(); + symp()->reinsert(pkgp, symp()->symRootp()); // Don't push/pop scope as they're global + } else { + pkgp = VN_CAST(rootSymp->nodep(), Package); + } + return pkgp; + } public: // CONSTRUCTORS diff --git a/src/V3Partition.cpp b/src/V3Partition.cpp index 0bff6bde5..f12a1bce3 100644 --- a/src/V3Partition.cpp +++ b/src/V3Partition.cpp @@ -624,8 +624,8 @@ public: static void dumpCpFilePrefixed(const V3Graph* graphp, const string& nameComment) { const string filename = v3Global.debugFilename(nameComment) + ".txt"; UINFO(1, "Writing " << filename << endl); - std::unique_ptr ofp(V3File::new_ofstream(filename)); - std::ostream* osp = &(*ofp); // &* needed to deref unique_ptr + std::unique_ptr ofp{V3File::new_ofstream(filename)}; + std::ostream* const osp = &(*ofp); // &* needed to deref unique_ptr if (osp->fail()) v3fatalStatic("Can't write " << filename); // Find start vertex with longest CP @@ -996,8 +996,7 @@ static V3GraphEdge* partBlastEdgep(GraphWay way, V3GraphEdge* edgep) { // non-transitive edges only ever increase. static void partMergeEdgesFrom(V3Graph* mtasksp, LogicMTask* recipientp, LogicMTask* donorp, V3Scoreboard* sbp) { - for (unsigned wi = 0; wi < 2; ++wi) { - const GraphWay way = wi ? GraphWay::REVERSE : GraphWay::FORWARD; + for (const auto& way : {GraphWay::FORWARD, GraphWay::REVERSE}) { for (V3GraphEdge* edgep = donorp->beginp(way); edgep; edgep = partBlastEdgep(way, edgep)) { MTaskEdge* tedgep = MTaskEdge::cast(edgep); if (sbp && !tedgep->removedFromSb()) sbp->removeElem(tedgep); @@ -2078,7 +2077,7 @@ void ThreadSchedule::dumpDotFilePrefixedAlways(const string& nameComment) const void ThreadSchedule::dumpDotFile(const string& filename) const { // This generates a file used by graphviz, https://www.graphviz.org - const std::unique_ptr logp(V3File::new_ofstream(filename)); + const std::unique_ptr logp{V3File::new_ofstream(filename)}; if (logp->fail()) v3fatal("Can't write " << filename); auto* depGraph = v3Global.rootp()->execGraphp()->depGraphp(); @@ -2766,8 +2765,8 @@ static const std::vector createThreadFunctions(const ThreadSchedule& funcp->argTypes("void* voidSelf, bool even_cycle"); // Setup vlSelf an vlSyms - funcp->addStmtsp(new AstCStmt(fl, EmitCBaseVisitor::voidSelfAssign(modp))); - funcp->addStmtsp(new AstCStmt(fl, EmitCBaseVisitor::symClassAssign())); + funcp->addStmtsp(new AstCStmt{fl, EmitCBaseVisitor::voidSelfAssign(modp)}); + funcp->addStmtsp(new AstCStmt{fl, EmitCBaseVisitor::symClassAssign()}); // Invoke each mtask scheduled to this thread from the thread function for (const ExecMTask* const mtaskp : thread) { diff --git a/src/V3PreLex.l b/src/V3PreLex.l index 81dcd763f..bb591ba9e 100644 --- a/src/V3PreLex.l +++ b/src/V3PreLex.l @@ -159,7 +159,7 @@ bom [\357\273\277] LEXP->curFilelinep()->v3warn(PROTECTED, "A '`pragma protected data_block' encrypted section was detected and will be skipped."); BEGIN(ENCBASE64); return VP_TEXT; } -("begin_protected"|"end_protected")[\n\r] { FL_FWDC; linenoInc(); BEGIN(INITIAL); return VP_TEXT; } +("begin_protected"|"end_protected"|"end")[\n\r] { FL_FWDC; linenoInc(); BEGIN(INITIAL); return VP_TEXT; } "version="[^\n\r]*[\n\r] { FL_FWDC; linenoInc(); @@ -175,6 +175,10 @@ bom [\357\273\277] linenoInc(); BEGIN(INITIAL); return VP_TEXT; } + /* end of `pragma protect */ +{wsn}+ { FL_FWDC; return VP_TEXT; } +[\n\r] { FL_FWDC; linenoInc(); BEGIN(INITIAL); return VP_TEXT; } + /* catch-all for unknown '`pragma protect' rules */ . { yyless(0); BEGIN(PRAGMAPRTERR); @@ -202,7 +206,7 @@ bom [\357\273\277] /* Catch only empty `pragma lines */ "`pragma"{wsn}*[\n\r] { - yyless(yyleng-1); FL_FWDC; + yyless(yyleng - 1); FL_FWDC; if (v3Global.opt.pedantic()) { LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "`pragma is missing a pragma_expression."); } @@ -210,12 +214,11 @@ bom [\357\273\277] /* catch all other nonempty `pragma */ "`pragma"{wsn}*[^\n\r] { - yyless(yyleng-1); FL_FWDC; + yyless(yyleng - 1); FL_FWDC; if (!v3Global.opt.preprocOnly()) BEGIN(PRAGMA); return VP_TEXT; } "protect"{wsn}* { FL_FWDC; BEGIN(PRAGMAPRT); return VP_TEXT;} - /* catch-all for unknown `pragma rules */ . { yyless(0); BEGIN(PRAGMAERR); @@ -524,7 +527,7 @@ again: } } if (debug() >= 10) { - cout << "- pp::inputToLex got=" << got << " '" << string(buf, got) << "'" << endl; + cout << "- pp::inputToLex got=" << got << " '" << std::string{buf, got} << "'" << endl; } return got; } @@ -567,7 +570,7 @@ string V3PreLex::endOfStream(bool& againr) { // Final shutdown phase for a stream, we can finally change the // current fileline to the new stream curStreamp()->m_termState = 0; - FileLine* filelinep = curFilelinep(); + FileLine* const filelinep = curFilelinep(); delete curStreamp(); m_streampStack.pop(); // Must work as size>1; EOF is entry 0 if (curStreamp()->m_eof) { @@ -590,7 +593,7 @@ string V3PreLex::endOfStream(bool& againr) { void V3PreLex::initFirstBuffer(FileLine* filelinep) { // Called from constructor to make first buffer // yy_create_buffer also sets yy_fill_buffer=1 so reads from YY_INPUT - VPreStream* streamp = new VPreStream(filelinep, this); + VPreStream* const streamp = new VPreStream{filelinep, this}; streamp->m_eof = true; m_streampStack.push(streamp); // @@ -606,7 +609,7 @@ void V3PreLex::scanNewFile(FileLine* filelinep) { yyerrorf("Recursive `define or other nested inclusion"); curStreamp()->m_eof = true; // Fake it to stop recursion } else { - VPreStream* streamp = new VPreStream(filelinep, this); + VPreStream* const streamp = new VPreStream{filelinep, this}; m_tokFilelinep = curFilelinep(); streamp->m_file = true; scanSwitchStream(streamp); @@ -615,7 +618,7 @@ void V3PreLex::scanNewFile(FileLine* filelinep) { void V3PreLex::scanBytes(const string& str) { // Note buffers also appended in ::scanBytesBack - // Not "m_buffers.push_front(string(strp,len))" as we need a `define + // Not "m_buffers.push_front(string{strp,len})" as we need a `define // to take effect immediately, in the middle of the current buffer // Also we don't use scan_bytes that would set yy_fill_buffer // which would force Flex to bypass our YY_INPUT routine. @@ -625,7 +628,7 @@ void V3PreLex::scanBytes(const string& str) { yyerrorf("Recursive `define or other nested inclusion"); curStreamp()->m_eof = true; // Fake it to stop recursion } else { - VPreStream* streamp = new VPreStream(curFilelinep(), this); + VPreStream* const streamp = new VPreStream{curFilelinep(), this}; streamp->m_buffers.push_front(str); scanSwitchStream(streamp); } @@ -649,7 +652,7 @@ string V3PreLex::currentUnreadChars() { ssize_t left = (yy_n_chars - (yy_c_buf_p - currentBuffer()->yy_ch_buf)); if (left > 0) { // left may be -1 at EOS *(yy_c_buf_p) = (yy_hold_char); - return string(yy_c_buf_p, left); + return std::string(yy_c_buf_p, left); // () narrowing conversion } else { return ""; } @@ -689,7 +692,7 @@ void V3PreLex::dumpStack() { dumpSummary(); std::stack tmpstack = LEXP->m_streampStack; while (!tmpstack.empty()) { - VPreStream* streamp = tmpstack.top(); + const VPreStream* const streamp = tmpstack.top(); cout << "- bufferStack[" << cvtToHex(streamp) << "]: " << " at=" << streamp->m_curFilelinep << " nBuf=" << streamp->m_buffers.size() << " size0=" << (streamp->m_buffers.empty() ? 0 : streamp->m_buffers.front().length()) diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp index 3bf62d303..02dbeeaa4 100644 --- a/src/V3PreProc.cpp +++ b/src/V3PreProc.cpp @@ -395,7 +395,7 @@ string V3PreProcImp::commentCleanup(const string& text) { bool V3PreProcImp::commentTokenMatch(string& cmdr, const char* strg) { int len = strlen(strg); - if (0 == strncmp(cmdr.c_str(), strg, len) && (cmdr[len] == '\0' || isspace(cmdr[len]))) { + if (VString::startsWith(cmdr, strg) && (cmdr[len] == '\0' || isspace(cmdr[len]))) { if (isspace(cmdr[len])) len++; cmdr = cmdr.substr(len); return true; @@ -423,27 +423,27 @@ void V3PreProcImp::comment(const string& text) { bool synth = false; bool vlcomment = false; - if ((cp[0] == 'v' || cp[0] == 'V') && 0 == (strncmp(cp + 1, "erilator", 8))) { + if ((cp[0] == 'v' || cp[0] == 'V') && VString::startsWith(cp + 1, "erilator")) { cp += strlen("verilator"); if (*cp == '_') { fileline()->v3error("Extra underscore in meta-comment;" " use /*verilator {...}*/ not /*verilator_{...}*/"); } vlcomment = true; - } else if (0 == (strncmp(cp, "synopsys", strlen("synopsys")))) { + } else if (VString::startsWith(cp, "synopsys")) { cp += strlen("synopsys"); synth = true; if (*cp == '_') { fileline()->v3error("Extra underscore in meta-comment;" " use /*synopsys {...}*/ not /*synopsys_{...}*/"); } - } else if (0 == (strncmp(cp, "cadence", strlen("cadence")))) { + } else if (VString::startsWith(cp, "cadence")) { cp += strlen("cadence"); synth = true; - } else if (0 == (strncmp(cp, "pragma", strlen("pragma")))) { + } else if (VString::startsWith(cp, "pragma")) { cp += strlen("pragma"); synth = true; - } else if (0 == (strncmp(cp, "ambit synthesis", strlen("ambit synthesis")))) { + } else if (VString::startsWith(cp, "ambit synthesis")) { cp += strlen("ambit synthesis"); synth = true; } else { @@ -1544,7 +1544,7 @@ int V3PreProcImp::getFinalToken(string& buf) { // Track `line const char* bufp = buf.c_str(); while (*bufp == '\n') bufp++; - if ((tok == VP_TEXT || tok == VP_LINE) && 0 == strncmp(bufp, "`line ", 6)) { + if ((tok == VP_TEXT || tok == VP_LINE) && VString::startsWith(bufp, "`line ")) { int enter; m_finFilelinep->lineDirective(bufp, enter /*ref*/); } else { diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index 897f25445..1c680382d 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -30,8 +30,8 @@ #include "V3Global.h" #include "V3Premit.h" #include "V3Ast.h" -#include "V3DupFinder.h" #include "V3Stats.h" +#include "V3UniqueNames.h" #include @@ -92,17 +92,17 @@ private: // AstNodeMath::user() -> bool. True if iterated already // AstShiftL::user2() -> bool. True if converted to conditional // AstShiftR::user2() -> bool. True if converted to conditional - // *::user4() -> See PremitAssignVisitor + // *::user3() -> See PremitAssignVisitor AstUser1InUse m_inuser1; AstUser2InUse m_inuser2; // STATE - AstNodeModule* m_modp = nullptr; // Current module AstCFunc* m_cfuncp = nullptr; // Current block AstNode* m_stmtp = nullptr; // Current statement AstWhile* m_inWhilep = nullptr; // Inside while loop, special statement additions AstTraceInc* m_inTracep = nullptr; // Inside while loop, special statement additions bool m_assignLhs = false; // Inside assignment lhs, don't breakup extracts + V3UniqueNames m_tempNames; // For generating unique temporary variable names VDouble0 m_extractedToConstPool; // Statistic tracking @@ -184,9 +184,8 @@ private: nodep->deleteTree(); ++m_extractedToConstPool; } else { - // Keep as local temporary - const string name = string("__Vtemp") + cvtToStr(m_modp->varNumGetInc()); - varp = new AstVar(fl, AstVarType::STMTTEMP, name, nodep->dtypep()); + // Keep as local temporary. Name based on hash of node for output stability. + varp = new AstVar(fl, AstVarType::STMTTEMP, m_tempNames.get(nodep), nodep->dtypep()); m_cfuncp->addInitsp(varp); // Put assignment before the referencing statement insertBeforeStmt(new AstAssign(fl, new AstVarRef(fl, varp, VAccess::WRITE), nodep)); @@ -202,16 +201,13 @@ private: // VISITORS virtual void visit(AstNodeModule* nodep) override { UINFO(4, " MOD " << nodep << endl); - UASSERT_OBJ(m_modp == nullptr, nodep, "Nested modules ?"); - m_modp = nodep; - m_cfuncp = nullptr; iterateChildren(nodep); - m_modp = nullptr; } virtual void visit(AstCFunc* nodep) override { VL_RESTORER(m_cfuncp); { m_cfuncp = nodep; + m_tempNames.reset(); iterateChildren(nodep); } } @@ -328,6 +324,10 @@ private: iterateChildren(nodep); checkNode(nodep); } + virtual void visit(AstRand* nodep) override { + iterateChildren(nodep); + checkNode(nodep); + } virtual void visit(AstUCFunc* nodep) override { iterateChildren(nodep); checkNode(nodep); @@ -412,7 +412,10 @@ private: public: // CONSTRUCTORS - explicit PremitVisitor(AstNetlist* nodep) { iterate(nodep); } + explicit PremitVisitor(AstNetlist* nodep) + : m_tempNames{"__Vtemp"} { + iterate(nodep); + } virtual ~PremitVisitor() { V3Stats::addStat("Optimizations, Prelim extracted value to ConstPool", m_extractedToConstPool); @@ -427,6 +430,6 @@ public: void V3Premit::premitAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { PremitVisitor visitor(nodep); } // Destruct before checking + { PremitVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("premit", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 1289b5a0d..6b6b1d8d4 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -246,8 +246,8 @@ public: void V3Randomize::randomizeNetlist(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { - RandomizeMarkVisitor markVisitor(nodep); - RandomizeVisitor visitor(nodep); + RandomizeMarkVisitor markVisitor{nodep}; + RandomizeVisitor visitor{nodep}; } V3Global::dumpCheckGlobalTree("randomize", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Reloop.cpp b/src/V3Reloop.cpp index e95239473..646758819 100644 --- a/src/V3Reloop.cpp +++ b/src/V3Reloop.cpp @@ -267,6 +267,6 @@ public: void V3Reloop::reloopAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { ReloopVisitor visitor(nodep); } // Destruct before checking + { ReloopVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("reloop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3Scope.cpp b/src/V3Scope.cpp index e90ed7a38..5cafafd3b 100644 --- a/src/V3Scope.cpp +++ b/src/V3Scope.cpp @@ -402,8 +402,8 @@ public: void V3Scope::scopeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { - ScopeVisitor visitor(nodep); - ScopeCleanupVisitor cleanVisitor(nodep); + ScopeVisitor visitor{nodep}; + ScopeCleanupVisitor cleanVisitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("scope", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 4fe3444ab..96967643a 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -484,8 +484,16 @@ private: return; } if (nodep->dpiImport()) { + if (m_params) { + nodep->v3error("Constant function may not be DPI import (IEEE 1800-2017 13.4.3)"); + } clearOptimizable(nodep, "DPI import functions aren't simulatable"); } + if (nodep->underGenerate()) { + nodep->v3error( + "Constant function may not be declared under generate (IEEE 1800-2017 13.4.3)"); + clearOptimizable(nodep, "Constant function called under generate"); + } checkNodeInfo(nodep); iterateChildren(nodep); } @@ -991,6 +999,7 @@ private: } } SimStackNode stackNode(nodep, &tconnects); + // cppcheck-suppress danglingLifetime m_callStack.push_back(&stackNode); // Clear output variable if (auto* const basicp = VN_CAST(funcp->fvarp(), Var)->basicp()) { diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index 695d93082..f2a809797 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -236,6 +236,6 @@ public: void V3Slice::sliceAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { SliceVisitor visitor(nodep); } // Destruct before checking + { SliceVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("slice", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Split.cpp b/src/V3Split.cpp index 4c8b1a60d..551dfd25c 100644 --- a/src/V3Split.cpp +++ b/src/V3Split.cpp @@ -932,7 +932,7 @@ protected: // Map each AstNodeIf to the set of colors (split always blocks) // it must participate in. Also find the whole set of colors. - IfColorVisitor ifColor(nodep); + IfColorVisitor ifColor{nodep}; if (ifColor.colors().size() > 1) { // Counting original always blocks rather than newly-split @@ -942,7 +942,7 @@ protected: // Visit through the original always block one more time, // and emit the split always blocks into m_replaceBlocks: - EmitSplitVisitor emitSplit(nodep, &ifColor, &(m_replaceBlocks[nodep])); + EmitSplitVisitor emitSplit{nodep, &ifColor, &(m_replaceBlocks[nodep])}; emitSplit.go(); } } @@ -964,11 +964,11 @@ private: void V3Split::splitReorderAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { ReorderVisitor visitor(nodep); } // Destruct before checking + { ReorderVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("reorder", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } void V3Split::splitAlwaysAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { SplitVisitor visitor(nodep); } // Destruct before checking + { SplitVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("split", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3SplitAs.cpp b/src/V3SplitAs.cpp index 5263a8a9a..ee9bf9236 100644 --- a/src/V3SplitAs.cpp +++ b/src/V3SplitAs.cpp @@ -144,11 +144,11 @@ private: newp->user1(true); // So we don't clone it again nodep->addNextHere(newp); { // Delete stuff we don't want in old - SplitAsCleanVisitor visitor(nodep, m_splitVscp, false); + SplitAsCleanVisitor visitor{nodep, m_splitVscp, false}; if (debug() >= 9) nodep->dumpTree(cout, "-out0: "); } { // Delete stuff we don't want in new - SplitAsCleanVisitor visitor(newp, m_splitVscp, true); + SplitAsCleanVisitor visitor{newp, m_splitVscp, true}; if (debug() >= 9) newp->dumpTree(cout, "-out1: "); } } @@ -159,7 +159,7 @@ private: AstVarScope* lastSplitVscp = nullptr; while (!nodep->user1()) { // Find any splittable variables - SplitAsFindVisitor visitor(nodep); + SplitAsFindVisitor visitor{nodep}; m_splitVscp = visitor.splitVscp(); if (m_splitVscp && m_splitVscp == lastSplitVscp) { // We did this last time! Something's stuck! @@ -194,6 +194,6 @@ public: void V3SplitAs::splitAsAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { SplitAsVisitor visitor(nodep); } // Destruct before checking + { SplitAsVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("splitas", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3SplitVar.cpp b/src/V3SplitVar.cpp index 5a3f8ab78..71818e975 100644 --- a/src/V3SplitVar.cpp +++ b/src/V3SplitVar.cpp @@ -117,6 +117,7 @@ #include "V3Global.h" #include "V3SplitVar.h" #include "V3Stats.h" +#include "V3UniqueNames.h" #include // sort #include @@ -124,17 +125,19 @@ #include struct SplitVarImpl { + // NODE STATE + // AstNodeModule::user1() -> Block number counter for generating unique names + AstUser1InUse m_user1InUse; // Only used in SplitUnpackedVarVisitor + static AstNodeAssign* newAssign(FileLine* fileline, AstNode* lhsp, AstNode* rhsp, const AstVar* varp) { if (varp->isFuncLocal() || varp->isFuncReturn()) { - return new AstAssign(fileline, lhsp, rhsp); + return new AstAssign{fileline, lhsp, rhsp}; } else { - return new AstAssignW(fileline, lhsp, rhsp); + return new AstAssignW{fileline, lhsp, rhsp}; } } - static const char* const notSplitMsg; - // These check functions return valid pointer to the reason text if a variable cannot be split. // Check if a var type can be split @@ -152,10 +155,12 @@ struct SplitVarImpl { return nullptr; } - static const char* cannotSplitConnectedPortReason(AstPin* pinp) { - AstVar* varp = pinp->modVarp(); + static const char* cannotSplitConnectedPortReason(const AstPin* pinp) { + const AstVar* const varp = pinp->modVarp(); if (!varp) return "it is not connected"; - if (const char* reason = cannotSplitVarDirectionReason(varp->direction())) return reason; + if (const char* const reason = cannotSplitVarDirectionReason(varp->direction())) { + return reason; + } return nullptr; } @@ -167,11 +172,15 @@ struct SplitVarImpl { } static const char* cannotSplitVarCommonReason(const AstVar* varp) { - if (AstNodeFTask* taskp = VN_CAST(varp->backp(), NodeFTask)) { - if (const char* reason = cannotSplitTaskReason(taskp)) return reason; + if (const AstNodeFTask* const taskp = VN_CAST(varp->backp(), NodeFTask)) { + if (const char* const reason = cannotSplitTaskReason(taskp)) return reason; + } + if (const char* const reason = cannotSplitVarTypeReason(varp->varType())) { + return reason; + } + if (const char* const reason = cannotSplitVarDirectionReason(varp->direction())) { + return reason; } - if (const char* reason = cannotSplitVarTypeReason(varp->varType())) return reason; - if (const char* reason = cannotSplitVarDirectionReason(varp->direction())) return reason; if (varp->isSigPublic()) return "it is public"; if (varp->isUsedLoopIdx()) return "it is used as a loop variable"; return nullptr; @@ -180,41 +189,47 @@ struct SplitVarImpl { static const char* cannotSplitPackedVarReason(const AstVar* varp); template - static void insertBeginCore(T_ALWAYSLIKE* ap, AstNodeStmt* stmtp, AstNodeModule* modp) { + void insertBeginCore(T_ALWAYSLIKE* ap, AstNodeStmt* stmtp, AstNodeModule* modp) { if (ap->isJustOneBodyStmt() && ap->bodysp() == stmtp) { stmtp->unlinkFrBack(); // Insert begin-end because temp value may be inserted to this block later. - const std::string name = "__VsplitVarBlk" + cvtToStr(modp->varNumGetInc()); - ap->addStmtp(new AstBegin(ap->fileline(), name, stmtp)); + const std::string name = "__VsplitVarBlk" + cvtToStr(modp->user1Inc(1)); + ap->addStmtp(new AstBegin{ap->fileline(), name, stmtp}); } } - static void insertBeginCore(AstInitial* initp, AstNodeStmt* stmtp, AstNodeModule* modp) { + void insertBeginCore(AstInitial* initp, AstNodeStmt* stmtp, AstNodeModule* modp) { if (initp->isJustOneBodyStmt() && initp->bodysp() == stmtp) { stmtp->unlinkFrBack(); // Insert begin-end because temp value may be inserted to this block later. - const std::string name = "__VsplitVarBlk" + cvtToStr(modp->varNumGetInc()); - initp->replaceWith( - new AstInitial(initp->fileline(), new AstBegin(initp->fileline(), name, stmtp))); + FileLine* const fl = initp->fileline(); + const std::string name = "__VsplitVarBlk" + cvtToStr(modp->user1Inc(1)); + initp->replaceWith(new AstInitial{fl, new AstBegin{fl, name, stmtp}}); VL_DO_DANGLING(initp->deleteTree(), initp); } } - static void insertBeginIfNecessary(AstNodeStmt* stmtp, AstNodeModule* modp) { - AstNode* backp = stmtp->backp(); - if (AstAlways* ap = VN_CAST(backp, Always)) { + void insertBeginIfNecessary(AstNodeStmt* stmtp, AstNodeModule* modp) { + AstNode* const backp = stmtp->backp(); + if (AstAlways* const ap = VN_CAST(backp, Always)) { insertBeginCore(ap, stmtp, modp); - } else if (AstAlwaysPublic* ap = VN_CAST(backp, AlwaysPublic)) { + } else if (AstAlwaysPublic* const ap = VN_CAST(backp, AlwaysPublic)) { insertBeginCore(ap, stmtp, modp); - } else if (AstInitial* ap = VN_CAST(backp, Initial)) { + } else if (AstInitial* const ap = VN_CAST(backp, Initial)) { insertBeginCore(ap, stmtp, modp); } } }; // SplitVarImpl -const char* const SplitVarImpl::notSplitMsg - = " has split_var metacomment but will not be split because "; +//###################################################################### +// Utilities required in wharious placs + +static void warnNoSplit(const AstVar* varp, const AstNode* wherep, const char* reasonp) { + wherep->v3warn(SPLITVAR, varp->prettyNameQ() + << " has split_var metacomment but will not be split because " + << reasonp << ".\n"); +} //###################################################################### // Split Unpacked Variables @@ -287,12 +302,11 @@ class UnpackRefMap final { public: using MapType = std::map, AstNodeComparator>; using MapIt = MapType::iterator; - using SetIt = MapType::value_type::second_type::iterator; private: MapType m_map; bool addCore(AstVarRef* refp, const UnpackRef& ref) { - AstVar* varp = refp->varp(); + AstVar* const varp = refp->varp(); UASSERT_OBJ(varp->attrSplitVar(), varp, " no split_var metacomment"); const MapIt it = m_map.find(varp); if (it == m_map.end()) return false; // Not registered @@ -358,23 +372,23 @@ public: v.iterate(nodep); } void visit(AstNVisitor* visitor) { - for (const auto& varp : m_vars) visitor->iterate(varp); - for (auto it = m_sels.begin(), it_end = m_sels.end(); it != it_end; ++it) { + for (AstVar* const varp : m_vars) visitor->iterate(varp); + for (AstSel* const selp : m_sels) { // If m_refs includes VarRef from ArraySel, remove it // because the VarRef would not be visited in SplitPackedVarVisitor::visit(AstSel*). - if (AstVarRef* refp = VN_CAST((*it)->fromp(), VarRef)) { + if (AstVarRef* const refp = VN_CAST(selp->fromp(), VarRef)) { m_refs.erase(refp); - } else if (AstVarRef* refp = VN_CAST((*it)->lsbp(), VarRef)) { + } else if (AstVarRef* const refp = VN_CAST(selp->lsbp(), VarRef)) { m_refs.erase(refp); - } else if (AstVarRef* refp = VN_CAST((*it)->widthp(), VarRef)) { + } else if (AstVarRef* const refp = VN_CAST(selp->widthp(), VarRef)) { m_refs.erase(refp); } - UASSERT_OBJ(reinterpret_cast((*it)->op1p()) != 1, *it, "stale"); - visitor->iterate(*it); + UASSERT_OBJ(reinterpret_cast(selp->op1p()) != 1, selp, "stale"); + visitor->iterate(selp); } - for (auto it = m_refs.begin(), it_end = m_refs.end(); it != it_end; ++it) { - UASSERT_OBJ(reinterpret_cast((*it)->op1p()) != 1, *it, "stale"); - visitor->iterate(*it); + for (AstVarRef* const vrefp : m_refs) { + UASSERT_OBJ(reinterpret_cast(vrefp->op1p()) != 1, vrefp, "stale"); + visitor->iterate(vrefp); } } }; @@ -392,15 +406,17 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { size_t m_numSplit = 0; // List for SplitPackedVarVisitor SplitVarRefsMap m_refsForPackedSplit; + V3UniqueNames m_tempNames; // For generating unique temporary variable names static AstVarRef* isTargetVref(AstNode* nodep) { - if (AstVarRef* refp = VN_CAST(nodep, VarRef)) { + if (AstVarRef* const refp = VN_CAST(nodep, VarRef)) { if (refp->varp()->attrSplitVar()) return refp; } return nullptr; } - static int outerMostSizeOfUnpackedArray(AstVar* nodep) { - AstUnpackArrayDType* dtypep = VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType); + static int outerMostSizeOfUnpackedArray(const AstVar* nodep) { + const AstUnpackArrayDType* const dtypep + = VN_CAST_CONST(nodep->dtypep()->skipRefp(), UnpackArrayDType); UASSERT_OBJ(dtypep, nodep, "Must be unapcked array"); return dtypep->elementsConst(); } @@ -425,13 +441,13 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { AstNVisitor::pushDeletep(nodep); } AstVar* newVar(FileLine* fl, AstVarType type, const std::string& name, AstNodeDType* dtp) { - AstVar* varp = new AstVar(fl, type, name, dtp); + AstVar* const varp = new AstVar{fl, type, name, dtp}; UASSERT_OBJ(m_modp, varp, "Must not nullptr"); m_refsForPackedSplit[m_modp].add(varp); return varp; } AstVarRef* newVarRef(FileLine* fl, AstVar* varp, const VAccess& access) { - AstVarRef* refp = new AstVarRef(fl, varp, access); + AstVarRef* const refp = new AstVarRef{fl, varp, access}; UASSERT_OBJ(m_modp, refp, "Must not nullptr"); m_refsForPackedSplit[m_modp].add(refp); return refp; @@ -447,6 +463,7 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { UASSERT_OBJ(!m_modp, m_modp, "Nested module declaration"); UASSERT_OBJ(m_refs.empty(), nodep, "The last module didn't finish split()"); m_modp = nodep; + m_tempNames.reset(); iterateChildren(nodep); split(); m_modp = nullptr; @@ -473,7 +490,7 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { VL_RESTORER(m_contextp); { m_contextp = nodep; - AstNodeFTask* ftaskp = nodep->taskp(); + AstNodeFTask* const ftaskp = nodep->taskp(); UASSERT_OBJ(ftaskp, nodep, "Unlinked"); // Iterate arguments of a function/task. for (AstNode *argp = nodep->pinsp(), *paramp = ftaskp->stmtsp(); argp; @@ -495,12 +512,9 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { m_foundTargetVar.clear(); iterate(argp); if (reason) { - for (VarSet::iterator it = m_foundTargetVar.begin(), - it_end = m_foundTargetVar.end(); - it != it_end; ++it) { - argp->v3warn(SPLITVAR, (*it)->prettyNameQ() - << notSplitMsg << reason << ".\n"); - m_refs.remove(*it); + for (AstVar* const varp : m_foundTargetVar) { + warnNoSplit(varp, argp, reason); + m_refs.remove(varp); } } m_foundTargetVar.clear(); @@ -509,15 +523,14 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { } virtual void visit(AstPin* nodep) override { UINFO(5, nodep->modVarp()->prettyNameQ() << " pin \n"); - AstNode* exprp = nodep->exprp(); + AstNode* const exprp = nodep->exprp(); if (!exprp) return; // Not connected pin m_foundTargetVar.clear(); iterate(exprp); if (const char* reason = cannotSplitConnectedPortReason(nodep)) { - for (VarSet::iterator it = m_foundTargetVar.begin(), it_end = m_foundTargetVar.end(); - it != it_end; ++it) { - nodep->v3warn(SPLITVAR, (*it)->prettyNameQ() << notSplitMsg << reason << ".\n"); - m_refs.remove(*it); + for (AstVar* const varp : m_foundTargetVar) { + warnNoSplit(varp, nodep, reason); + m_refs.remove(varp); } m_foundTargetVar.clear(); } @@ -550,22 +563,18 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { iterateChildren(nodep); } virtual void visit(AstArraySel* nodep) override { - if (AstVarRef* refp = isTargetVref(nodep->fromp())) { - AstConst* indexp = VN_CAST(nodep->bitp(), Const); + if (AstVarRef* const refp = isTargetVref(nodep->fromp())) { + AstConst* const indexp = VN_CAST(nodep->bitp(), Const); if (indexp) { // OK UINFO(4, "add " << nodep << " for " << refp->varp()->prettyName() << "\n"); if (indexp->toSInt() < outerMostSizeOfUnpackedArray(refp->varp())) { m_refs.tryAdd(m_contextp, refp, nodep, indexp->toSInt(), m_inFTask); } else { - nodep->bitp()->v3warn(SPLITVAR, refp->prettyNameQ() - << notSplitMsg - << "index is out of range.\n"); + warnNoSplit(refp->varp(), nodep->bitp(), "index is out of range"); m_refs.remove(refp->varp()); } } else { - nodep->bitp()->v3warn(SPLITVAR, refp->prettyNameQ() - << notSplitMsg - << "index cannot be determined statically.\n"); + warnNoSplit(refp->varp(), nodep->bitp(), "index cannot be determined statically"); m_refs.remove(refp->varp()); iterate(nodep->bitp()); } @@ -574,8 +583,8 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { } } virtual void visit(AstSliceSel* nodep) override { - if (AstVarRef* refp = isTargetVref(nodep->fromp())) { - AstUnpackArrayDType* dtypep + if (AstVarRef* const refp = isTargetVref(nodep->fromp())) { + const AstUnpackArrayDType* const dtypep = VN_CAST(refp->varp()->dtypep()->skipRefp(), UnpackArrayDType); // declRange() of AstSliceSel is shifted by dtypep->declRange().lo() in V3WidthSel.cpp // restore the original decl range here. @@ -591,8 +600,8 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { iterateChildren(nodep); } } - static AstNode* toInsertPoint(AstNode* insertp) { - if (AstNodeStmt* stmtp = VN_CAST(insertp, NodeStmt)) { + AstNode* toInsertPoint(AstNode* insertp) { + if (AstNodeStmt* const stmtp = VN_CAST(insertp, NodeStmt)) { if (!stmtp->isStatement()) insertp = stmtp->backp(); } return insertp; @@ -600,15 +609,15 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { AstVarRef* createTempVar(AstNode* context, AstNode* nodep, AstUnpackArrayDType* dtypep, const std::string& name_prefix, std::vector& vars, int start_idx, bool lvalue, bool ftask) { - const std::string name - = "__VsplitVar" + cvtToStr(m_modp->varNumGetInc()) + "__" + name_prefix; - AstNodeAssign* assignp = VN_CAST(context, NodeAssign); + FileLine* const fl = nodep->fileline(); + const std::string name = m_tempNames.get(nodep) + "__" + name_prefix; + AstNodeAssign* const assignp = VN_CAST(context, NodeAssign); if (assignp) { // "always_comb a = b;" to "always_comb begin a = b; end" so that local // variable can be added. insertBeginIfNecessary(assignp, m_modp); } - AstVar* varp = newVar(nodep->fileline(), AstVarType::VAR, name, dtypep); + AstVar* const varp = newVar(fl, AstVarType::VAR, name, dtypep); // Variable will be registered in the caller side. UINFO(3, varp->prettyNameQ() << " is created lsb:" << dtypep->lo() << " msb:" << dtypep->hi() << "\n"); @@ -617,18 +626,17 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { = (context && VN_IS(context, NodeFTaskRef)) || (assignp && VN_IS(assignp, Assign)); for (int i = 0; i < dtypep->elementsConst(); ++i) { - AstNode* lhsp = newVarRef(nodep->fileline(), vars.at(start_idx + i), - lvalue ? VAccess::WRITE : VAccess::READ); - AstNode* rhsp = new AstArraySel( - nodep->fileline(), - newVarRef(nodep->fileline(), varp, !lvalue ? VAccess::WRITE : VAccess::READ), i); + AstNode* lhsp + = newVarRef(fl, vars.at(start_idx + i), lvalue ? VAccess::WRITE : VAccess::READ); + AstNode* rhsp = new AstArraySel{ + fl, newVarRef(fl, varp, !lvalue ? VAccess::WRITE : VAccess::READ), i}; AstNode* refp = lhsp; UINFO(9, "Creating assign idx:" << i << " + " << start_idx << "\n"); if (!lvalue) std::swap(lhsp, rhsp); AstNode* newassignp; if (use_simple_assign) { - AstNode* insertp = toInsertPoint(context); - newassignp = new AstAssign(nodep->fileline(), lhsp, rhsp); + AstNode* const insertp = toInsertPoint(context); + newassignp = new AstAssign{fl, lhsp, rhsp}; if (lvalue) { // If varp is LHS, this assignment must appear after the original // assignment(context). @@ -638,28 +646,28 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { insertp->addHereThisAsNext(newassignp); } } else { - newassignp = new AstAssignW(nodep->fileline(), lhsp, rhsp); + newassignp = new AstAssignW{fl, lhsp, rhsp}; // Continuous assignment must be in module context. varp->addNextHere(newassignp); } UASSERT_OBJ(!m_contextp, m_contextp, "must be null"); setContextAndIterate(newassignp, refp); } - return newVarRef(nodep->fileline(), varp, lvalue ? VAccess::WRITE : VAccess::READ); + return newVarRef(fl, varp, lvalue ? VAccess::WRITE : VAccess::READ); } void connectPort(AstVar* varp, std::vector& vars, AstNode* insertp) { UASSERT_OBJ(varp->isIO(), varp, "must be port"); insertp = insertp ? toInsertPoint(insertp) : nullptr; const bool lvalue = varp->direction().isWritable(); + FileLine* const fl = varp->fileline(); for (size_t i = 0; i < vars.size(); ++i) { - AstNode* nodes[] = { - new AstArraySel( - varp->fileline(), - newVarRef(varp->fileline(), varp, lvalue ? VAccess::WRITE : VAccess::READ), i), - newVarRef(varp->fileline(), vars.at(i), !lvalue ? VAccess::WRITE : VAccess::READ)}; - AstNode* lhsp = nodes[lvalue ? 0 : 1]; - AstNode* rhsp = nodes[lvalue ? 1 : 0]; - AstNodeAssign* assignp = newAssign(varp->fileline(), lhsp, rhsp, varp); + AstNode* const nodes[] = { + new AstArraySel{fl, newVarRef(fl, varp, lvalue ? VAccess::WRITE : VAccess::READ), + static_cast(i)}, + newVarRef(fl, vars.at(i), !lvalue ? VAccess::WRITE : VAccess::READ)}; + AstNode* const lhsp = nodes[lvalue ? 0 : 1]; + AstNode* const rhsp = nodes[lvalue ? 1 : 0]; + AstNodeAssign* const assignp = newAssign(fl, lhsp, rhsp, varp); if (insertp) { if (lvalue) { // Just after writing to the temporary variable insertp->addNextHere(assignp); @@ -675,14 +683,15 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { } size_t collapse(UnpackRefMap& refs) { size_t numSplit = 0; - for (UnpackRefMap::MapIt it = refs.begin(), it_end = refs.end(); it != it_end; ++it) { - UINFO(3, "In module " << m_modp->name() << " var " << it->first->prettyNameQ() - << " which has " << it->second.size() + for (const auto& pair : refs) { + UINFO(3, "In module " << m_modp->name() << " var " << pair.first->prettyNameQ() + << " which has " << pair.second.size() << " refs will be split.\n"); - AstVar* varp = it->first; + AstVar* const varp = pair.first; AstNode* insertp = varp; - AstUnpackArrayDType* dtypep = VN_CAST(varp->dtypep()->skipRefp(), UnpackArrayDType); - AstNodeDType* subTypep = dtypep->subDTypep(); + const AstUnpackArrayDType* const dtypep + = VN_CAST(varp->dtypep()->skipRefp(), UnpackArrayDType); + AstNodeDType* const subTypep = dtypep->subDTypep(); const bool needNext = VN_IS(subTypep, UnpackArrayDType); // Still unpacked array. std::vector vars; // Add the split variables @@ -701,39 +710,38 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { vars.push_back(newp); setContextAndIterate(nullptr, newp); } - for (UnpackRefMap::SetIt sit = it->second.begin(), sit_end = it->second.end(); - sit != sit_end; ++sit) { + for (const UnpackRef& ref : pair.second) { AstNode* newp = nullptr; - if (sit->isSingleRef()) { - newp = newVarRef(sit->nodep()->fileline(), vars.at(sit->index()), - sit->access()); + if (ref.isSingleRef()) { + newp = newVarRef(ref.nodep()->fileline(), vars.at(ref.index()), ref.access()); } else { - AstVarRef* refp = VN_CAST(sit->nodep(), VarRef); + AstVarRef* refp = VN_CAST(ref.nodep(), VarRef); AstUnpackArrayDType* adtypep; int lsb = 0; if (refp) { adtypep = VN_CAST(refp->dtypep()->skipRefp(), UnpackArrayDType); } else { - AstSliceSel* selp = VN_CAST(sit->nodep(), SliceSel); - UASSERT_OBJ(selp, sit->nodep(), "Unexpected op is registered"); + AstSliceSel* selp = VN_CAST(ref.nodep(), SliceSel); + UASSERT_OBJ(selp, ref.nodep(), "Unexpected op is registered"); refp = VN_CAST(selp->fromp(), VarRef); UASSERT_OBJ(refp, selp, "Unexpected op is registered"); adtypep = VN_CAST(selp->dtypep()->skipRefp(), UnpackArrayDType); lsb = adtypep->lo(); } - AstVarRef* newrefp = createTempVar(sit->context(), refp, adtypep, varp->name(), - vars, lsb, refp->access(), sit->ftask()); + AstVarRef* const newrefp + = createTempVar(ref.context(), refp, adtypep, varp->name(), vars, lsb, + refp->access(), ref.ftask()); newp = newrefp; refp->varp()->addNextHere(newrefp->varp()); UINFO(3, "Create " << newrefp->varp()->prettyNameQ() << " for " << refp << "\n"); } - sit->nodep()->replaceWith(newp); - pushDeletep(sit->nodep()); - setContextAndIterate(sit->context(), newp->backp()); + ref.nodep()->replaceWith(newp); + pushDeletep(ref.nodep()); + setContextAndIterate(ref.context(), newp->backp()); // AstAssign is used. So assignment is necessary for each reference. if (varp->isIO() && (varp->isFuncLocal() || varp->isFuncReturn())) - connectPort(varp, vars, sit->context()); + connectPort(varp, vars, ref.context()); } if (varp->isIO()) { // AssignW will be created, so just once @@ -763,7 +771,8 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { public: explicit SplitUnpackedVarVisitor(AstNetlist* nodep) - : m_refs{} { + : m_refs{} + , m_tempNames{"__VsplitVar"} { iterate(nodep); } ~SplitUnpackedVarVisitor() override { @@ -850,7 +859,9 @@ public: // If this is AstVarRef and referred in the sensitivity list of always@, // return the sensitivity item AstSenItem* backSenItemp() const { - if (AstVarRef* refp = VN_CAST(m_nodep, VarRef)) return VN_CAST(refp->backp(), SenItem); + if (AstVarRef* const refp = VN_CAST(m_nodep, VarRef)) { + return VN_CAST(refp->backp(), SenItem); + } return nullptr; } }; @@ -872,9 +883,7 @@ class PackedVarRef final { for (size_t i = 0; i < refs.size(); ++i) { nodes.emplace(refs[i].nodep(), i); } std::vector vect; vect.reserve(nodes.size()); - for (auto it = nodes.cbegin(), it_end = nodes.cend(); it != it_end; ++it) { - vect.push_back(refs[it->second]); - } + for (const auto& pair : nodes) vect.push_back(refs[pair.second]); refs.swap(vect); } @@ -910,16 +919,16 @@ public: std::vector plan; std::vector> points; // points.reserve(m_lhs.size() * 2 + 2); // 2 points will be added per one PackedVarRefEntry - for (const_iterator it = m_lhs.begin(), itend = m_lhs.end(); it != itend; ++it) { - points.emplace_back(std::make_pair(it->lsb(), false)); // Start of a region - points.emplace_back(std::make_pair(it->msb() + 1, true)); // End of a region + for (const PackedVarRefEntry& ref : m_lhs) { + points.emplace_back(std::make_pair(ref.lsb(), false)); // Start of a region + points.emplace_back(std::make_pair(ref.msb() + 1, true)); // End of a region } if (skipUnused && !m_rhs.empty()) { // Range to be read must be kept, so add points here int lsb = m_basicp->hi() + 1; int msb = m_basicp->lo() - 1; - for (size_t i = 0; i < m_rhs.size(); ++i) { - lsb = std::min(lsb, m_rhs[i].lsb()); - msb = std::max(msb, m_rhs[i].msb()); + for (const PackedVarRefEntry& ref : m_rhs) { + lsb = std::min(lsb, ref.lsb()); + msb = std::max(msb, ref.msb()); } UASSERT_OBJ(lsb <= msb, m_basicp, "lsb:" << lsb << " msb:" << msb << " are wrong"); points.emplace_back(std::make_pair(lsb, false)); @@ -960,8 +969,8 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { } virtual void visit(AstVar* nodep) override { if (!nodep->attrSplitVar()) return; // Nothing to do - if (const char* reason = cannotSplitReason(nodep, true)) { - nodep->v3warn(SPLITVAR, nodep->prettyNameQ() << notSplitMsg << reason); + if (const char* const reason = cannotSplitReason(nodep, true)) { + warnNoSplit(nodep, nodep, reason); nodep->attrSplitVar(false); } else { // Finally find a good candidate const bool inserted = m_refs.insert(std::make_pair(nodep, PackedVarRef(nodep))).second; @@ -969,7 +978,7 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { } } virtual void visit(AstVarRef* nodep) override { - AstVar* varp = nodep->varp(); + AstVar* const varp = nodep->varp(); visit(varp); const auto refit = m_refs.find(varp); if (refit == m_refs.end()) return; // variable without split_var metacomment @@ -983,13 +992,13 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { << " Entire bit of [" << basicp->lo() << "+:" << varp->width() << "] \n"); } virtual void visit(AstSel* nodep) override { - AstVarRef* vrefp = VN_CAST(nodep->fromp(), VarRef); + AstVarRef* const vrefp = VN_CAST(nodep->fromp(), VarRef); if (!vrefp) { iterateChildren(nodep); return; } - AstVar* varp = vrefp->varp(); + AstVar* const varp = vrefp->varp(); const auto refit = m_refs.find(varp); if (refit == m_refs.end()) { iterateChildren(nodep); @@ -1009,9 +1018,7 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { << " [" << consts[0]->toSInt() << ":+" << consts[1]->toSInt() << "] lsb:" << refit->second.basicp()->lo() << "\n"); } else { - nodep->v3warn(SPLITVAR, vrefp->prettyNameQ() - << notSplitMsg - << "its bit range cannot be determined statically."); + warnNoSplit(vrefp->varp(), nodep, "its bit range cannot be determined statically"); if (!consts[0]) { UINFO(4, "LSB " << nodep->lsbp() << " is expected to be constant, but not\n"); } @@ -1028,7 +1035,8 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { // Extract necessary bit range from a newly created variable to meet ref static AstNode* extractBits(const PackedVarRefEntry& ref, const SplitNewVar& var, const VAccess access) { - AstVarRef* refp = new AstVarRef(ref.nodep()->fileline(), var.varp(), access); + FileLine* const fl = ref.nodep()->fileline(); + AstVarRef* const refp = new AstVarRef{fl, var.varp(), access}; if (ref.lsb() <= var.lsb() && var.msb() <= ref.msb()) { // Use the entire bits return refp; } else { // Use slice @@ -1038,27 +1046,25 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { UINFO(4, var.varp()->prettyNameQ() << "[" << msb << ":" << lsb << "] used for " << ref.nodep()->prettyNameQ() << '\n'); // LSB of varp is always 0. "lsb - var.lsb()" means this. see also SplitNewVar - AstSel* selp = new AstSel(ref.nodep()->fileline(), refp, lsb - var.lsb(), bitwidth); - return selp; + return new AstSel{fl, refp, lsb - var.lsb(), bitwidth}; } } static void connectPortAndVar(const std::vector& vars, AstVar* portp, AstNode* insertp) { for (; insertp; insertp = insertp->backp()) { - if (AstNodeStmt* stmtp = VN_CAST(insertp, NodeStmt)) { + if (const AstNodeStmt* const stmtp = VN_CAST(insertp, NodeStmt)) { if (stmtp->isStatement()) break; } } const bool in = portp->isReadOnly(); - for (size_t i = 0; i < vars.size(); ++i) { - AstNode* rhsp = new AstSel( - portp->fileline(), - new AstVarRef(portp->fileline(), portp, !in ? VAccess::WRITE : VAccess::READ), - vars[i].lsb(), vars[i].bitwidth()); - AstNode* lhsp = new AstVarRef(portp->fileline(), vars[i].varp(), - in ? VAccess::WRITE : VAccess::READ); + FileLine* const fl = portp->fileline(); + for (const SplitNewVar& var : vars) { + AstNode* rhsp + = new AstSel{fl, new AstVarRef{fl, portp, !in ? VAccess::WRITE : VAccess::READ}, + var.lsb(), var.bitwidth()}; + AstNode* lhsp = new AstVarRef{fl, var.varp(), in ? VAccess::WRITE : VAccess::READ}; if (!in) std::swap(lhsp, rhsp); - AstNodeAssign* assignp = newAssign(portp->fileline(), lhsp, rhsp, portp); + AstNodeAssign* const assignp = newAssign(fl, lhsp, rhsp, portp); if (insertp) { if (in) { insertp->addHereThisAsNext(assignp); @@ -1066,15 +1072,14 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { insertp->addNextHere(assignp); } } else { - vars[i].varp()->addNextHere(assignp); + var.varp()->addNextHere(assignp); } } } void createVars(AstVar* varp, const AstBasicDType* basicp, std::vector& vars) { - for (size_t i = 0; i < vars.size(); ++i) { - SplitNewVar* newvarp = &vars[i]; - int left = newvarp->msb(); - int right = newvarp->lsb(); + for (SplitNewVar& newvar : vars) { + int left = newvar.msb(); + int right = newvar.lsb(); if (basicp->littleEndian()) std::swap(left, right); const std::string name = (left == right) @@ -1085,93 +1090,93 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { AstBasicDType* dtypep; switch (basicp->keyword()) { case AstBasicDTypeKwd::BIT: - dtypep = new AstBasicDType(varp->subDTypep()->fileline(), VFlagBitPacked(), - newvarp->bitwidth()); + dtypep = new AstBasicDType{varp->subDTypep()->fileline(), VFlagBitPacked(), + newvar.bitwidth()}; break; case AstBasicDTypeKwd::LOGIC: - dtypep = new AstBasicDType(varp->subDTypep()->fileline(), VFlagLogicPacked(), - newvarp->bitwidth()); + dtypep = new AstBasicDType{varp->subDTypep()->fileline(), VFlagLogicPacked(), + newvar.bitwidth()}; break; default: UASSERT_OBJ(false, basicp, "Only bit and logic are allowed"); } - dtypep->rangep(new AstRange{varp->fileline(), VNumRange{newvarp->msb(), newvarp->lsb(), - basicp->littleEndian()}}); - newvarp->varp(new AstVar(varp->fileline(), AstVarType::VAR, name, dtypep)); - newvarp->varp()->propagateAttrFrom(varp); - newvarp->varp()->funcLocal(varp->isFuncLocal() || varp->isFuncReturn()); + dtypep->rangep(new AstRange{ + varp->fileline(), VNumRange{newvar.msb(), newvar.lsb(), basicp->littleEndian()}}); + newvar.varp(new AstVar{varp->fileline(), AstVarType::VAR, name, dtypep}); + newvar.varp()->propagateAttrFrom(varp); + newvar.varp()->funcLocal(varp->isFuncLocal() || varp->isFuncReturn()); // Enable this line to trace split variable directly: - // newvarp->varp()->trace(varp->isTrace()); + // newvar.varp()->trace(varp->isTrace()); m_netp->typeTablep()->addTypesp(dtypep); - varp->addNextHere(newvarp->varp()); - UINFO(4, newvarp->varp()->prettyNameQ() + varp->addNextHere(newvar.varp()); + UINFO(4, newvar.varp()->prettyNameQ() << " is added for " << varp->prettyNameQ() << '\n'); } } static void updateReferences(AstVar* varp, PackedVarRef& ref, const std::vector& vars) { - for (int lvalue = 0; lvalue <= 1; ++lvalue) { // Refer the new split variables + for (const bool lvalue : {false, true}) { // Refer the new split variables std::vector& refs = lvalue ? ref.lhs() : ref.rhs(); - for (PackedVarRef::iterator refit = refs.begin(), refitend = refs.end(); - refit != refitend; ++refit) { - auto varit = std::upper_bound(vars.begin(), vars.end(), refit->lsb(), - SplitNewVar::Match()); - UASSERT_OBJ(varit != vars.end(), refit->nodep(), "Not found"); - UASSERT(!(varit->msb() < refit->lsb() || refit->msb() < varit->lsb()), + for (PackedVarRefEntry& ref : refs) { + auto varit + = std::upper_bound(vars.begin(), vars.end(), ref.lsb(), SplitNewVar::Match()); + UASSERT_OBJ(varit != vars.end(), ref.nodep(), "Not found"); + UASSERT(!(varit->msb() < ref.lsb() || ref.msb() < varit->lsb()), "wrong search result"); AstNode* prevp; bool inSentitivityList = false; - if (AstSenItem* senitemp = refit->backSenItemp()) { - AstNode* oldsenrefp = senitemp->sensp(); + if (AstSenItem* const senitemp = ref.backSenItemp()) { + AstNode* const oldsenrefp = senitemp->sensp(); oldsenrefp->replaceWith( - new AstVarRef(senitemp->fileline(), varit->varp(), VAccess::READ)); + new AstVarRef{senitemp->fileline(), varit->varp(), VAccess::READ}); VL_DO_DANGLING(oldsenrefp->deleteTree(), oldsenrefp); prevp = senitemp; inSentitivityList = true; } else { - prevp = extractBits(*refit, *varit, lvalue ? VAccess::WRITE : VAccess::READ); + prevp = extractBits(ref, *varit, lvalue ? VAccess::WRITE : VAccess::READ); } - for (int residue = refit->msb() - varit->msb(); residue > 0; + for (int residue = ref.msb() - varit->msb(); residue > 0; residue -= varit->bitwidth()) { ++varit; - UASSERT_OBJ(varit != vars.end(), refit->nodep(), "not enough split variables"); - if (AstSenItem* senitemp = VN_CAST(prevp, SenItem)) { - prevp = new AstSenItem( + UASSERT_OBJ(varit != vars.end(), ref.nodep(), "not enough split variables"); + if (AstSenItem* const senitemp = VN_CAST(prevp, SenItem)) { + prevp = new AstSenItem{ senitemp->fileline(), senitemp->edgeType(), - new AstVarRef(senitemp->fileline(), varit->varp(), VAccess::READ)); + new AstVarRef{senitemp->fileline(), varit->varp(), VAccess::READ}}; senitemp->addNextHere(prevp); } else { - AstNode* bitsp - = extractBits(*refit, *varit, lvalue ? VAccess::WRITE : VAccess::READ); - prevp = new AstConcat(refit->nodep()->fileline(), bitsp, prevp); + AstNode* const bitsp + = extractBits(ref, *varit, lvalue ? VAccess::WRITE : VAccess::READ); + prevp = new AstConcat{ref.nodep()->fileline(), bitsp, prevp}; } } // If varp is an argument of task/func, need to update temporary var // everytime the var is updated. See also another call of connectPortAndVar() in // split() if (varp->isIO() && (varp->isFuncLocal() || varp->isFuncReturn())) - connectPortAndVar(vars, varp, refit->nodep()); - if (!inSentitivityList) refit->replaceNodeWith(prevp); - UASSERT_OBJ(varit->msb() >= refit->msb(), varit->varp(), "Out of range"); + connectPortAndVar(vars, varp, ref.nodep()); + if (!inSentitivityList) ref.replaceNodeWith(prevp); + UASSERT_OBJ(varit->msb() >= ref.msb(), varit->varp(), "Out of range"); } } } // Do the actual splitting operation void split() { - for (auto it = m_refs.begin(), it_end = m_refs.end(); it != it_end; ++it) { - it->second.dedup(); - AstVar* varp = it->first; + for (auto& pair : m_refs) { + AstVar* const varp = pair.first; + PackedVarRef& ref = pair.second; + ref.dedup(); UINFO(3, "In module " << m_modp->name() << " var " << varp->prettyNameQ() - << " which has " << it->second.lhs().size() << " lhs refs and " - << it->second.rhs().size() << " rhs refs will be split.\n"); + << " which has " << ref.lhs().size() << " lhs refs and " + << ref.rhs().size() << " rhs refs will be split.\n"); std::vector vars - = it->second.splitPlan(!varp->isTrace()); // If traced, all bit must be kept + = ref.splitPlan(!varp->isTrace()); // If traced, all bit must be kept if (vars.empty()) continue; if (vars.size() == 1 && vars.front().bitwidth() == varp->width()) continue; // No split - createVars(varp, it->second.basicp(), vars); // Add the split variables + createVars(varp, ref.basicp(), vars); // Add the split variables - updateReferences(varp, it->second, vars); + updateReferences(varp, ref, vars); if (varp->isIO()) { // port cannot be deleted // If varp is a port of a module, single AssignW is sufficient @@ -1179,16 +1184,15 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { connectPortAndVar(vars, varp, nullptr); } else if (varp->isTrace()) { // Let's reuse the original variable for tracing - AstNode* rhsp = new AstVarRef(vars.front().varp()->fileline(), vars.front().varp(), - VAccess::READ); + AstNode* rhsp = new AstVarRef{vars.front().varp()->fileline(), vars.front().varp(), + VAccess::READ}; + FileLine* const fl = varp->fileline(); for (size_t i = 1; i < vars.size(); ++i) { - rhsp = new AstConcat( - varp->fileline(), - new AstVarRef(varp->fileline(), vars[i].varp(), VAccess::READ), rhsp); + rhsp = new AstConcat{fl, new AstVarRef{fl, vars[i].varp(), VAccess::READ}, + rhsp}; } - varp->addNextHere(newAssign(varp->fileline(), - new AstVarRef(varp->fileline(), varp, VAccess::WRITE), - rhsp, varp)); + varp->addNextHere( + newAssign(fl, new AstVarRef{fl, varp, VAccess::WRITE}, rhsp, varp)); } else { // the original variable is not used anymore. VL_DO_DANGLING(varp->unlinkFrBack()->deleteTree(), varp); } @@ -1231,9 +1235,10 @@ public: } else { reason = "its type is unknown"; // LCOV_EXCL_LINE } - if (reason) + if (reason) { UINFO(5, "Check " << nodep->prettyNameQ() << " cannot split because" << reason << endl); + } return reason; } VL_DEBUG_FUNC; // Declare debug() @@ -1250,11 +1255,11 @@ void V3SplitVar::splitVariable(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); SplitVarRefsMap refs; { - SplitUnpackedVarVisitor visitor(nodep); + SplitUnpackedVarVisitor visitor{nodep}; refs = visitor.getPackedVarRefs(); } V3Global::dumpCheckGlobalTree("split_var", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 9); - { SplitPackedVarVisitor visitor(nodep, refs); } + { SplitPackedVarVisitor visitor{nodep, refs}; } V3Global::dumpCheckGlobalTree("split_var", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 9); } diff --git a/src/V3Stats.cpp b/src/V3Stats.cpp index ba11b2f4f..3084b233b 100644 --- a/src/V3Stats.cpp +++ b/src/V3Stats.cpp @@ -279,7 +279,7 @@ public: // Top Stats class void V3Stats::statsStageAll(AstNetlist* nodep, const string& stage, bool fast) { - StatsVisitor visitor(nodep, stage, fast); + StatsVisitor visitor{nodep, stage, fast}; } void V3Stats::statsFinalAll(AstNetlist* nodep) { diff --git a/src/V3Stats.h b/src/V3Stats.h index 1b6c785c7..b340cf0a9 100644 --- a/src/V3Stats.h +++ b/src/V3Stats.h @@ -24,12 +24,6 @@ class AstNetlist; -#define STAT_ADD_UINFO(level, text, value) \ - do { \ - UINFO((level), "addStat " << text << " " << value << endl); \ - V3Stats::addStat(text, value); \ - } while (0) - //============================================================================ class VDouble0 final { diff --git a/src/V3StatsReport.cpp b/src/V3StatsReport.cpp index 24c148d3d..5833f8db6 100644 --- a/src/V3StatsReport.cpp +++ b/src/V3StatsReport.cpp @@ -222,7 +222,7 @@ void V3Stats::statsReport() { // Open stats file const string filename = v3Global.opt.hierTopDataDir() + "/" + v3Global.opt.prefix() + "__stats.txt"; - std::ofstream* ofp(V3File::new_ofstream(filename)); + std::ofstream* ofp{V3File::new_ofstream(filename)}; if (ofp->fail()) v3fatal("Can't write " << filename); StatsReport reporter(ofp); diff --git a/src/V3String.cpp b/src/V3String.cpp index c4f054575..caf5a8283 100644 --- a/src/V3String.cpp +++ b/src/V3String.cpp @@ -174,6 +174,10 @@ string VString::replaceWord(const string& str, const string& from, const string& return result; } +bool VString::startsWith(const string& str, const string& prefix) { + return str.rfind(prefix, 0) == 0; // Faster than .find(_) == 0 +} + //###################################################################### // VHashSha256 diff --git a/src/V3String.h b/src/V3String.h index f3ed2f651..e028561f3 100644 --- a/src/V3String.h +++ b/src/V3String.h @@ -113,6 +113,8 @@ public: // to be a consecutive sequence of the characters [a-zA-Z0-9_]. Sub-words are not replaced. // e.g.: replaceWords("one apple bad_apple", "apple", "banana") -> "one banana bad_apple" static string replaceWord(const string& str, const string& from, const string& to); + // Predicate to check if 'str' starts with 'prefix' + static bool startsWith(const string& str, const string& prefix); }; //###################################################################### diff --git a/src/V3Subst.cpp b/src/V3Subst.cpp index a3c51797e..a9d422344 100644 --- a/src/V3Subst.cpp +++ b/src/V3Subst.cpp @@ -314,7 +314,7 @@ private: SubstVarEntry* entryp = getEntryp(varrefp); if (AstNode* substp = entryp->substWord(nodep, word)) { // Check that the RHS hasn't changed value since we recorded it. - SubstUseVisitor visitor(substp, entryp->getWordStep(word)); + SubstUseVisitor visitor{substp, entryp->getWordStep(word)}; if (visitor.ok()) { VL_DO_DANGLING(replaceSubstEtc(nodep, substp), nodep); } else { @@ -341,7 +341,7 @@ private: entryp->assignComplex(); } else if (AstNode* substp = entryp->substWhole(nodep)) { // Check that the RHS hasn't changed value since we recorded it. - SubstUseVisitor visitor(substp, entryp->getWholeStep()); + SubstUseVisitor visitor{substp, entryp->getWholeStep()}; if (visitor.ok()) { UINFO(8, " USEwhole " << nodep << endl); VL_DO_DANGLING(replaceSubstEtc(nodep, substp), nodep); @@ -380,6 +380,6 @@ public: void V3Subst::substituteAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { SubstVisitor visitor(nodep); } // Destruct before checking + { SubstVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("subst", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3SymTable.h b/src/V3SymTable.h index 6db11ae30..e7ff1905f 100644 --- a/src/V3SymTable.h +++ b/src/V3SymTable.h @@ -317,7 +317,7 @@ public: if (v3Global.opt.dumpTree()) { const string filename = v3Global.debugFilename(nameComment) + ".txt"; UINFO(2, "Dumping " << filename << endl); - const std::unique_ptr logp(V3File::new_ofstream(filename)); + const std::unique_ptr logp{V3File::new_ofstream(filename)}; if (logp->fail()) v3fatal("Can't write " << filename); dump(*logp, ""); } diff --git a/src/V3TSP.cpp b/src/V3TSP.cpp index 6e3cc079d..4ea0ae3a4 100644 --- a/src/V3TSP.cpp +++ b/src/V3TSP.cpp @@ -355,7 +355,7 @@ public: void dumpGraphFilePrefixed(const string& nameComment) const { if (v3Global.opt.dumpTree()) { const string filename = v3Global.debugFilename(nameComment) + ".txt"; - const std::unique_ptr logp(V3File::new_ofstream(filename)); + const std::unique_ptr logp{V3File::new_ofstream(filename)}; if (logp->fail()) v3fatal("Can't write " << filename); dumpGraph(*logp, nameComment); } @@ -499,7 +499,7 @@ public: : m_xpos{xpos} , m_ypos{ypos} , m_serial{++s_serialNext} {} - ~TspTestState() = default; + virtual ~TspTestState() override = default; virtual int cost(const TspStateBase* otherp) const override { return cost(dynamic_cast(otherp)); } diff --git a/src/V3Table.cpp b/src/V3Table.cpp index 330873ced..f81c83021 100644 --- a/src/V3Table.cpp +++ b/src/V3Table.cpp @@ -97,9 +97,9 @@ public: v3Global.rootp()->typeTablep()->addTypesp(tableDTypep); // Create table initializer (with default value 0) AstConst* const defaultp = elemDType->isString() - ? new AstConst(m_fl, AstConst::String(), "") - : new AstConst(m_fl, AstConst::WidthedValue(), width, 0); - m_initp = new AstInitArray(m_fl, tableDTypep, defaultp); + ? new AstConst{m_fl, AstConst::String(), ""} + : new AstConst{m_fl, AstConst::WidthedValue(), width, 0}; + m_initp = new AstInitArray{m_fl, tableDTypep, defaultp}; } void addValue(unsigned index, const V3Number& value) { @@ -196,7 +196,7 @@ private: m_outVarps.clear(); // Collect stats - TableSimulateVisitor chkvis(this); + TableSimulateVisitor chkvis{this}; chkvis.mainTableCheck(nodep); m_assignDly = chkvis.isAssignDly(); // Also sets m_inWidthBits @@ -280,7 +280,7 @@ private: // There may be a simulation path by which the output doesn't change value. // We could bail on these cases, or we can have a "change it" boolean. // We've chosen the latter route, since recirc is common in large FSMs. - TableSimulateVisitor simvis(this); + TableSimulateVisitor simvis{this}; for (uint32_t i = 0; i <= VL_MASK_I(m_inWidthBits); ++i) { const uint32_t inValue = i; // Make a new simulation structure so we can set new input values @@ -354,11 +354,11 @@ private: AstVarScope* outputAssignedTableVscp) { FileLine* const fl = nodep->fileline(); for (TableOutputVar& tov : m_outVarps) { - AstNode* const alhsp = new AstVarRef(fl, tov.varScopep(), VAccess::WRITE); + AstNode* const alhsp = new AstVarRef{fl, tov.varScopep(), VAccess::WRITE}; AstNode* const arhsp = select(fl, tov.tabeVarScopep(), indexVscp); AstNode* outsetp = m_assignDly - ? static_cast(new AstAssignDly(fl, alhsp, arhsp)) - : static_cast(new AstAssign(fl, alhsp, arhsp)); + ? static_cast(new AstAssignDly{fl, alhsp, arhsp}) + : static_cast(new AstAssign{fl, alhsp, arhsp}); // If this output is unassigned on some code paths, wrap the assignment in an If if (tov.mayBeUnassigned()) { @@ -425,6 +425,6 @@ void TableSimulateVisitor::varRefCb(AstVarRef* nodep) { void V3Table::tableAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { TableVisitor visitor(nodep); } // Destruct before checking + { TableVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("table", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 13ab8a148..a4bcff21d 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -364,6 +364,39 @@ struct TaskDpiUtils { } }; +//###################################################################### +// Gather non-local variables written by an AstCFunc + +class TaskGatherWrittenVisitor final : public AstNVisitor { + // NODE STATE + // AstVarScope::user5 -> Already considered variable + AstUser5InUse m_user5InUse; + + std::vector m_writtenVariables; // Variables written + + virtual void visit(AstVarRef* nodep) override { + if (nodep->access().isReadOnly()) return; // Ignore read reference + AstVarScope* const varScopep = nodep->varScopep(); + if (varScopep->user5()) return; // Ignore already processed variable + varScopep->user5(true); // Mark as already processed + // Note: We are ignoring function locals as they should not be referenced anywhere outside + // of the enclosing AstCFunc, and therefore they are irrelevant for code ordering. This is + // simply an optimization to avoid adding useless nodes to the ordering graph in V3Order. + if (varScopep->varp()->isFuncLocal()) return; + m_writtenVariables.push_back(varScopep); + } + virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } + + explicit TaskGatherWrittenVisitor(AstNode* nodep) { iterate(nodep); } + +public: + // Gather all written non-local variables + static const std::vector gather(AstCFunc* funcp) { + TaskGatherWrittenVisitor visitor{funcp}; + return std::move(visitor.m_writtenVariables); + } +}; + //###################################################################### // Task state, as a visitor of each AstNode @@ -419,17 +452,22 @@ private: return newvscp; } AstVarScope* createVarScope(AstVar* invarp, const string& name) { - // We could create under either the ref's scope or the ftask's scope. - // It shouldn't matter, as they are only local variables. - // We choose to do it under whichever called this function, which results - // in more cache locality. - AstVar* newvarp = new AstVar(invarp->fileline(), AstVarType::BLOCKTEMP, name, invarp); - newvarp->funcLocal(false); - newvarp->propagateAttrFrom(invarp); - m_modp->addStmtp(newvarp); - AstVarScope* newvscp = new AstVarScope(newvarp->fileline(), m_scopep, newvarp); - m_scopep->addVarp(newvscp); - return newvscp; + if (invarp->isParam() && VN_IS(invarp->valuep(), InitArray)) { + // Move array params in functions into constant pool + return v3Global.rootp()->constPoolp()->findTable(VN_CAST(invarp->valuep(), InitArray)); + } else { + // We could create under either the ref's scope or the ftask's scope. + // It shouldn't matter, as they are only local variables. + // We choose to do it under whichever called this function, which results + // in more cache locality. + AstVar* newvarp = new AstVar{invarp->fileline(), AstVarType::BLOCKTEMP, name, invarp}; + newvarp->funcLocal(false); + newvarp->propagateAttrFrom(invarp); + m_modp->addStmtp(newvarp); + AstVarScope* newvscp = new AstVarScope{newvarp->fileline(), m_scopep, newvarp}; + m_scopep->addVarp(newvscp); + return newvscp; + } } AstNode* createInlinedFTask(AstNodeFTaskRef* refp, const string& namePrefix, @@ -545,7 +583,7 @@ private: // Iteration requires a back, so put under temporary node { AstBegin* tempp = new AstBegin(beginp->fileline(), "[EditWrapper]", beginp); - TaskRelinkVisitor visitor(tempp); + TaskRelinkVisitor visitor{tempp}; tempp->stmtsp()->unlinkFrBackWithNext(); VL_DO_DANGLING(tempp->deleteTree(), tempp); } @@ -684,13 +722,13 @@ private: return dpiproto; } - AstNode* createDpiTemp(AstVar* portp, const string& suffix) { + static AstNode* createDpiTemp(AstVar* portp, const string& suffix) { const string stmt = portp->dpiTmpVarType(portp->name() + suffix) + ";\n"; return new AstCStmt(portp->fileline(), stmt); } - AstNode* createAssignInternalToDpi(AstVar* portp, bool isPtr, const string& frSuffix, - const string& toSuffix) { + static AstNode* createAssignInternalToDpi(AstVar* portp, bool isPtr, const string& frSuffix, + const string& toSuffix) { const string stmt = V3Task::assignInternalToDpi(portp, isPtr, frSuffix, toSuffix); return new AstCStmt(portp->fileline(), stmt); } @@ -763,6 +801,7 @@ private: AstCFunc* const funcp = new AstCFunc(nodep->fileline(), nodep->cname(), m_scopep, (rtnvarp ? rtnvarp->dpiArgType(true, true) : "")); funcp->dpiExportDispatcher(true); + funcp->dpiContext(nodep->dpiContext()); funcp->dontCombine(true); funcp->entryPoint(true); funcp->isStatic(true); @@ -894,6 +933,7 @@ private: : ""; AstCFunc* const funcp = new AstCFunc(nodep->fileline(), nodep->cname(), m_scopep, rtnType); funcp->dpiImportPrototype(true); + funcp->dpiContext(nodep->dpiContext()); funcp->dontCombine(true); funcp->entryPoint(false); funcp->isMethod(false); @@ -943,7 +983,7 @@ private: } } - void makePortList(AstNodeFTask* nodep, AstCFunc* dpip) { + static void makePortList(AstNodeFTask* nodep, AstCFunc* dpip) { // Copy nodep's list of function I/O to the new dpip c function for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { if (AstVar* portp = VN_CAST(stmtp, Var)) { @@ -1057,6 +1097,22 @@ private: } } + AstVarScope* makeDpiExporTrigger() { + AstVarScope* dpiExportTriggerp = v3Global.rootp()->dpiExportTriggerp(); + if (!dpiExportTriggerp) { + // Create the global DPI export trigger flag the first time we encounter a DPI export. + // This flag is set any time a DPI export is invoked, and cleared at the end of eval. + FileLine* const fl = m_topScopep->fileline(); + AstVar* const varp + = new AstVar{fl, AstVarType::VAR, "__Vdpi_export_trigger", VFlagBitPacked{}, 1}; + m_topScopep->scopep()->modp()->addStmtp(varp); + dpiExportTriggerp = new AstVarScope{fl, m_topScopep->scopep(), varp}; + m_topScopep->scopep()->addVarp(dpiExportTriggerp); + v3Global.rootp()->dpiExportTriggerp(dpiExportTriggerp); + } + return dpiExportTriggerp; + } + AstCFunc* makeUserFunc(AstNodeFTask* nodep, bool ftaskNoInline) { // Given a already cloned node, make a public C function, or a non-inline C function // Probably some of this work should be done later, but... @@ -1146,6 +1202,7 @@ private: cfuncp->funcPublic(nodep->taskPublic()); cfuncp->dpiExportImpl(nodep->dpiExport()); cfuncp->dpiImportWrapper(nodep->dpiImport()); + cfuncp->dpiContext(nodep->dpiContext()); if (nodep->dpiImport() || nodep->dpiExport()) { cfuncp->isStatic(true); cfuncp->isLoose(true); @@ -1192,18 +1249,27 @@ private: for (AstNode *nextp, *stmtp = nodep->stmtsp(); stmtp; stmtp = nextp) { nextp = stmtp->nextp(); if (AstVar* portp = VN_CAST(stmtp, Var)) { - if (portp->isIO()) { - // Move it to new function + if (portp->isParam() && VN_IS(portp->valuep(), InitArray)) { + // Move array parameters in functions into constant pool portp->unlinkFrBack(); - portp->funcLocal(true); - cfuncp->addArgsp(portp); + pushDeletep(portp); + AstNode* const tablep = v3Global.rootp()->constPoolp()->findTable( + VN_CAST(portp->valuep(), InitArray)); + portp->user2p(tablep); } else { - // "Normal" variable, mark inside function - portp->funcLocal(true); + if (portp->isIO()) { + // Move it to new function + portp->unlinkFrBack(); + portp->funcLocal(true); + cfuncp->addArgsp(portp); + } else { + // "Normal" variable, mark inside function + portp->funcLocal(true); + } + AstVarScope* newvscp = new AstVarScope{portp->fileline(), m_scopep, portp}; + m_scopep->addVarp(newvscp); + portp->user2p(newvscp); } - AstVarScope* newvscp = new AstVarScope(portp->fileline(), m_scopep, portp); - m_scopep->addVarp(newvscp); - portp->user2p(newvscp); } } @@ -1230,10 +1296,46 @@ private: // Iteration requires a back, so put under temporary node { AstBegin* tempp = new AstBegin(cfuncp->fileline(), "[EditWrapper]", cfuncp); - TaskRelinkVisitor visitor(tempp); + TaskRelinkVisitor visitor{tempp}; tempp->stmtsp()->unlinkFrBackWithNext(); VL_DO_DANGLING(tempp->deleteTree(), tempp); } + + if (cfuncp->dpiExportImpl()) { + // Mark all non-local variables written by the DPI exported function as being updated + // by DPI exports. This ensures correct ordering and change detection later. + const std::vector writtenps = TaskGatherWrittenVisitor::gather(cfuncp); + if (!writtenps.empty()) { + AstVarScope* const dpiExportTriggerp = makeDpiExporTrigger(); + FileLine* const fl = cfuncp->fileline(); + + // Set DPI export trigger flag every time the DPI export is called. + AstAssign* const assignp + = new AstAssign{fl, new AstVarRef{fl, dpiExportTriggerp, VAccess::WRITE}, + new AstConst{fl, AstConst::BitTrue{}}}; + // Add as first statement (to avoid issues with early returns) to exported function + if (cfuncp->stmtsp()) { + cfuncp->stmtsp()->addHereThisAsNext(assignp); + } else { + cfuncp->addStmtsp(assignp); + } + + // Add an always block sensitive to the DPI export trigger flag, and add an + // AstDpiExportUpdated node under it for each variable that are writen by the + // exported function. + AstAlways* const alwaysp = new AstAlways{ + fl, VAlwaysKwd::ALWAYS, + new AstSenTree{ + fl, new AstSenItem{fl, VEdgeType::ET_HIGHEDGE, + new AstVarRef{fl, dpiExportTriggerp, VAccess::READ}}}, + nullptr}; + for (AstVarScope* const varScopep : writtenps) { + alwaysp->addStmtp(new AstDpiExportUpdated{fl, varScopep}); + } + m_scopep->addActivep(alwaysp); + } + } + // Delete rest of cloned task and return new func VL_DO_DANGLING(pushDeletep(nodep), nodep); if (debug() >= 9) cfuncp->dumpTree(cout, "-userFunc: "); @@ -1728,8 +1830,8 @@ const char* V3Task::dpiTemporaryVarSuffix() { void V3Task::taskAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { - TaskStateVisitor visitors(nodep); - TaskVisitor visitor(nodep, &visitors); + TaskStateVisitor visitors{nodep}; + TaskVisitor visitor{nodep, &visitors}; } // Destruct before checking V3Global::dumpCheckGlobalTree("task", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index 92ecc8b5b..168ae24d4 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -171,6 +171,7 @@ private: AstNodeModule* m_topModp = nullptr; // Module to add variables to AstScope* m_topScopep = nullptr; // Scope to add variables to AstCFunc* m_cfuncp = nullptr; // C function adding to graph + AstCFunc* m_regFuncp = nullptr; // Trace registration function AstTraceDecl* m_tracep = nullptr; // Trace function adding to graph AstVarScope* m_activityVscp = nullptr; // Activity variable uint32_t m_activityNumber = 0; // Count of fields in activity variable @@ -472,69 +473,77 @@ private: } } - AstCFunc* newCFunc(AstCFuncType type, AstCFunc* callfromp, AstCFunc* regp, int& funcNump) { + AstCFunc* newCFunc(bool full, AstCFunc* topFuncp, int& funcNump, uint32_t baseCode = 0) { // Create new function - string name; - switch (type) { - case AstCFuncType::TRACE_FULL: name = "traceFullTop"; break; - case AstCFuncType::TRACE_FULL_SUB: name = "traceFullSub"; break; - case AstCFuncType::TRACE_CHANGE: name = "traceChgTop"; break; - case AstCFuncType::TRACE_CHANGE_SUB: name = "traceChgSub"; break; - default: m_topScopep->v3fatalSrc("Bad trace function type"); - } - name += cvtToStr(funcNump++); - FileLine* const flp = m_topScopep->fileline(); - AstCFunc* const funcp = new AstCFunc(flp, name, m_topScopep); - funcp->funcType(type); - funcp->dontCombine(true); - const bool isTopFunc - = type == AstCFuncType::TRACE_FULL || type == AstCFuncType::TRACE_CHANGE; - if (isTopFunc) { - funcp->argTypes("void* voidSelf, " + v3Global.opt.traceClassBase() + "* tracep"); - funcp->isStatic(true); - funcp->addInitsp(new AstCStmt( - flp, prefixNameProtect(m_topModp) + "* const __restrict vlSelf = static_cast<" - + prefixNameProtect(m_topModp) + "*>(voidSelf);\n")); - funcp->addInitsp(new AstCStmt(flp, symClassAssign())); + const bool isTopFunc = topFuncp == nullptr; + const string baseName = full && isTopFunc ? "trace_full_top_" + : full ? "trace_full_sub_" + : isTopFunc ? "trace_chg_top_" + : "trace_chg_sub_"; - } else { - funcp->argTypes(v3Global.opt.traceClassBase() + "* tracep"); - funcp->isStatic(false); - } + FileLine* const flp = m_topScopep->fileline(); + AstCFunc* const funcp = new AstCFunc(flp, baseName + cvtToStr(funcNump++), m_topScopep); + funcp->isTrace(true); + funcp->dontCombine(true); funcp->isLoose(true); - funcp->slow(type == AstCFuncType::TRACE_FULL || type == AstCFuncType::TRACE_FULL_SUB); + funcp->slow(full); + funcp->isStatic(isTopFunc); // Add it to top scope m_topScopep->addActivep(funcp); - // Add call to new function - if (callfromp) { + const auto addInitStr = [funcp, flp](const string& str) -> void { + funcp->addInitsp(new AstCStmt(flp, str)); + }; + if (isTopFunc) { + // Top functions + funcp->argTypes("void* voidSelf, " + v3Global.opt.traceClassBase() + "* tracep"); + addInitStr(voidSelfAssign(m_topModp)); + addInitStr(symClassAssign()); + // Add global activity check to change dump functions + if (!full) { // + addInitStr("if (VL_UNLIKELY(!vlSymsp->__Vm_activity)) return;\n"); + } + // Register function + if (full) { + m_regFuncp->addStmtsp(new AstText(flp, "tracep->addFullCb(", true)); + } else { + m_regFuncp->addStmtsp(new AstText(flp, "tracep->addChgCb(", true)); + } + m_regFuncp->addStmtsp(new AstAddrOfCFunc(flp, funcp)); + m_regFuncp->addStmtsp(new AstText(flp, ", vlSelf);\n", true)); + } else { + // Sub functions + funcp->argTypes(v3Global.opt.traceClassBase() + "* tracep"); + // Setup base references. Note in rare occasions we can end up with an empty trace + // sub function, hence the VL_ATTR_UNUSED attributes. + if (full) { + // Full dump sub function + addInitStr("vluint32_t* const oldp VL_ATTR_UNUSED = " + "tracep->oldp(vlSymsp->__Vm_baseCode);\n"); + } else { + // Change dump sub function + if (v3Global.opt.trueTraceThreads()) { + addInitStr("const vluint32_t base VL_ATTR_UNUSED = " + "vlSymsp->__Vm_baseCode + " + + cvtToStr(baseCode) + ";\n"); + addInitStr("if (false && tracep) {} // Prevent unused\n"); + } else { + addInitStr("vluint32_t* const oldp VL_ATTR_UNUSED = " + "tracep->oldp(vlSymsp->__Vm_baseCode + " + + cvtToStr(baseCode) + ");\n"); + } + } + // Add call to top function AstCCall* callp = new AstCCall(funcp->fileline(), funcp); callp->argTypes("tracep"); - callfromp->addStmtsp(callp); - } - // Register function - if (regp) { - if (type == AstCFuncType::TRACE_FULL) { - regp->addStmtsp(new AstText(flp, "tracep->addFullCb(", true)); - } else if (type == AstCFuncType::TRACE_CHANGE) { - regp->addStmtsp(new AstText(flp, "tracep->addChgCb(", true)); - } else { - funcp->v3fatalSrc("Don't know how to register this type of function"); - } - regp->addStmtsp(new AstAddrOfCFunc(flp, funcp)); - regp->addStmtsp(new AstText(flp, ", vlSelf);\n", true)); - } - // Add global activity check to TRACE_CHANGE functions - if (type == AstCFuncType::TRACE_CHANGE) { - funcp->addInitsp( - new AstCStmt(flp, string("if (VL_UNLIKELY(!vlSymsp->__Vm_activity)) return;\n"))); + topFuncp->addStmtsp(callp); } // Done UINFO(5, " newCFunc " << funcp << endl); return funcp; } - void createFullTraceFunction(const TraceVec& traces, uint32_t nAllCodes, uint32_t parallelism, - AstCFunc* regFuncp) { + void createFullTraceFunction(const TraceVec& traces, uint32_t nAllCodes, + uint32_t parallelism) { const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace() : std::numeric_limits::max(); @@ -571,20 +580,17 @@ private: ++m_statUniqSigs; // Create top function if not yet created - if (!topFuncp) { - topFuncp - = newCFunc(AstCFuncType::TRACE_FULL, nullptr, regFuncp, topFuncNum); - } + if (!topFuncp) { topFuncp = newCFunc(/* full: */ true, nullptr, topFuncNum); } // Crate new sub function if required if (!subFuncp || subStmts > splitLimit) { subStmts = 0; - subFuncp = newCFunc(AstCFuncType::TRACE_FULL_SUB, topFuncp, nullptr, - subFuncNum); + subFuncp = newCFunc(/* full: */ true, topFuncp, subFuncNum); } // Add TraceInc node - AstTraceInc* const incp = new AstTraceInc(declp->fileline(), declp, true); + AstTraceInc* const incp + = new AstTraceInc(declp->fileline(), declp, /* full: */ true); subFuncp->addStmtsp(incp); subStmts += EmitCBaseCounterVisitor(incp).count(); @@ -593,14 +599,14 @@ private: } } if (topFuncp) { // might be nullptr if all trailing entries were duplicates - UINFO(5, "traceFullTop" << topFuncNum - 1 << " codes: " << nCodes << "/" - << maxCodes << endl); + UINFO(5, "trace_full_top" << topFuncNum - 1 << " codes: " << nCodes << "/" + << maxCodes << endl); } } } - void createChgTraceFunctions(const TraceVec& traces, uint32_t nAllCodes, uint32_t parallelism, - AstCFunc* regFuncp) { + void createChgTraceFunctions(const TraceVec& traces, uint32_t nAllCodes, + uint32_t parallelism) { const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace() : std::numeric_limits::max(); int topFuncNum = 0; @@ -615,6 +621,7 @@ private: uint32_t nCodes = 0; const ActCodeSet* prevActSet = nullptr; AstIf* ifp = nullptr; + uint32_t baseCode = 0; for (; nCodes < maxCodes && it != traces.end(); ++it) { const TraceTraceVertex* const vtxp = it->second; // This is a duplicate decl, no need to add it to incremental dump @@ -623,16 +630,16 @@ private: // Traced value never changes, no need to add it to incremental dump if (actSet.count(TraceActivityVertex::ACTIVITY_NEVER)) continue; - // Create top function if not yet created - if (!topFuncp) { - topFuncp = newCFunc(AstCFuncType::TRACE_CHANGE, nullptr, regFuncp, topFuncNum); - } + AstTraceDecl* const declp = vtxp->nodep(); - // Crate new sub function if required + // Create top function if not yet created + if (!topFuncp) { topFuncp = newCFunc(/* full: */ false, nullptr, topFuncNum); } + + // Create new sub function if required if (!subFuncp || subStmts > splitLimit) { + baseCode = declp->code(); subStmts = 0; - subFuncp - = newCFunc(AstCFuncType::TRACE_CHANGE_SUB, topFuncp, nullptr, subFuncNum); + subFuncp = newCFunc(/* full: */ false, topFuncp, subFuncNum, baseCode); prevActSet = nullptr; ifp = nullptr; } @@ -658,8 +665,8 @@ private: } // Add TraceInc node - AstTraceDecl* const declp = vtxp->nodep(); - AstTraceInc* const incp = new AstTraceInc(declp->fileline(), declp, VAccess::READ); + AstTraceInc* const incp + = new AstTraceInc(declp->fileline(), declp, /* full: */ false, baseCode); ifp->addIfsp(incp); subStmts += EmitCBaseCounterVisitor(incp).count(); @@ -667,31 +674,29 @@ private: nCodes += declp->codeInc(); } if (topFuncp) { // might be nullptr if all trailing entries were duplicates/constants - UINFO(5, "traceChgTop" << topFuncNum - 1 << " codes: " << nCodes << "/" << maxCodes - << endl); + UINFO(5, "trace_chg_top" << topFuncNum - 1 << " codes: " << nCodes << "/" + << maxCodes << endl); } } } - void createCleanupFunction(AstCFunc* regFuncp) { + void createCleanupFunction() { FileLine* const fl = m_topScopep->fileline(); - AstCFunc* const cleanupFuncp = new AstCFunc(fl, "traceCleanup", m_topScopep); + AstCFunc* const cleanupFuncp = new AstCFunc(fl, "trace_cleanup", m_topScopep); cleanupFuncp->argTypes("void* voidSelf, " + v3Global.opt.traceClassBase() + "* /*unused*/"); - cleanupFuncp->funcType(AstCFuncType::TRACE_CLEANUP); + cleanupFuncp->isTrace(true); cleanupFuncp->slow(false); cleanupFuncp->isStatic(true); cleanupFuncp->isLoose(true); m_topScopep->addActivep(cleanupFuncp); - cleanupFuncp->addInitsp(new AstCStmt( - fl, prefixNameProtect(m_topModp) + "* const __restrict vlSelf = static_cast<" - + prefixNameProtect(m_topModp) + "*>(voidSelf);\n")); + cleanupFuncp->addInitsp(new AstCStmt(fl, voidSelfAssign(m_topModp))); cleanupFuncp->addInitsp(new AstCStmt(fl, symClassAssign())); // Register it - regFuncp->addStmtsp(new AstText(fl, "tracep->addCleanupCb(", true)); - regFuncp->addStmtsp(new AstAddrOfCFunc(fl, cleanupFuncp)); - regFuncp->addStmtsp(new AstText(fl, ", vlSelf);\n", true)); + m_regFuncp->addStmtsp(new AstText(fl, "tracep->addCleanupCb(", true)); + m_regFuncp->addStmtsp(new AstAddrOfCFunc(fl, cleanupFuncp)); + m_regFuncp->addStmtsp(new AstText(fl, ", vlSelf);\n", true)); // Clear global activity flag cleanupFuncp->addStmtsp( @@ -735,22 +740,21 @@ private: // last value vector is more compact // Create the trace registration function - AstCFunc* const regFuncp - = new AstCFunc(m_topScopep->fileline(), "traceRegister", m_topScopep); - regFuncp->argTypes(v3Global.opt.traceClassBase() + "* tracep"); - regFuncp->funcType(AstCFuncType::TRACE_REGISTER); - regFuncp->slow(true); - regFuncp->isStatic(false); - regFuncp->isLoose(true); - m_topScopep->addActivep(regFuncp); + m_regFuncp = new AstCFunc(m_topScopep->fileline(), "trace_register", m_topScopep); + m_regFuncp->argTypes(v3Global.opt.traceClassBase() + "* tracep"); + m_regFuncp->isTrace(true); + m_regFuncp->slow(true); + m_regFuncp->isStatic(false); + m_regFuncp->isLoose(true); + m_topScopep->addActivep(m_regFuncp); const int parallelism = 1; // Note: will bump this later, code below works for any value // Create the full dump functions, also allocates signal numbers - createFullTraceFunction(traces, nFullCodes, parallelism, regFuncp); + createFullTraceFunction(traces, nFullCodes, parallelism); // Create the incremental dump functions - createChgTraceFunctions(traces, nChgCodes, parallelism, regFuncp); + createChgTraceFunctions(traces, nChgCodes, parallelism); // Remove refs to traced values from TraceDecl nodes, these have now moved under // TraceInc @@ -761,7 +765,7 @@ private: } // Create the trace cleanup function clearing the activity flags - createCleanupFunction(regFuncp); + createCleanupFunction(); } TraceCFuncVertex* getCFuncVertexp(AstCFunc* nodep) { @@ -903,6 +907,6 @@ public: void V3Trace::traceAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { TraceVisitor visitor(nodep); } // Destruct before checking + { TraceVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("trace", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index 873d66b80..066aa51ce 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -69,13 +69,13 @@ private: return nullptr; } - AstCFunc* newCFunc(AstCFuncType type, const string& name) { + AstCFunc* newCFunc(const string& name) { FileLine* const flp = m_topScopep->fileline(); AstCFunc* const funcp = new AstCFunc(flp, name, m_topScopep); string argTypes = v3Global.opt.traceClassBase() + "* tracep"; if (m_interface) argTypes += ", int scopet, const char* scopep"; funcp->argTypes(argTypes); - funcp->funcType(type); + funcp->isTrace(true); funcp->isStatic(false); funcp->isLoose(true); funcp->slow(true); @@ -94,8 +94,11 @@ private: basep->addStmtsp(callp); } AstCFunc* newCFuncSub(AstCFunc* basep) { - const string name = "traceInitSub" + cvtToStr(m_funcNum++); - AstCFunc* const funcp = newCFunc(AstCFuncType::TRACE_INIT_SUB, name); + FileLine* const fl = basep->fileline(); + const string name = "trace_init_sub_" + cvtToStr(m_funcNum++); + AstCFunc* const funcp = newCFunc(name); + funcp->addInitsp(new AstCStmt(fl, "const int c = vlSymsp->__Vm_baseCode;\n")); + funcp->addInitsp(new AstCStmt(fl, "if (false && tracep && c) {} // Prevent unused\n")); if (!m_interface) callCFuncSub(basep, funcp, nullptr); return funcp; } @@ -135,7 +138,7 @@ private: virtual void visit(AstTopScope* nodep) override { m_topScopep = nodep->scopep(); // Create the trace init function - m_initFuncp = newCFunc(AstCFuncType::TRACE_INIT, "traceInitTop"); + m_initFuncp = newCFunc("trace_init_top"); // Create initial sub function m_initSubFuncp = newCFuncSub(m_initFuncp); // And find variables @@ -250,7 +253,7 @@ private: VL_RESTORER(m_traShowname); VL_RESTORER(m_traValuep); { - m_traShowname += string("(") + cvtToStr(i) + string(")"); + m_traShowname += string("[") + cvtToStr(i) + string("]"); m_traValuep = new AstArraySel( nodep->fileline(), m_traValuep->cloneTree(true), i - nodep->lo()); @@ -275,7 +278,7 @@ private: VL_RESTORER(m_traShowname); VL_RESTORER(m_traValuep); { - m_traShowname += string("(") + cvtToStr(i) + string(")"); + m_traShowname += string("[") + cvtToStr(i) + string("]"); m_traValuep = new AstSel(nodep->fileline(), m_traValuep->cloneTree(true), (i - nodep->lo()) * subtypep->width(), subtypep->width()); @@ -358,6 +361,6 @@ public: void V3TraceDecl::traceDeclAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { TraceDeclVisitor visitor(nodep); } // Destruct before checking + { TraceDeclVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("tracedecl", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index dc85754bb..6be7c1621 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -1213,7 +1213,7 @@ class TristateVisitor final : public TristateBaseVisitor { // simple, it will flip ArraySel's and such, but if the // pin is an input the earlier reconnectSimple made it // a VarRef without any ArraySel, etc - TristatePinVisitor visitor(outexprp, m_tgraph, true); + TristatePinVisitor visitor{outexprp, m_tgraph, true}; } if (debug() >= 9) outpinp->dumpTree(cout, "-pin-opr: "); outAssignp = V3Inst::pinReconnectSimple(outpinp, m_cellp, @@ -1224,7 +1224,7 @@ class TristateVisitor final : public TristateBaseVisitor { } // Existing pin becomes an input, and we mark each resulting signal as tristate - TristatePinVisitor visitor(nodep->exprp(), m_tgraph, false); + TristatePinVisitor visitor{nodep->exprp(), m_tgraph, false}; AstNode* inAssignp = V3Inst::pinReconnectSimple( nodep, m_cellp, true); // Note may change nodep->exprp() if (debug() >= 9) nodep->dumpTree(cout, "-pin-in: "); @@ -1424,6 +1424,6 @@ public: void V3Tristate::tristateAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { TristateVisitor visitor(nodep); } // Destruct before checking + { TristateVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("tristate", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 4f8f4bcb4..ccf6ea283 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -461,5 +461,5 @@ public: void V3Undriven::undrivenAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - UndrivenVisitor visitor(nodep); + UndrivenVisitor visitor{nodep}; } diff --git a/src/V3UniqueNames.h b/src/V3UniqueNames.h new file mode 100644 index 000000000..f33c86a12 --- /dev/null +++ b/src/V3UniqueNames.h @@ -0,0 +1,64 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Basic data structure to keep names unique +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2005-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 +// +//************************************************************************* +// +//************************************************************************* + +#ifndef VERILATOR_V3UNIQUENAMES_H_ +#define VERILATOR_V3UNIQUENAMES_H_ + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Hasher.h" + +#include +#include + +class V3UniqueNames final { + const std::string m_prefix; // Prefix to attach to all names + + std::unordered_map m_multiplicity; // Suffix number for given key + +public: + V3UniqueNames() + : m_prefix{""} {} + explicit V3UniqueNames(const std::string& prefix) + : m_prefix{prefix} {} + + // Return argument, prepended with the prefix if any, then appended with a unique suffix each + // time we are called with the same argument. + std::string get(const std::string& name) { + const unsigned num = m_multiplicity.emplace(name, 0).first->second++; + std::string result; + if (!m_prefix.empty()) { + result += m_prefix; + result += "_"; + } + result += name; + result += "__"; + result += cvtToStr(num); + return result; + } + + // Return hash of node as string, prepended with the prefix if any, appended with a unique + // suffix each time we are called with a node that hashes to the same value. + std::string get(const AstNode* nodep) { return get(V3Hasher::uncachedHash(nodep).toString()); } + + // Reset to initial state (as if just constructed) + void reset() { m_multiplicity.clear(); } +}; + +#endif // Guard diff --git a/src/V3Unknown.cpp b/src/V3Unknown.cpp index fb0d89866..5ac9b5763 100644 --- a/src/V3Unknown.cpp +++ b/src/V3Unknown.cpp @@ -36,6 +36,7 @@ #include "V3Ast.h" #include "V3Const.h" #include "V3Stats.h" +#include "V3UniqueNames.h" #include @@ -57,6 +58,8 @@ private: AstAssignDly* m_assigndlyp = nullptr; // Current assignment bool m_constXCvt = false; // Convert X's VDouble0 m_statUnkVars; // Statistic tracking + V3UniqueNames m_lvboundNames; // For generating unique temporary variable names + V3UniqueNames m_xrandNames; // For generating unique temporary variable names // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -113,11 +116,10 @@ private: UINFO(4, "Edit BOUNDLVALUE " << newp << endl); replaceHandle.relink(newp); } else { - const string name = (string("__Vlvbound") + cvtToStr(m_modp->varNumGetInc())); - AstVar* varp = new AstVar(fl, AstVarType::MODULETEMP, name, prep->dtypep()); + AstVar* const varp + = new AstVar(fl, AstVarType::MODULETEMP, m_lvboundNames.get(prep), prep->dtypep()); m_modp->addStmtp(varp); - - AstNode* abovep = prep->backp(); // Grab above point before lose it w/ next replace + AstNode* const abovep = prep->backp(); // Grab above point before we replace 'prep' prep->replaceWith(new AstVarRef(fl, varp, VAccess::WRITE)); AstIf* newp = new AstIf( fl, condp, @@ -142,6 +144,8 @@ private: { m_modp = nodep; m_constXCvt = true; + m_lvboundNames.reset(); + m_xrandNames.reset(); iterateChildren(nodep); } } @@ -322,15 +326,16 @@ private: // Make a Vxrand variable // We use the special XTEMP type so it doesn't break pure functions UASSERT_OBJ(m_modp, nodep, "X number not under module"); - const string newvarname = (string("__Vxrand") + cvtToStr(m_modp->varNumGetInc())); - AstVar* newvarp = new AstVar(nodep->fileline(), AstVarType::XTEMP, newvarname, - VFlagLogicPacked(), nodep->width()); + AstVar* const newvarp + = new AstVar(nodep->fileline(), AstVarType::XTEMP, m_xrandNames.get(nodep), + VFlagLogicPacked(), nodep->width()); ++m_statUnkVars; AstNRelinker replaceHandle; nodep->unlinkFrBack(&replaceHandle); - AstNodeVarRef* newref1p = new AstVarRef(nodep->fileline(), newvarp, VAccess::READ); + AstNodeVarRef* const newref1p + = new AstVarRef(nodep->fileline(), newvarp, VAccess::READ); replaceHandle.relink(newref1p); // Replace const with varref - AstInitial* newinitp = new AstInitial( + AstInitial* const newinitp = new AstInitial( nodep->fileline(), new AstAssign( nodep->fileline(), @@ -342,7 +347,7 @@ private: nodep->dtypep(), true))))); // Add inits in front of other statement. // In the future, we should stuff the initp into the module's constructor. - AstNode* afterp = m_modp->stmtsp()->unlinkFrBackWithNext(); + AstNode* const afterp = m_modp->stmtsp()->unlinkFrBackWithNext(); m_modp->addStmtp(newvarp); m_modp->addStmtp(newinitp); m_modp->addStmtp(afterp); @@ -476,7 +481,11 @@ private: public: // CONSTRUCTORS - explicit UnknownVisitor(AstNetlist* nodep) { iterate(nodep); } + explicit UnknownVisitor(AstNetlist* nodep) + : m_lvboundNames{"__Vlvbound"} + , m_xrandNames{"__Vxrand"} { + iterate(nodep); + } virtual ~UnknownVisitor() override { // V3Stats::addStat("Unknowns, variables created", m_statUnkVars); } @@ -487,6 +496,6 @@ public: void V3Unknown::unknownAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { UnknownVisitor visitor(nodep); } // Destruct before checking + { UnknownVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("unknown", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3VariableOrder.cpp b/src/V3VariableOrder.cpp new file mode 100644 index 000000000..ea88cd98f --- /dev/null +++ b/src/V3VariableOrder.cpp @@ -0,0 +1,207 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Variable ordering +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* +// V3VariableOrder's Transformations: +// +// Each module: +// Order module variables +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Global.h" +#include "V3VariableOrder.h" +#include "V3Ast.h" +#include "V3AstUserAllocator.h" +#include "V3EmitCBase.h" +#include "V3TSP.h" + +#include +#include + +//###################################################################### +// Establish mtask variable sort order in mtasks mode + +class VarTspSorter final : public V3TSP::TspStateBase { +private: + // MEMBERS + const MTaskIdSet& m_mtaskIds; // Mtask we're ordering + static unsigned s_serialNext; // Unique ID to establish serial order + unsigned m_serial; // Serial ordering +public: + // CONSTRUCTORS + explicit VarTspSorter(const MTaskIdSet& mtaskIds) + : m_mtaskIds(mtaskIds) { // Cannot be {} or GCC 4.8 false warning + m_serial = ++s_serialNext; // Cannot be ()/{} or GCC 4.8 false warning + } + virtual ~VarTspSorter() = default; + // METHODS + virtual bool operator<(const TspStateBase& other) const override { + return operator<(dynamic_cast(other)); + } + bool operator<(const VarTspSorter& other) const { return m_serial < other.m_serial; } + const MTaskIdSet& mtaskIds() const { return m_mtaskIds; } + virtual int cost(const TspStateBase* otherp) const override { + return cost(dynamic_cast(otherp)); + } + virtual int cost(const VarTspSorter* otherp) const { + int cost = diffs(m_mtaskIds, otherp->m_mtaskIds); + cost += diffs(otherp->m_mtaskIds, m_mtaskIds); + return cost; + } + // Returns the number of elements in set_a that don't appear in set_b + static int diffs(const MTaskIdSet& set_a, const MTaskIdSet& set_b) { + int diffs = 0; + for (int i : set_a) { + if (set_b.find(i) == set_b.end()) ++diffs; + } + return diffs; + } +}; + +unsigned VarTspSorter::s_serialNext = 0; + +class VariableOrder final { + // NODE STATE + // AstVar::user1() -> attributes, via m_attributes + AstUser1InUse m_user1InUse; // AstVar + + struct VarAttributes { + uint32_t stratum; // Roughly equivalent to alignment requirement, to avoid padding + bool anonOk; // Can be emitted as part of anonymous structure + }; + + AstUser1Allocator m_attributes; // Attributes used for sorting + + //###################################################################### + + // Simple sort + void simpleSortVars(std::vector& varps) { + stable_sort(varps.begin(), varps.end(), + [this](const AstVar* ap, const AstVar* bp) -> bool { + if (ap->isStatic() != bp->isStatic()) { // Non-statics before statics + return bp->isStatic(); + } + const auto& attrA = m_attributes(ap); + const auto& attrB = m_attributes(bp); + if (attrA.anonOk != attrB.anonOk) { // Anons before non-anons + return attrA.anonOk; + } + return attrA.stratum < attrB.stratum; // Finally sort by stratum + }); + } + + // Sort by MTask-affinity first, then the same as simpleSortVars + void tspSortVars(std::vector& varps) { + // Map from "MTask affinity" -> "variable list" + std::map> m2v; + for (AstVar* const varp : varps) { m2v[varp->mtaskIds()].push_back(varp); } + + // Create a TSP sort state for each unique MTaskIdSet, except for the empty set + V3TSP::StateVec states; + for (const auto& pair : m2v) { + if (pair.first.empty()) continue; + states.push_back(new VarTspSorter(pair.first)); + } + + // Do the TSP sort + V3TSP::StateVec sortedStates; + V3TSP::tspSort(states, &sortedStates); + + varps.clear(); + + // Helper function to sort given vector, then append to 'varps' + const auto sortAndAppend = [this, &varps](std::vector& subVarps) { + simpleSortVars(subVarps); + for (AstVar* const varp : subVarps) { varps.push_back(varp); } + }; + + // Enumerate by sorted MTaskIdSet, sort within the set separately + for (const V3TSP::TspStateBase* const stateBasep : sortedStates) { + const VarTspSorter* const statep = dynamic_cast(stateBasep); + sortAndAppend(m2v[statep->mtaskIds()]); + VL_DO_DANGLING(delete statep, statep); + } + + // Finally add the variables with no known MTask affinity + sortAndAppend(m2v[MTaskIdSet()]); + } + + void orderModuleVars(AstNodeModule* modp) { + std::vector varps; + + // Unlink all module variables from the module, compute attributes + for (AstNode *nodep = modp->stmtsp(), *nextp; nodep; nodep = nextp) { + nextp = nodep->nextp(); + if (AstVar* const varp = VN_CAST(nodep, Var)) { + // Unlink, add to vector + varp->unlinkFrBack(); + varps.push_back(varp); + // Compute attributes up front + auto& attributes = m_attributes(varp); + // Stratum + const int sigbytes = varp->dtypeSkipRefp()->widthAlignBytes(); + attributes.stratum = (varp->isUsedClock() && varp->widthMin() == 1) ? 0 + : VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType) ? 8 + : (varp->basicp() && varp->basicp()->isOpaque()) ? 7 + : (varp->isScBv() || varp->isScBigUint()) ? 6 + : (sigbytes == 8) ? 5 + : (sigbytes == 4) ? 4 + : (sigbytes == 2) ? 2 + : (sigbytes == 1) ? 1 + : 9; + // Anonymous structure ok + attributes.anonOk = EmitCBaseVisitor::isAnonOk(varp); + } + } + + if (!varps.empty()) { + // Sort variables + if (!v3Global.opt.mtasks()) { + simpleSortVars(varps); + } else { + tspSortVars(varps); + } + + // Insert them back under the module, in the new order, but at + // the front of the list so they come out first in dumps/XML. + auto it = varps.cbegin(); + AstVar* const firstp = *it++; + for (; it != varps.cend(); ++it) firstp->addNext(*it); + if (AstNode* const stmtsp = modp->stmtsp()) { + stmtsp->unlinkFrBackWithNext(); + firstp->addNext(stmtsp); + } + modp->addStmtp(firstp); + } + } + +public: + static void processModule(AstNodeModule* modp) { VariableOrder().orderModuleVars(modp); } +}; + +//###################################################################### +// V3VariableOrder static functions + +void V3VariableOrder::orderAll() { + UINFO(2, __FUNCTION__ << ": " << endl); + for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; + modp = VN_CAST(modp->nextp(), NodeModule)) { + VariableOrder::processModule(modp); + } + V3Global::dumpCheckGlobalTree("variableorder", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); +} diff --git a/src/V3VariableOrder.h b/src/V3VariableOrder.h new file mode 100644 index 000000000..0b84c9894 --- /dev/null +++ b/src/V3VariableOrder.h @@ -0,0 +1,30 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Variable ordering +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* + +#ifndef VERILATOR_V3VARIABLEORDER_H_ +#define VERILATOR_V3VARIABLEORDER_H_ + +#include "config_build.h" +#include "verilatedos.h" + +//============================================================================ + +class V3VariableOrder final { +public: + static void orderAll(); +}; + +#endif // Guard diff --git a/src/V3Waiver.cpp b/src/V3Waiver.cpp index 31b6b9c86..7e96d7ca3 100644 --- a/src/V3Waiver.cpp +++ b/src/V3Waiver.cpp @@ -30,7 +30,7 @@ void V3Waiver::addEntry(V3ErrorCode errorCode, const std::string& filename, } void V3Waiver::write(const std::string& filename) { - const std::unique_ptr ofp(V3File::new_ofstream(filename)); + const std::unique_ptr ofp{V3File::new_ofstream(filename)}; if (ofp->fail()) v3fatal("Can't write " << filename); *ofp << "// DESCR" diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 5f67404e4..24b8e9c12 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1078,6 +1078,12 @@ private: // We don't size the constant until we commit the widths, as need parameters // to remain unsized, and numbers to remain unsized to avoid backp() warnings } + virtual void visit(AstEmptyQueue* nodep) override { + nodep->dtypeSetEmptyQueue(); + if (!VN_IS(nodep->backp(), Assign)) + nodep->v3warn(E_UNSUPPORTED, + "Unsupported/Illegal: empty queue ('{}') in this context"); + } virtual void visit(AstFell* nodep) override { if (m_vup->prelim()) { iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); @@ -1173,6 +1179,7 @@ private: return; } } + nodep->backp()->dumpTree(cout, "-FIXME-tr "); nodep->v3warn(E_UNSUPPORTED, "Unsupported/illegal unbounded ('$') in this context."); } virtual void visit(AstIsUnbounded* nodep) override { @@ -3761,6 +3768,23 @@ private: nodep->v3warn(E_UNSUPPORTED, "Unsupported: assignment of event data type"); } } + if (VN_IS(nodep->rhsp(), EmptyQueue)) { + UINFO(9, "= {} -> .delete(): " << nodep); + if (!VN_IS(nodep->lhsp()->dtypep()->skipRefp(), QueueDType)) { + nodep->v3warn(E_UNSUPPORTED, + "Unsupported/Illegal: empty queue ('{}') in this assign context"); + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return; + } + AstMethodCall* const newp = new AstMethodCall{ + nodep->fileline(), nodep->lhsp()->unlinkFrBack(), "delete", nullptr}; + newp->makeStatement(); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + // Need to now convert it + visit(newp); + return; + } if (AstNewDynamic* dynp = VN_CAST(nodep->rhsp(), NewDynamic)) { UINFO(9, "= new[] -> .resize(): " << nodep); AstCMethodHard* newp; @@ -3777,6 +3801,7 @@ private: newp->makeStatement(); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); + // return; } } @@ -4127,6 +4152,9 @@ private: // TOP LEVEL NODE if (nodep->modVarp() && nodep->modVarp()->isGParam()) { // Widthing handled as special init() case + if (auto* patternp = VN_CAST(nodep->exprp(), Pattern)) + if (auto* modVarp = nodep->modVarp()) + patternp->childDTypep(modVarp->childDTypep()->cloneTree(false)); userIterateChildren(nodep, WidthVP(SELF, BOTH).p()); } else if (!m_paramsOnly) { if (!nodep->modVarp()->didWidth()) { @@ -5079,9 +5107,9 @@ private: case EXTEND_LHS: doSigned = nodep->isSigned(); break; default: nodep->v3fatalSrc("bad case"); } - AstNode* newp - = (doSigned ? static_cast(new AstExtendS(nodep->fileline(), nodep)) - : static_cast(new AstExtend(nodep->fileline(), nodep))); + AstNode* const newp + = (doSigned ? static_cast(new AstExtendS{nodep->fileline(), nodep}) + : static_cast(new AstExtend{nodep->fileline(), nodep})); linker.relink(newp); nodep = newp; } @@ -6158,8 +6186,8 @@ void V3Width::width(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { // We should do it in bottom-up module order, but it works in any order. - WidthClearVisitor cvisitor(nodep); - WidthVisitor visitor(false, false); + WidthClearVisitor cvisitor{nodep}; + WidthVisitor visitor{false, false}; (void)visitor.mainAcceptEdit(nodep); WidthRemoveVisitor rvisitor; (void)rvisitor.mainAcceptEdit(nodep); @@ -6172,7 +6200,7 @@ void V3Width::width(AstNetlist* nodep) { AstNode* V3Width::widthParamsEdit(AstNode* nodep) { UINFO(4, __FUNCTION__ << ": " << nodep << endl); // We should do it in bottom-up module order, but it works in any order. - WidthVisitor visitor(true, false); + WidthVisitor visitor{true, false}; nodep = visitor.mainAcceptEdit(nodep); // No WidthRemoveVisitor, as don't want to drop $signed etc inside gen blocks return nodep; @@ -6191,7 +6219,7 @@ AstNode* V3Width::widthGenerateParamsEdit( AstNode* nodep) { //!< [in] AST whose parameters widths are to be analysed. UINFO(4, __FUNCTION__ << ": " << nodep << endl); // We should do it in bottom-up module order, but it works in any order. - WidthVisitor visitor(true, true); + WidthVisitor visitor{true, true}; nodep = visitor.mainAcceptEdit(nodep); // No WidthRemoveVisitor, as don't want to drop $signed etc inside gen blocks return nodep; @@ -6199,6 +6227,6 @@ AstNode* V3Width::widthGenerateParamsEdit( void V3Width::widthCommit(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { WidthCommitVisitor visitor(nodep); } // Destruct before checking + { WidthCommitVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("widthcommit", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 9a742cafd..28400d9e4 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -34,6 +34,7 @@ #include "V3Clean.h" #include "V3Clock.h" #include "V3Combine.h" +#include "V3Common.h" #include "V3Const.h" #include "V3Coverage.h" #include "V3CoverageJoin.h" @@ -96,6 +97,7 @@ #include "V3Undriven.h" #include "V3Unknown.h" #include "V3Unroll.h" +#include "V3VariableOrder.h" #include "V3Waiver.h" #include "V3Width.h" @@ -501,24 +503,31 @@ static void process() { V3Partition::finalize(); } + if (!v3Global.opt.lintOnly() && !v3Global.opt.xmlOnly() && !v3Global.opt.dpiHdrOnly()) { + // Add common methods/etc to modules + V3Common::commonAll(); + + // Order variables + V3VariableOrder::orderAll(); + + // Create AstCUse to determine what class forward declarations/#includes needed in C + V3CUse::cUseAll(); + } + // Output the text if (!v3Global.opt.lintOnly() && !v3Global.opt.xmlOnly() && !v3Global.opt.dpiHdrOnly()) { - // Create AstCUse to determine what class forward declarations/#includes needed in C - // Must be before V3EmitC - V3CUse::cUseAll(); - // emitcInlines is first, as it may set needHInlines which other emitters read V3EmitC::emitcInlines(); V3EmitC::emitcSyms(); V3EmitC::emitcConstPool(); V3EmitC::emitcModel(); - V3EmitC::emitcTrace(); + V3EmitC::emitcHeaders(); } else if (v3Global.opt.dpiHdrOnly()) { V3EmitC::emitcSyms(true); } if (!v3Global.opt.xmlOnly() - && !v3Global.opt.dpiHdrOnly()) { // Unfortunately we have some lint checks in emitc. - V3EmitC::emitc(); + && !v3Global.opt.dpiHdrOnly()) { // Unfortunately we have some lint checks in emitcImp. + V3EmitC::emitcImp(); } if (v3Global.opt.xmlOnly() // Check XML when debugging to make sure no missing node types diff --git a/src/VlcBucket.h b/src/VlcBucket.h index 3add0dd46..89e758c45 100644 --- a/src/VlcBucket.h +++ b/src/VlcBucket.h @@ -39,7 +39,7 @@ private: if (m_dataSize < point) m_dataSize = (point + 64) & ~63ULL; // Keep power of two m_dataSize *= 2; // UINFO(9, "Realloc "<(std::realloc(m_datap, allocSize())); + vluint64_t* const newp = static_cast(std::realloc(m_datap, allocSize())); if (VL_UNCOVERABLE(!newp)) { // cppcheck-suppress doubleFree // cppcheck 1.90 bug - realloc doesn't free on fail free(m_datap); // LCOV_EXCL_LINE diff --git a/src/VlcMain.cpp b/src/VlcMain.cpp index f29fef2c4..f8c57d7c8 100644 --- a/src/VlcMain.cpp +++ b/src/VlcMain.cpp @@ -83,7 +83,7 @@ void VlcOptions::parseOptsList(int argc, char** argv) { i += consumed; } else { v3fatal("Invalid option: " << argv[i] << parser.getSuggestion(argv[i])); - ++i; + ++i; // LCOV_EXCL_LINE } } else { addReadFile(argv[i]); diff --git a/src/VlcOptions.h b/src/VlcOptions.h index 22d98dd4a..6bebf0902 100644 --- a/src/VlcOptions.h +++ b/src/VlcOptions.h @@ -35,11 +35,11 @@ class VlcOptions final { // MEMBERS (general options) // clang-format off string m_annotateOut; // main switch: --annotate I - bool m_annotateAll=false; // main switch: --annotate-all - int m_annotateMin=10; // main switch: --annotate-min I + bool m_annotateAll = false; // main switch: --annotate-all + int m_annotateMin = 10; // main switch: --annotate-min I VlStringSet m_readFiles; // main switch: --read - bool m_rank=false; // main switch: --rank - bool m_unlink=false; // main switch: --unlink + bool m_rank = false; // main switch: --rank + bool m_unlink = false; // main switch: --unlink string m_writeFile; // main switch: --write string m_writeInfoFile; // main switch: --write-info // clang-format on diff --git a/src/VlcPoint.h b/src/VlcPoint.h index 26c8f5c65..c9ea193bb 100644 --- a/src/VlcPoint.h +++ b/src/VlcPoint.h @@ -127,7 +127,7 @@ public: m_points[pointnum].countInc(count); } else { pointnum = m_numPoints++; - VlcPoint point(name, pointnum); + VlcPoint point{name, pointnum}; point.countInc(count); m_points.push_back(point); m_nameMap.emplace(point.name(), point.pointNum()); diff --git a/src/VlcTest.h b/src/VlcTest.h index 7aa4cb77a..cf354d6aa 100644 --- a/src/VlcTest.h +++ b/src/VlcTest.h @@ -112,7 +112,7 @@ public: for (const auto& testp : m_tests) testp->dump(bucketsToo); } VlcTest* newTest(const string& name, vluint64_t testrun, double comp) { - VlcTest* testp = new VlcTest(name, testrun, comp); + VlcTest* const testp = new VlcTest{name, testrun, comp}; m_tests.push_back(testp); return testp; } diff --git a/src/VlcTop.cpp b/src/VlcTop.cpp index 606404666..219218232 100644 --- a/src/VlcTop.cpp +++ b/src/VlcTop.cpp @@ -27,14 +27,14 @@ void VlcTop::readCoverage(const string& filename, bool nonfatal) { UINFO(2, "readCoverage " << filename << endl); - std::ifstream is(filename.c_str()); + std::ifstream is{filename.c_str()}; if (!is) { if (!nonfatal) v3fatal("Can't read " << filename); return; } // Testrun and computrons argument unsupported as yet - VlcTest* testp = tests().newTest(filename, 0, 0); + VlcTest* const testp = tests().newTest(filename, 0, 0); while (!is.eof()) { const string line = V3Os::getline(is); @@ -63,7 +63,7 @@ void VlcTop::readCoverage(const string& filename, bool nonfatal) { void VlcTop::writeCoverage(const string& filename) { UINFO(2, "writeCoverage " << filename << endl); - std::ofstream os(filename.c_str()); + std::ofstream os{filename.c_str()}; if (!os) { v3fatal("Can't write " << filename); return; @@ -79,7 +79,7 @@ void VlcTop::writeCoverage(const string& filename) { void VlcTop::writeInfo(const string& filename) { UINFO(2, "writeInfo " << filename << endl); - std::ofstream os(filename.c_str()); + std::ofstream os{filename.c_str()}; if (!os) { v3fatal("Can't write " << filename); return; @@ -157,7 +157,7 @@ void VlcTop::rank() { VlcBuckets remaining; for (const auto& i : m_points) { - VlcPoint* pointp = &points().pointNumber(i.second); + VlcPoint* const pointp = &points().pointNumber(i.second); // If any tests hit this point, then we'll need to cover it. if (pointp->testsCovering()) remaining.addData(pointp->pointNum(), 1); } @@ -182,7 +182,7 @@ void VlcTop::rank() { } } } - if (VlcTest* testp = bestTestp) { + if (VlcTest* const testp = bestTestp) { testp->rank(nextrank++); testp->rankPoints(bestRemain); remaining.orData(bestTestp->buckets()); @@ -227,7 +227,7 @@ void VlcTop::annotateCalc() { } else if (*covp == '-') { range = true; } else if (std::isdigit(*covp)) { - const char* digitsp = covp; + const char* const digitsp = covp; while (std::isdigit(*covp)) ++covp; --covp; // Will inc in for loop if (!range) start = std::atoi(digitsp); @@ -279,13 +279,13 @@ void VlcTop::annotateOutputFiles(const string& dirname) { UINFO(1, "annotateOutputFile " << filename << " -> " << outfilename << endl); - std::ifstream is(filename.c_str()); + std::ifstream is{filename.c_str()}; if (!is) { v3error("Can't read " << filename); return; } - std::ofstream os(outfilename.c_str()); + std::ofstream os{outfilename.c_str()}; if (!os) { v3fatal("Can't write " << outfilename); return; diff --git a/src/verilog.l b/src/verilog.l index 75739e32a..3c831abea 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -60,9 +60,9 @@ //====================================================================== static double lexParseDouble(FileLine* fl, const char* textp, size_t length) { - string text = std::string(textp, length); + const string text = std::string{textp, length}; bool success = false; - double d = VString::parseDouble(text, &success); + const double d = VString::parseDouble(text, &success); if (!success) fl->v3error("Syntax error parsing real: '" << textp << "'"); return d; } @@ -87,12 +87,9 @@ static double lexParseDouble(FileLine* fl, const char* textp, size_t length) { ws [ \t\f\r]+ wsnr [ \t\f]+ crnl [\r]*[\n] - /* identifier */ id [a-zA-Z_][a-zA-Z0-9_$]* - /* escaped identifier */ escid \\[^ \t\f\r\n]+ word [a-zA-Z0-9_]+ - /* verilog numbers, constructed to not match the ' that begins a '( or '{ */ vnum1 [0-9]*?[''][sS]?[bcodhBCODH][ \t\n]*[A-Fa-f0-9xXzZ_?]* vnum2 [0-9]*?[''][sS]?[01xXzZ] vnum3 [0-9][_0-9]*[ \t\n]*[''][sS]?[bcodhBCODH]?[ \t\n]*[A-Fa-f0-9xXzZ_?]+ @@ -102,7 +99,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} %% -.|\n {BEGIN STATE_VERILOG_RECENT; yyless(0); } +.|\n { BEGIN STATE_VERILOG_RECENT; yyless(0); } /************************************************************************/ /* Verilator control files */ @@ -611,7 +608,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} /* Default PLI rule */ { - "$"[a-zA-Z_$][a-zA-Z0-9_$]* { string str (yytext, yyleng); + "$"[a-zA-Z_$][a-zA-Z0-9_$]* { const string str (yytext, yyleng); yylval.strp = PARSEP->newString(AstNode::encodeName(str)); FL; return yaD_PLI; } @@ -864,10 +861,10 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} /* Identifiers and numbers */ { {escid} { FL; yylval.strp = PARSEP->newString - (AstNode::encodeName(string(yytext+1))); // +1 to skip the backslash + (AstNode::encodeName(std::string{yytext+1})); // +1 to skip the backslash return yaID__LEX; } - {id} { FL; yylval.strp = PARSEP->newString(AstNode::encodeName(string(yytext))); + {id} { FL; yylval.strp = PARSEP->newString(AstNode::encodeName(std::string{yytext})); return yaID__LEX; } \"[^\"\\]*\" { FL; yylval.strp = PARSEP->newString(yytext+1, yyleng-2); @@ -878,10 +875,10 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} /* "# 1'b0" is a delay value so must lex as "#" "1" "'b0" */ if (PARSEP->lexPrevToken()=='#') { int shortlen = 0; - while (isdigit(yytext[shortlen])) shortlen++; + while (isdigit(yytext[shortlen])) ++shortlen; if (shortlen) { // Push rest past numbers for later parse - PARSEP->lexUnputString(yytext+shortlen, yyleng-shortlen); + PARSEP->lexUnputString(yytext + shortlen, yyleng - shortlen); // Return is stuff before the tick yyleng = shortlen; yytext[yyleng] = '\0'; diff --git a/src/verilog.y b/src/verilog.y index d5bb07096..2d56fa9a7 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -148,18 +148,6 @@ public: m_pinNum = m_pinStack.top(); m_pinStack.pop(); } - AstPackage* unitPackage(FileLine* fl) { - // Find one made earlier? - VSymEnt* symp = SYMP->symRootp()->findIdFlat(AstPackage::dollarUnitName()); - AstPackage* pkgp; - if (!symp) { - pkgp = PARSEP->rootp()->dollarUnitPkgAddp(); - SYMP->reinsert(pkgp, SYMP->symRootp()); // Don't push/pop scope as they're global - } else { - pkgp = VN_CAST(symp->nodep(), Package); - } - return pkgp; - } AstNodeDType* addRange(AstBasicDType* dtypep, AstNodeRange* rangesp, bool isPacked) { // If dtypep isn't basic, don't use this, call createArray() instead if (!rangesp) { @@ -1043,8 +1031,8 @@ description: // ==IEEE: description | interface_declaration { } | program_declaration { } | package_declaration { } - | package_item { if ($1) GRAMMARP->unitPackage($1->fileline())->addStmtp($1); } - | bind_directive { if ($1) GRAMMARP->unitPackage($1->fileline())->addStmtp($1); } + | package_item { if ($1) PARSEP->unitPackage($1->fileline())->addStmtp($1); } + | bind_directive { if ($1) PARSEP->unitPackage($1->fileline())->addStmtp($1); } // unsupported // IEEE: config_declaration // // Verilator only | yaT_RESETALL { } // Else, under design, and illegal based on IEEE 22.3 @@ -1457,10 +1445,12 @@ interface_item: // IEEE: interface_item + non_port_interface_item // // IEEE: generate_region | interface_generate_region { $$ = $1; } | interface_or_generate_item { $$ = $1; } - | program_declaration { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: program decls within interface decls"); } + | program_declaration + { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: program decls within interface decls"); } // // IEEE 1800-2017: modport_item // // See instead old 2012 position in interface_or_generate_item - | interface_declaration { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: interface decls within interface decls"); } + | interface_declaration + { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: interface decls within interface decls"); } | timeunits_declaration { $$ = $1; } // // See note in interface_or_generate item | module_common_item { $$ = $1; } @@ -1483,7 +1473,8 @@ interface_or_generate_item: // ==IEEE: interface_or_generate_item anonymous_program: // ==IEEE: anonymous_program // // See the spec - this doesn't change the scope, items still go up "top" - yPROGRAM ';' anonymous_program_itemListE yENDPROGRAM { BBUNSUP($1, "Unsupported: Anonymous programs"); $$ = nullptr; } + yPROGRAM ';' anonymous_program_itemListE yENDPROGRAM + { BBUNSUP($1, "Unsupported: Anonymous programs"); $$ = nullptr; } ; anonymous_program_itemListE: // IEEE: { anonymous_program_item } @@ -1565,9 +1556,12 @@ program_generate_item: // ==IEEE: program_generate_item ; extern_tf_declaration: // ==IEEE: extern_tf_declaration - yEXTERN task_prototype ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: extern task"); } - | yEXTERN function_prototype ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: extern function"); } - | yEXTERN yFORKJOIN task_prototype ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: extern forkjoin"); } + yEXTERN task_prototype ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: extern task"); } + | yEXTERN function_prototype ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: extern function"); } + | yEXTERN yFORKJOIN task_prototype ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: extern forkjoin"); } ; modport_declaration: // ==IEEE: modport_declaration @@ -1598,14 +1592,17 @@ modportPortsDecl: // // IEEE: modport_simple_ports_declaration port_direction modportSimplePort { $$ = new AstModportVarRef($2, *$2, GRAMMARP->m_varIO); } // // IEEE: modport_clocking_declaration - | yCLOCKING idAny/*clocking_identifier*/ { $$ = nullptr; BBUNSUP($1, "Unsupported: Modport clocking"); } + | yCLOCKING idAny/*clocking_identifier*/ + { $$ = nullptr; BBUNSUP($1, "Unsupported: Modport clocking"); } // // IEEE: yIMPORT modport_tf_port // // IEEE: yEXPORT modport_tf_port // // modport_tf_port expanded here | yIMPORT id/*tf_identifier*/ { $$ = new AstModportFTaskRef($2, *$2, false); } | yEXPORT id/*tf_identifier*/ { $$ = new AstModportFTaskRef($2, *$2, true); } - | yIMPORT method_prototype { $$ = nullptr; BBUNSUP($1, "Unsupported: Modport import with prototype"); } - | yEXPORT method_prototype { $$ = nullptr; BBUNSUP($1, "Unsupported: Modport export with prototype"); } + | yIMPORT method_prototype + { $$ = nullptr; BBUNSUP($1, "Unsupported: Modport import with prototype"); } + | yEXPORT method_prototype + { $$ = nullptr; BBUNSUP($1, "Unsupported: Modport export with prototype"); } // Continuations of above after a comma. // // IEEE: modport_simple_ports_declaration | modportSimplePort { $$ = new AstModportVarRef($1,*$1,GRAMMARP->m_varIO); } @@ -1875,9 +1872,11 @@ data_typeNoRef: // ==IEEE: data_type, excluding class_type etc referenc // // declarations which decode '.' modport themselves, so // // instead see data_typeVar | yVIRTUAL__INTERFACE yINTERFACE id/*interface*/ - { $$ = new AstBasicDType($1, AstBasicDTypeKwd::CHANDLE); BBUNSUP($1, "Unsupported: virtual interface"); } + { $$ = new AstBasicDType{$1, AstBasicDTypeKwd::CHANDLE}; + BBUNSUP($1, "Unsupported: virtual interface"); } | yVIRTUAL__anyID id/*interface*/ - { $$ = new AstBasicDType($1, AstBasicDTypeKwd::CHANDLE); BBUNSUP($1, "Unsupported: virtual data type"); } + { $$ = new AstBasicDType{$1, AstBasicDTypeKwd::CHANDLE}; + BBUNSUP($1, "Unsupported: virtual data type"); } | type_reference { $$ = $1; } // // IEEE: class_scope: see data_type above // // IEEE: class_type: see data_type above @@ -1952,7 +1951,8 @@ member_decl_assignment: // Derived from IEEE: variable_decl_assignment // // // IEEE: "[ covergroup_variable_identifier ] '=' class_new // // Pushed into variable_declExpr:class_new - | '=' class_new { nullptr; BBUNSUP($1, "Unsupported: member declaration assignment with new()"); } + | '=' class_new + { $$ = nullptr; BBUNSUP($1, "Unsupported: member declaration assignment with new()"); } ; list_of_variable_decl_assignments: // ==IEEE: list_of_variable_decl_assignments @@ -1975,7 +1975,8 @@ variable_decl_assignment: // ==IEEE: variable_decl_assignment // // // IEEE: "[ covergroup_variable_identifier ] '=' class_new // // Pushed into variable_declExpr:class_new - | '=' class_new { nullptr; BBUNSUP($1, "Unsupported: declaration assignment with new()"); } + | '=' class_new + { $$ = nullptr; BBUNSUP($1, "Unsupported: declaration assignment with new()"); } ; list_of_tf_variable_identifiers: // ==IEEE: list_of_tf_variable_identifiers @@ -2014,8 +2015,10 @@ variable_dimension: // ==IEEE: variable_dimension // // IEEE: associative_dimension (if data_type) // // Can't tell which until see if expr is data type or not | '[' exprOrDataType ']' { $$ = new AstBracketRange($1, $2); } - | yP_BRASTAR ']' { $$ = nullptr; BBUNSUP($1, "Unsupported: [*] wildcard associative arrays"); } - | '[' '*' ']' { $$ = nullptr; BBUNSUP($2, "Unsupported: [*] wildcard associative arrays"); } + | yP_BRASTAR ']' + { $$ = nullptr; BBUNSUP($1, "Unsupported: [*] wildcard associative arrays"); } + | '[' '*' ']' + { $$ = nullptr; BBUNSUP($2, "Unsupported: [*] wildcard associative arrays"); } // // IEEE: queue_dimension // // '[' '$' ']' -- $ is part of expr, see '[' constExpr ']' // // '[' '$' ':' expr ']' -- anyrange:expr:$ @@ -2287,9 +2290,12 @@ non_port_module_item: // ==IEEE: non_port_module_item | module_or_generate_item { $$ = $1; } | specify_block { $$ = $1; } | specparam_declaration { $$ = $1; } - | program_declaration { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: program decls within module decls"); } - | module_declaration { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: module decls within module decls"); } - | interface_declaration { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: interface decls within module decls"); } + | program_declaration + { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: program decls within module decls"); } + | module_declaration + { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: module decls within module decls"); } + | interface_declaration + { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: interface decls within module decls"); } | timeunits_declaration { $$ = $1; } // // Verilator specific | yaSCHDR { $$ = new AstScHdr($1,*$1); } @@ -2325,7 +2331,8 @@ module_common_item: // ==IEEE: module_common_item | bind_directive { $$ = $1; } | continuous_assign { $$ = $1; } // // IEEE: net_alias - | yALIAS variable_lvalue aliasEqList ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: alias statements"); } + | yALIAS variable_lvalue aliasEqList ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: alias statements"); } | initial_construct { $$ = $1; } | final_construct { $$ = $1; } // // IEEE: always_construct @@ -2358,7 +2365,8 @@ module_or_generate_item_declaration: // ==IEEE: module_or_generate_item_d package_or_generate_item_declaration { $$ = $1; } | genvar_declaration { $$ = $1; } | clocking_declaration { $$ = $1; } - | yDEFAULT yCLOCKING idAny/*new-clocking_identifier*/ ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: default clocking identifier"); } + | yDEFAULT yCLOCKING idAny/*new-clocking_identifier*/ ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: default clocking identifier"); } //UNSUP yDEFAULT yDISABLE yIFF expr/*expression_or_dist*/ ';' { } ; @@ -2691,7 +2699,8 @@ packed_dimensionList: // IEEE: { packed_dimension } packed_dimension: // ==IEEE: packed_dimension anyrange { $$ = $1; } - | '[' ']' { $$ = nullptr; BBUNSUP($1, "Unsupported: [] dimensions"); } + | '[' ']' + { $$ = nullptr; BBUNSUP($1, "Unsupported: [] dimensions"); } ; //************************************************ @@ -2735,8 +2744,7 @@ list_of_defparam_assignments: //== IEEE: list_of_defparam_assignments defparam_assignment: // ==IEEE: defparam_assignment idAny '.' idAny '=' expr { $$ = new AstDefParam($4, *$1, *$3, $5); } | idAny '.' idAny '.' - { $$ = nullptr; - BBUNSUP($4, "Unsupported: defparam with more than one dot"); } + { $$ = nullptr; BBUNSUP($4, "Unsupported: defparam with more than one dot"); } ; //************************************************ @@ -3080,9 +3088,12 @@ statement_item: // IEEE: statement_item // // IEEE: procedural_continuous_assignment | yASSIGN idClassSel '=' delayE expr ';' { $$ = new AstAssign($1,$2,$5); } //UNSUP: delay_or_event_controlE above - | yDEASSIGN variable_lvalue ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 deassign"); } - | yFORCE expr '=' expr ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 force"); } - | yRELEASE variable_lvalue ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 release"); } + | yDEASSIGN variable_lvalue ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 deassign"); } + | yFORCE expr '=' expr ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 force"); } + | yRELEASE variable_lvalue ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 release"); } // // // IEEE: case_statement | unique_priorityE caseStart caseAttrE case_itemListE yENDCASE { $$ = $2; if ($4) $2->addItemsp($4); @@ -3204,7 +3215,8 @@ statement_item: // IEEE: statement_item //UNSUP randsequence_statement { $$ = $1; } // // // IEEE: randcase_statement - | yRANDCASE case_itemList yENDCASE { $$ = nullptr; BBUNSUP($1, "Unsupported: SystemVerilog 2005 randcase statements"); } + | yRANDCASE case_itemList yENDCASE + { $$ = nullptr; BBUNSUP($1, "Unsupported: SystemVerilog 2005 randcase statements"); } // //UNSUP expect_property_statement { $$ = $1; } // @@ -3382,11 +3394,14 @@ caseCondList: // IEEE: part of case_item ; patternNoExpr: // IEEE: pattern **Excluding Expr* - '.' id/*variable*/ { $$ = nullptr; BBUNSUP($1, "Unsupported: '{} tagged patterns"); } - | yP_DOTSTAR { $$ = nullptr; BBUNSUP($1, "Unsupported: '{} tagged patterns"); } + '.' id/*variable*/ + { $$ = nullptr; BBUNSUP($1, "Unsupported: '{} tagged patterns"); } + | yP_DOTSTAR + { $$ = nullptr; BBUNSUP($1, "Unsupported: '{} tagged patterns"); } // // IEEE: "expr" excluded; expand in callers // // "yTAGGED id [expr]" Already part of expr - //UNSUP yTAGGED id/*member_identifier*/ patternNoExpr { $$ = nullptr; BBUNSUP($1, "Unsupported: '{} tagged patterns"); } + //UNSUP yTAGGED id/*member_identifier*/ patternNoExpr + //UNSUP { $$ = nullptr; BBUNSUP($1, "Unsupported: '{} tagged patterns"); } // // "yP_TICKBRA patternList '}'" part of expr under assignment_pattern ; @@ -3457,7 +3472,8 @@ for_initialization: // ==IEEE: for_initialization + for_variable_declarat for_initializationItemList: // IEEE: [for_variable_declaration...] for_initializationItem { $$ = $1; } - | for_initializationItemList ',' for_initializationItem { $$ = $1; BBUNSUP($2, "Unsupported: for loop initialization after the first comma"); } + | for_initializationItemList ',' for_initializationItem + { $$ = $1; BBUNSUP($2, "Unsupported: for loop initialization after the first comma"); } ; for_initializationItem: // IEEE: variable_assignment + for_variable_declaration @@ -3561,7 +3577,8 @@ function_subroutine_callNoMethod: // IEEE: function_subroutine_call (as f // // IEEE: randomize_call // // We implement randomize as a normal funcRef, since randomize isn't a keyword // // Note yNULL is already part of expressions, so they come for free - | funcRef yWITH__CUR constraint_block { $$ = $1; BBUNSUP($2, "Unsupported: randomize() 'with' constraint"); } + | funcRef yWITH__CUR constraint_block + { $$ = $1; BBUNSUP($2, "Unsupported: randomize() 'with' constraint"); } | funcRef yWITH__CUR '{' '}' { $$ = new AstWithParse($2, false, $1, nullptr); } ; @@ -4251,8 +4268,7 @@ expr: // IEEE: part of expression/constant_expression/primary // // IEEE: "... hierarchical_identifier select" see below // // // IEEE: empty_queue (IEEE 1800-2017 empty_unpacked_array_concatenation) - | '{' '}' { $$ = new AstConst($1, AstConst::BitFalse()); - BBUNSUP($1, "Unsupported: empty queues (\"{ }\")"); } + | '{' '}' { $$ = new AstEmptyQueue($1); } // // // IEEE: concatenation/constant_concatenation // // Part of exprOkLvalue below @@ -4272,7 +4288,8 @@ expr: // IEEE: part of expression/constant_expression/primary // // // IEEE: '(' mintypmax_expression ')' | ~noPar__IGNORE~'(' expr ')' { $$ = $2; } - | ~noPar__IGNORE~'(' expr ':' expr ':' expr ')' { $$ = $2; BBUNSUP($1, "Unsupported: min typ max expressions"); } + | ~noPar__IGNORE~'(' expr ':' expr ':' expr ')' + { $$ = $2; BBUNSUP($1, "Unsupported: min typ max expressions"); } // // PSL rule | '_' '(' expr ')' { $$ = $3; } // Arbitrary Verilog inside PSL // @@ -4761,7 +4778,7 @@ gateUnsupPinList: ; gatePinExpr: - expr { $$ = GRAMMARP ->createGatePin($1); } + expr { $$ = GRAMMARP->createGatePin($1); } ; // This list is also hardcoded in VParseLex.l @@ -6127,7 +6144,7 @@ dollarUnitNextId: // $unit // // if not needed must use packageClassScopeNoId // // Must call nextId without any additional tokens following yD_UNIT - { $$ = new AstClassOrPackageRef($1, "$unit", GRAMMARP->unitPackage($1), nullptr); + { $$ = new AstClassOrPackageRef{$1, "$unit", PARSEP->unitPackage($1), nullptr}; SYMP->nextId(PARSEP->rootp()); } ; @@ -6136,7 +6153,7 @@ localNextId: // local // // if not needed must use packageClassScopeNoId // // Must call nextId without any additional tokens following yLOCAL__COLONCOLON - { $$ = new AstClassOrPackageRef($1, "local::", GRAMMARP->unitPackage($1), nullptr); + { $$ = new AstClassOrPackageRef{$1, "local::", PARSEP->unitPackage($1), nullptr}; SYMP->nextId(PARSEP->rootp()); BBUNSUP($1, "Unsupported: Randomize 'local::'"); } ; @@ -6158,11 +6175,13 @@ class_item: // ==IEEE: class_item | class_method { $$ = $1; } | class_constraint { $$ = $1; } // - | class_declaration { $$ = nullptr; BBUNSUP($1, "Unsupported: class within class"); } + | class_declaration + { $$ = nullptr; BBUNSUP($1, "Unsupported: class within class"); } | timeunits_declaration { $$ = $1; } //UNSUP covergroup_declaration { $$ = $1; } // // local_parameter_declaration under parameter_declaration - | parameter_declaration ';' { $$ = $1; BBUNSUP($2, "Unsupported: class parameters"); } // 1800-2009 + | parameter_declaration ';' + { $$ = $1; BBUNSUP($2, "Unsupported: class parameters"); } // 1800-2009 | ';' { $$ = nullptr; } // | error ';' { $$ = nullptr; } @@ -6222,8 +6241,10 @@ class_constraint: // ==IEEE: class_constraint constraintStaticE yCONSTRAINT idAny constraint_block { $$ = nullptr; /*UNSUP*/ } // // IEEE: constraint_prototype + constraint_prototype_qualifier | constraintStaticE yCONSTRAINT idAny ';' { $$ = nullptr; } - | yEXTERN constraintStaticE yCONSTRAINT idAny ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: extern constraint"); } - | yPURE constraintStaticE yCONSTRAINT idAny ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: pure constraint"); } + | yEXTERN constraintStaticE yCONSTRAINT idAny ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: extern constraint"); } + | yPURE constraintStaticE yCONSTRAINT idAny ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: pure constraint"); } ; constraint_block: // ==IEEE: constraint_block @@ -6237,7 +6258,8 @@ constraint_block_itemList: // IEEE: { constraint_block_item } constraint_block_item: // ==IEEE: constraint_block_item constraint_expression { $$ = $1; } - | ySOLVE solve_before_list yBEFORE solve_before_list ';' { $$ = nullptr; BBUNSUP($2, "Unsupported: solve before"); } + | ySOLVE solve_before_list yBEFORE solve_before_list ';' + { $$ = nullptr; BBUNSUP($2, "Unsupported: solve before"); } ; solve_before_list: // ==IEEE: solve_before_list diff --git a/test_regress/driver.pl b/test_regress/driver.pl index 9c1a56408..240049350 100755 --- a/test_regress/driver.pl +++ b/test_regress/driver.pl @@ -2185,13 +2185,14 @@ sub files_identical { s/^-V\{t[0-9]+,[0-9]+\}/-V{t#,#}/; # --vlt vs --vltmt run differences $_; } @l1; - for (my $l=0; $l<=$#l1; ++$l) { + for (my $l = 0; $l <= $#l1; ++$l) { # Don't put control chars into our source repository $l1[$l] =~ s/\r/<#013>/mig; $l1[$l] =~ s/Command Failed[^\n]+/Command Failed/mig; $l1[$l] =~ s/Version: Verilator[^\n]+/Version: Verilator ###/mig; $l1[$l] =~ s/CPU Time: +[0-9.]+ seconds[^\n]+/CPU Time: ###/mig; $l1[$l] =~ s/\?v=[0-9.]+/?v=latest/mig; # warning URL + $l1[$l] =~ s/_h[0-9a-f]{8}_/_h########_/mg; if ($l1[$l] =~ s/Exiting due to.*/Exiting due to/mig) { splice @l1, $l+1; # Trunc rest last; @@ -2371,6 +2372,33 @@ sub tries { return 2; } +sub glob_all { + my $self = (ref $_[0]? shift : $Self); + my $pattern = shift; + + return glob($pattern); +} + +sub glob_one { + my $self = (ref $_[0]? shift : $Self); + my $pattern = shift; + return if $self->errors || $self->skips || $self->unsupporteds; + + my @files = glob($pattern); + my $n = scalar @files; + if ($n == 0) { + $self->error("glob_one: pattern '$pattern' does not match any files\n"); + } elsif ($n != 1) { + my $msg = "glob_one: pattern '$pattern' matches multiple files:\n"; + foreach my $file (@files) { + $msg .= $file."\n"; + } + $self->error($msg); + } else { + return $files[0]; + } +} + sub file_grep_not { my $self = (ref $_[0]? shift : $Self); my $filename = shift; @@ -2402,6 +2430,30 @@ sub file_grep { } } +sub file_grep_any { + my $self = $Self; + my @filenames = @{$_[0]}; shift; + my $regexp = shift; + my $expvalue = shift; + return if $self->errors || $self->skips || $self->unsupporteds; + + foreach my $filename (@filenames) { + my $contents = $self->file_contents($filename); + return if ($contents eq "_Already_Errored_"); + if ($contents =~ /$regexp/) { + if ($expvalue && $expvalue ne $1) { + $self->error("file_grep: $filename: Got='$1' Expected='$expvalue' in regexp: $regexp\n"); + } + return; + } + } + my $msg = "file_grep_any: Regexp '$regexp' not found in any of the following files:\n"; + foreach my $filename (@filenames) { + $msg .= $filename."\n"; + } + $self->error($msg); +} + my %_File_Contents_Cache; sub file_contents { diff --git a/test_regress/t/t_alw_noreorder.pl b/test_regress/t/t_alw_noreorder.pl index b0543df38..7d6eceb88 100755 --- a/test_regress/t/t_alw_noreorder.pl +++ b/test_regress/t/t_alw_noreorder.pl @@ -19,8 +19,9 @@ file_grep($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 0); # Here we should see some dly vars since reorder is disabled. # (Whereas our twin test, t_alw_reorder, should see no dly vars # since it enables the reorder step.) -file_grep("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/dly__t__DOT__v1/i); -file_grep("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/dly__t__DOT__v2/i); +my @files = glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root*.cpp"); +file_grep_any(\@files, qr/dly__t__DOT__v1/i); +file_grep_any(\@files, qr/dly__t__DOT__v2/i); execute( check_finished=>1, diff --git a/test_regress/t/t_alw_reorder.pl b/test_regress/t/t_alw_reorder.pl index 1335722ca..dce2f101f 100755 --- a/test_regress/t/t_alw_reorder.pl +++ b/test_regress/t/t_alw_reorder.pl @@ -18,8 +18,10 @@ file_grep($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 0); # Important: if reorder succeeded, we should see no dly vars. # Equally important: twin test t_alw_noreorder should see dly vars, # is identical to this test except for disabling the reorder step. -foreach my $file ("$Self->{obj_dir}/$Self->{VM_PREFIX}.cpp", - "$Self->{obj_dir}/$Self->{VM_PREFIX}.h") { +foreach my $file ( + glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.h"), + glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.cpp") + ) { file_grep_not($file, qr/dly__t__DOT__v1/i); file_grep_not($file, qr/dly__t__DOT__v2/i); file_grep_not($file, qr/dly__t__DOT__v3/i); diff --git a/test_regress/t/t_c_this.pl b/test_regress/t/t_c_this.pl index 37616efd7..b8edcffb4 100755 --- a/test_regress/t/t_c_this.pl +++ b/test_regress/t/t_c_this.pl @@ -15,7 +15,7 @@ compile(); if ($Self->{vlt_all}) { # The word 'this' (but only the whole word 'this' should have been replaced # in the contents. - my $file = "$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp"; + my $file = glob_one("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__DepSet_*__0.cpp"); my $text = file_contents($file); error("$file has 'this->clk'") if ($text =~ m/\bthis->clk\b/); error("$file does not have 'xthis'") if ($text !~ m/\bxthis\b/); diff --git a/test_regress/t/t_const_bitoptree_bug3096.cpp b/test_regress/t/t_const_bitoptree_bug3096.cpp new file mode 100644 index 000000000..27a3ca385 --- /dev/null +++ b/test_regress/t/t_const_bitoptree_bug3096.cpp @@ -0,0 +1,29 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 +#include + +#include + +int main(int argc, char* argv[]) { + Vt_const_bitoptree_bug3096* const tb = new Vt_const_bitoptree_bug3096; + + tb->instr_i = 0x08c0006f; + tb->eval(); + + std::cout << "tb->illegal_instr_o: " << static_cast(tb->illegal_instr_o) << std::endl + << std::flush; + assert(tb->illegal_instr_o == 0); + + delete tb; + return 0; +} diff --git a/test_regress/t/t_const_bitoptree_bug3096.pl b/test_regress/t/t_const_bitoptree_bug3096.pl new file mode 100755 index 000000000..16e215620 --- /dev/null +++ b/test_regress/t/t_const_bitoptree_bug3096.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2015 by Todd Strader. 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, + v_flags2 => ["--exe $Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute(); + +ok(1); +1; diff --git a/test_regress/t/t_const_bitoptree_bug3096.v b/test_regress/t/t_const_bitoptree_bug3096.v new file mode 100644 index 000000000..2f3e42f97 --- /dev/null +++ b/test_regress/t/t_const_bitoptree_bug3096.v @@ -0,0 +1,24 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. 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 + +// From issue #3096 + +module decoder( + input wire [31:0] instr_i, + // Making 'a' an output preserves it as a sub-expression and causes a missing clean + output wire a, + output wire illegal_instr_o + ); + /* verilator lint_off WIDTH */ + wire b = ! instr_i[12:5]; + wire c = ! instr_i[1:0]; + wire d = ! instr_i[15:13]; + /* verilator lint_on WIDTH */ + assign a = d ? b : 1'h1; + assign illegal_instr_o = c ? a : 1'h0; +endmodule diff --git a/test_regress/t/t_const_opt_cov.pl b/test_regress/t/t_const_opt_cov.pl index 0a72e4f4f..a052e9321 100755 --- a/test_regress/t/t_const_opt_cov.pl +++ b/test_regress/t/t_const_opt_cov.pl @@ -19,7 +19,7 @@ execute( ); if ($Self->{vlt}) { - file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 2); + file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 144); } ok(1); diff --git a/test_regress/t/t_const_opt_red.pl b/test_regress/t/t_const_opt_red.pl index d2a4c777b..666e5d478 100755 --- a/test_regress/t/t_const_opt_red.pl +++ b/test_regress/t/t_const_opt_red.pl @@ -19,7 +19,7 @@ execute( ); if ($Self->{vlt}) { - file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 149); + file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 158); } ok(1); diff --git a/test_regress/t/t_dist_error_format.pl b/test_regress/t/t_dist_error_format.pl index 8a30f1f7b..654a7c70f 100755 --- a/test_regress/t/t_dist_error_format.pl +++ b/test_regress/t/t_dist_error_format.pl @@ -40,6 +40,7 @@ sub formats { ++$lineno; $line =~ s/(\$display|\$write).*\".*%(Error|Warning)//; if ($line =~ /(Error|Warning)/ + && $line !~ /^\s* 1); - -my $root = ".."; - -### Must trim output before and after our file list -my %files = %{get_manifest_files($root)}; - -my $all_files = `cd $root && find . -type f -print`; -foreach my $file (split /\s+/,$all_files) { - next if $file eq ''; - $file =~ s!^\./!!; - $files{$file} |= 2; -} - -my %file_regexps; -my $skip = file_contents("$root/MANIFEST.SKIP"); -foreach my $file (sort keys %files) { - foreach my $skip (split /\s+/,$skip) { - if ($file =~ /$skip/) { - $files{$file} |= 4; - $file_regexps{$file} = $skip; - } - } -} - -# The repo may be a Git worktree -my $git_dir = `cd $root ; git rev-parse --git-common-dir`; -chomp $git_dir; -if (! -d $git_dir) { - $git_dir = ".git"; -} - -# Ignore files locally excluded -my $git_exclude = `cd $root && git ls-files --others --ignored --exclude-from $git_dir/info/exclude`; -foreach my $exclude (split /\s+/, $git_exclude) { - if (exists $files{$exclude}) { - $files{$exclude} |= 8; - } -} - -my %warns; -foreach my $file (sort keys %files) { - my $tar = $files{$file}&1; - my $dir = $files{$file}&2; - my $skip = $files{$file}&4; - my $exclude = $files{$file}&8; - - print +(($tar ? "TAR ":" ") - .($dir ? "DIR ":" ") - .($skip ? "SKIP ":" ") - .($exclude ? "EXCL ":" ") - ." $file\n") if $Self->{verbose}; - - if ($dir && !$tar && !$skip && !$exclude) { - $warns{$file} = "File not in manifest or MANIFEST.SKIP: $file"; - } elsif (!$dir && $tar && !$skip) { - $warns{$file} = "File in manifest, but not directory: $file"; - } elsif ($dir && $tar && $skip) { - $warns{$file} = "File in manifest and also MANIFEST.SKIP, too general skip regexp '$file_regexps{$file}'?: $file"; - } -} - -if (keys %warns) { - # First warning lists everything as that's shown in the driver summary - error("Files mismatch with manifest: ",join(' ',sort keys %warns)); - foreach my $file (sort keys %warns) { - error($warns{$file}); - } -} - -ok(1); -1; - -sub get_manifest_files { - my $root = shift; - `cd $root && $ENV{MAKE} dist-file-list`; - my $manifest_files = `cd $root && $ENV{MAKE} dist-file-list`; - $manifest_files =~ s!.*begin-dist-file-list:!!sg; - $manifest_files =~ s!end-dist-file-list:.*$!!sg; - print "MF $manifest_files\n" if $Self->{verbose}; - my %files; - foreach my $file (split /\s+/,$manifest_files) { - next if $file eq ''; - $files{$file} |= 1; - } - return \%files; -} diff --git a/test_regress/t/t_dist_whitespace.pl b/test_regress/t/t_dist_whitespace.pl index 4056e1053..bf7f6ca36 100755 --- a/test_regress/t/t_dist_whitespace.pl +++ b/test_regress/t/t_dist_whitespace.pl @@ -14,10 +14,11 @@ my $root = ".."; my $Debug; ### Must trim output before and after our file list -my %files = %{get_manifest_files($root)}; +my %files = %{get_source_files($root)}; foreach my $file (sort keys %files) { my $filename = "$root/$file"; + next if !-f $file; # git file might be deleted but not yet staged my $contents = file_contents($filename); if ($file =~ /\.out$/) { # Ignore golden files @@ -79,15 +80,12 @@ if (keys %warns) { ok(1); 1; -sub get_manifest_files { +sub get_source_files { my $root = shift; - `cd $root && $ENV{MAKE} dist-file-list`; - my $manifest_files = `cd $root && $ENV{MAKE} dist-file-list`; - $manifest_files =~ s!.*begin-dist-file-list:!!sg; - $manifest_files =~ s!end-dist-file-list:.*$!!sg; - print "MF $manifest_files\n" if $Self->{verbose}; + my $git_files = `cd $root && git ls-files`; + print "MF $git_files\n" if $Self->{verbose}; my %files; - foreach my $file (split /\s+/,$manifest_files) { + foreach my $file (split /\s+/, $git_files) { next if $file eq ''; $files{$file} |= 1; } diff --git a/test_regress/t/t_dpi_arg_inout_type__Dpi.out b/test_regress/t/t_dpi_arg_inout_type__Dpi.out index 01379ddcd..b800f7d8f 100644 --- a/test_regress/t/t_dpi_arg_inout_type__Dpi.out +++ b/test_regress/t/t_dpi_arg_inout_type__Dpi.out @@ -5,6 +5,9 @@ // Manually include this file where DPI .c import functions are declared to ensure // the C functions match the expectations of the DPI imports. +#ifndef VERILATED_VT_DPI_ARG_INOUT_TYPE__DPI_H_ +#define VERILATED_VT_DPI_ARG_INOUT_TYPE__DPI_H_ // guard + #include "svdpi.h" #ifdef __cplusplus @@ -152,3 +155,5 @@ extern "C" { #ifdef __cplusplus } #endif + +#endif // guard diff --git a/test_regress/t/t_dpi_arg_inout_unpack__Dpi.out b/test_regress/t/t_dpi_arg_inout_unpack__Dpi.out index e4f0d5949..8c6d9be71 100644 --- a/test_regress/t/t_dpi_arg_inout_unpack__Dpi.out +++ b/test_regress/t/t_dpi_arg_inout_unpack__Dpi.out @@ -5,6 +5,9 @@ // Manually include this file where DPI .c import functions are declared to ensure // the C functions match the expectations of the DPI imports. +#ifndef VERILATED_VT_DPI_ARG_INOUT_UNPACK__DPI_H_ +#define VERILATED_VT_DPI_ARG_INOUT_UNPACK__DPI_H_ // guard + #include "svdpi.h" #ifdef __cplusplus @@ -181,3 +184,5 @@ extern "C" { #ifdef __cplusplus } #endif + +#endif // guard diff --git a/test_regress/t/t_dpi_arg_input_type__Dpi.out b/test_regress/t/t_dpi_arg_input_type__Dpi.out index 632b44b9c..d0994c659 100644 --- a/test_regress/t/t_dpi_arg_input_type__Dpi.out +++ b/test_regress/t/t_dpi_arg_input_type__Dpi.out @@ -5,6 +5,9 @@ // Manually include this file where DPI .c import functions are declared to ensure // the C functions match the expectations of the DPI imports. +#ifndef VERILATED_VT_DPI_ARG_INPUT_TYPE__DPI_H_ +#define VERILATED_VT_DPI_ARG_INPUT_TYPE__DPI_H_ // guard + #include "svdpi.h" #ifdef __cplusplus @@ -152,3 +155,5 @@ extern "C" { #ifdef __cplusplus } #endif + +#endif // guard diff --git a/test_regress/t/t_dpi_arg_input_unpack__Dpi.out b/test_regress/t/t_dpi_arg_input_unpack__Dpi.out index 0867d13f0..4d1afa518 100644 --- a/test_regress/t/t_dpi_arg_input_unpack__Dpi.out +++ b/test_regress/t/t_dpi_arg_input_unpack__Dpi.out @@ -5,6 +5,9 @@ // Manually include this file where DPI .c import functions are declared to ensure // the C functions match the expectations of the DPI imports. +#ifndef VERILATED_VT_DPI_ARG_INPUT_UNPACK__DPI_H_ +#define VERILATED_VT_DPI_ARG_INPUT_UNPACK__DPI_H_ // guard + #include "svdpi.h" #ifdef __cplusplus @@ -181,3 +184,5 @@ extern "C" { #ifdef __cplusplus } #endif + +#endif // guard diff --git a/test_regress/t/t_dpi_arg_output_type__Dpi.out b/test_regress/t/t_dpi_arg_output_type__Dpi.out index 1a9db977f..e84de47c3 100644 --- a/test_regress/t/t_dpi_arg_output_type__Dpi.out +++ b/test_regress/t/t_dpi_arg_output_type__Dpi.out @@ -5,6 +5,9 @@ // Manually include this file where DPI .c import functions are declared to ensure // the C functions match the expectations of the DPI imports. +#ifndef VERILATED_VT_DPI_ARG_OUTPUT_TYPE__DPI_H_ +#define VERILATED_VT_DPI_ARG_OUTPUT_TYPE__DPI_H_ // guard + #include "svdpi.h" #ifdef __cplusplus @@ -152,3 +155,5 @@ extern "C" { #ifdef __cplusplus } #endif + +#endif // guard diff --git a/test_regress/t/t_dpi_arg_output_unpack__Dpi.out b/test_regress/t/t_dpi_arg_output_unpack__Dpi.out index 24675921e..ddcd704de 100644 --- a/test_regress/t/t_dpi_arg_output_unpack__Dpi.out +++ b/test_regress/t/t_dpi_arg_output_unpack__Dpi.out @@ -5,6 +5,9 @@ // Manually include this file where DPI .c import functions are declared to ensure // the C functions match the expectations of the DPI imports. +#ifndef VERILATED_VT_DPI_ARG_OUTPUT_UNPACK__DPI_H_ +#define VERILATED_VT_DPI_ARG_OUTPUT_UNPACK__DPI_H_ // guard + #include "svdpi.h" #ifdef __cplusplus @@ -181,3 +184,5 @@ extern "C" { #ifdef __cplusplus } #endif + +#endif // guard diff --git a/test_regress/t/t_dpi_qw.pl b/test_regress/t/t_dpi_qw.pl index 74e3bae58..f5c98cf48 100755 --- a/test_regress/t/t_dpi_qw.pl +++ b/test_regress/t/t_dpi_qw.pl @@ -12,7 +12,7 @@ scenarios(simulator => 1); compile( v_flags2 => ["t/t_dpi_qw_c.cpp"], - verilator_flags2 => ["-Wall -Wno-DECLFILENAME -no-l2name"], + verilator_flags2 => ["-Wall -Wno-DECLFILENAME -Wno-UNOPTFLAT -no-l2name"], ); execute( diff --git a/test_regress/t/t_dpi_result_type__Dpi.out b/test_regress/t/t_dpi_result_type__Dpi.out index cd2d0ecc8..344971158 100644 --- a/test_regress/t/t_dpi_result_type__Dpi.out +++ b/test_regress/t/t_dpi_result_type__Dpi.out @@ -5,6 +5,9 @@ // Manually include this file where DPI .c import functions are declared to ensure // the C functions match the expectations of the DPI imports. +#ifndef VERILATED_VT_DPI_RESULT_TYPE__DPI_H_ +#define VERILATED_VT_DPI_RESULT_TYPE__DPI_H_ // guard + #include "svdpi.h" #ifdef __cplusplus @@ -86,3 +89,5 @@ extern "C" { #ifdef __cplusplus } #endif + +#endif // guard diff --git a/test_regress/t/t_dpi_var.pl b/test_regress/t/t_dpi_var.pl index e7f19cd2a..7f3485f5a 100755 --- a/test_regress/t/t_dpi_var.pl +++ b/test_regress/t/t_dpi_var.pl @@ -18,10 +18,10 @@ compile( ); if ($Self->{vlt_all}) { - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); } execute( diff --git a/test_regress/t/t_dpi_var_vlt.pl b/test_regress/t/t_dpi_var_vlt.pl index a9d16c3c4..476882c77 100755 --- a/test_regress/t/t_dpi_var_vlt.pl +++ b/test_regress/t/t_dpi_var_vlt.pl @@ -20,10 +20,10 @@ compile( ); if ($Self->{vlt_all}) { - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); } execute( diff --git a/test_regress/t/t_flag_comp_limit_parens.pl b/test_regress/t/t_flag_comp_limit_parens.pl index 11731a873..51fb7f932 100755 --- a/test_regress/t/t_flag_comp_limit_parens.pl +++ b/test_regress/t/t_flag_comp_limit_parens.pl @@ -18,7 +18,7 @@ execute( check_finished => 1, ); -file_grep("$Self->{obj_dir}/Vt_flag_comp_limit_parens___024root__Slow.cpp", qr/Vdeeptemp/x); +file_grep(glob_one("$Self->{obj_dir}/Vt_flag_comp_limit_parens___024root__DepSet_*__0__Slow.cpp"), qr/Vdeeptemp/x); ok(1); 1; diff --git a/test_regress/t/t_math_msvc_64.v b/test_regress/t/t_flag_compiler.v similarity index 100% rename from test_regress/t/t_math_msvc_64.v rename to test_regress/t/t_flag_compiler.v diff --git a/test_regress/t/t_flag_compiler_clang.pl b/test_regress/t/t_flag_compiler_clang.pl new file mode 100755 index 000000000..edd331316 --- /dev/null +++ b/test_regress/t/t_flag_compiler_clang.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_flag_compiler.v"); + +compile( + verilator_flags2 => ["--compiler clang"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_flag_compiler_gcc.pl b/test_regress/t/t_flag_compiler_gcc.pl new file mode 100755 index 000000000..faaedc67a --- /dev/null +++ b/test_regress/t/t_flag_compiler_gcc.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_flag_compiler.v"); + +compile( + verilator_flags2 => ["--compiler gcc"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_msvc_64.pl b/test_regress/t/t_flag_compiler_msvc.pl similarity index 94% rename from test_regress/t/t_math_msvc_64.pl rename to test_regress/t/t_flag_compiler_msvc.pl index 2769c27e2..01c70152c 100755 --- a/test_regress/t/t_math_msvc_64.pl +++ b/test_regress/t/t_flag_compiler_msvc.pl @@ -10,6 +10,8 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); +top_filename("t/t_flag_compiler.v"); + compile( verilator_flags2 => ["--compiler msvc"], # Bug requires msvc ); diff --git a/test_regress/t/t_flag_csplit_off.pl b/test_regress/t/t_flag_csplit_off.pl index d29b8f2ef..8e3014653 100755 --- a/test_regress/t/t_flag_csplit_off.pl +++ b/test_regress/t/t_flag_csplit_off.pl @@ -61,7 +61,7 @@ while (1) { sub check_no_splits { foreach my $file (glob("$Self->{obj_dir}/*.cpp")) { $file =~ s/__024root//; - if ($file =~ qr/__\d/) { + if ($file =~ qr/__[1-9]/) { error("Split file found: $file"); } } diff --git a/test_regress/t/t_flag_instr_count_dpi_bad.pl b/test_regress/t/t_flag_instr_count_dpi_bad.pl new file mode 100755 index 000000000..33b76e374 --- /dev/null +++ b/test_regress/t/t_flag_instr_count_dpi_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); + +compile( + verilator_flags2 => ["--instr-count-dpi -1"], + fails => 1, + expect => "%Error: --instr-count-dpi must be non-negative: -1" + ); + +ok(1); +1; diff --git a/test_regress/t/t_flag_j_bad.out b/test_regress/t/t_flag_j_bad.out new file mode 100644 index 000000000..2fe652c5d --- /dev/null +++ b/test_regress/t/t_flag_j_bad.out @@ -0,0 +1,2 @@ +%Error: -j accepts positive integer, but '0' is passed +%Error: Exiting due to diff --git a/test_regress/t/t_flag_j_bad.pl b/test_regress/t/t_flag_j_bad.pl new file mode 100755 index 000000000..ee3d0fdc8 --- /dev/null +++ b/test_regress/t/t_flag_j_bad.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt => 1); + +top_filename("t/t_flag_werror.v"); + +lint( + fails => 1, + verilator_flags => [qw(-j 0 --build)], + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_flag_make_bad.out b/test_regress/t/t_flag_make_bad.out new file mode 100644 index 000000000..d3a472dd3 --- /dev/null +++ b/test_regress/t/t_flag_make_bad.out @@ -0,0 +1 @@ +%Error: Unknown --make system specified: 'bad_one' diff --git a/test_regress/t/t_flag_make_bad.pl b/test_regress/t/t_flag_make_bad.pl new file mode 100755 index 000000000..1c6f23aec --- /dev/null +++ b/test_regress/t/t_flag_make_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 => ["--make bad_one"], + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_flag_make_gmake.pl b/test_regress/t/t_flag_make_gmake.pl new file mode 100755 index 000000000..e5caca9d8 --- /dev/null +++ b/test_regress/t/t_flag_make_gmake.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 2008 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_flag_make_cmake.v"); + +compile( + verilator_flags2 => ['--make gmake'], + ); + +ok(1); +1; diff --git a/test_regress/t/t_flag_parameter.v b/test_regress/t/t_flag_parameter.v index 33f1c102e..4fe3ad8ee 100644 --- a/test_regress/t/t_flag_parameter.v +++ b/test_regress/t/t_flag_parameter.v @@ -53,6 +53,8 @@ module t; parameter int52 = 1; parameter int61 = 1; parameter int62 = 1; + parameter int71 = 1; + parameter int72 = 1; initial begin `check(string1,"New String"); @@ -83,6 +85,11 @@ module t; `check(int52,32'hdeadbeef); `check(int61,32'hdeadbeef); `check(int62,32'hdeadbeef); + `check(int71,-1000); + `check(int72,-1000); + + // Check parameter assigned simple integer literal is signed + if ((int11 << 27) >>> 31 != -1) $stop; $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_flag_parameter.vc b/test_regress/t/t_flag_parameter.vc index 955540440..35687e39e 100644 --- a/test_regress/t/t_flag_parameter.vc +++ b/test_regress/t/t_flag_parameter.vc @@ -26,3 +26,5 @@ -pvalue+int52=32\'hdead_beef -Gint61="32'hdead_beef" -pvalue+int62="32'hdead_beef" +-Gint71=-1000 +-pvalue+int72=-1000 diff --git a/test_regress/t/t_flag_xinitial_unique.pl b/test_regress/t/t_flag_xinitial_unique.pl index 572880d86..f2451bd3f 100755 --- a/test_regress/t/t_flag_xinitial_unique.pl +++ b/test_regress/t/t_flag_xinitial_unique.pl @@ -18,7 +18,7 @@ execute( check_finished => 1, ); -file_grep("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__Slow.cpp", qr/VL_RAND_RESET/); +file_grep(glob_one("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__DepSet_*__0__Slow.cpp"), qr/VL_RAND_RESET/); ok(1); 1; diff --git a/test_regress/t/t_flag_xinitial_unique.v b/test_regress/t/t_flag_xinitial_unique.v index 04321788a..aff70e92c 100644 --- a/test_regress/t/t_flag_xinitial_unique.v +++ b/test_regress/t/t_flag_xinitial_unique.v @@ -6,10 +6,13 @@ module t (/*AUTOARG*/ // Outputs - value + value, value2 ); output reg [63:0] value; + output wire [64:0] value2; + + assign value2 = {8'bx, 57'h12}; initial begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_foreach.pl b/test_regress/t/t_foreach.pl index 06fc908f0..5c5b93dd0 100755 --- a/test_regress/t/t_foreach.pl +++ b/test_regress/t/t_foreach.pl @@ -20,14 +20,16 @@ execute( # We expect all loops should be unrolled by verilator, # none of the loop variables should exist in the output: -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/index_/); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__Slow.cpp", qr/index_/); +for my $file (glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.cpp")) { + file_grep_not($file, qr/index_/); +} # Further, we expect that all logic within the loop should # have been evaluated inside the compiler. So there should be # no references to 'sum' in the .cpp. -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/sum/); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__Slow.cpp", qr/sum/); +for my $file (glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.cpp")) { + file_grep_not($file, qr/sum/); +} ok(1); 1; diff --git a/test_regress/t/t_func_tasknsvar_bad.out b/test_regress/t/t_func_tasknsvar_bad.out index b2338cb62..723df5dc4 100644 --- a/test_regress/t/t_func_tasknsvar_bad.out +++ b/test_regress/t/t_func_tasknsvar_bad.out @@ -2,7 +2,7 @@ 16 | foo(bus_we_select_from[2]); | ^ ... For error description see https://verilator.org/warn/TASKNSVAR?v=latest -%Error: Internal Error: t/t_func_tasknsvar_bad.v:10:7: ../V3Broken.cpp:#: Broken link in node (or something without maybePointedTo): m_varScopep && !m_varScopep->brokeExists() +%Error: Internal Error: t/t_func_tasknsvar_bad.v:10:7: ../V3Broken.cpp:#: Broken link in node (or something without maybePointedTo): 'm_varScopep && !m_varScopep->brokeExists()' @ ../V3AstNodes.cpp:51 10 | sig = '1; | ^~~ ... See the manual at https://verilator.org/verilator_doc.html for more assistance. diff --git a/test_regress/t/t_gate_basic.v b/test_regress/t/t_gate_basic.v index 8f120463a..9a3e7fbab 100644 --- a/test_regress/t/t_gate_basic.v +++ b/test_regress/t/t_gate_basic.v @@ -16,8 +16,8 @@ module t (/*AUTOARG*/ reg [31:0] b; wire [2:0] bf; buf BF0 (bf[0], a[0]), - BF1 (bf[1], a[1]), - BF2 (bf[2], a[2]); + BF1 (bf[1], a[1]), + BF2 (bf[2], a[2]); // verilator lint_off IMPLICIT not #(0.108) NT0 (nt0, a[0]); @@ -48,43 +48,66 @@ module t (/*AUTOARG*/ // path delays (A1 *> Q) = (a$A1$Y, a$A1$Y); (A0 *> Q) = (b$A0$Y, a$A0$Z); + + if (C1) (IN => OUT) = (1,1); + ifnone (IN => OUT) = (2,2); + + showcancelled Q; + noshowcancelled Q; + pulsestyle_ondetect Q; + pulsestyle_onevent Q; + + // other unimplemented stuff + $fullskew(); + $hold(); + $nochange(); + $period(); + $recovery(); + $recrem(); + $removal(); + $setup(); + $setuphold(); + $skew(); + $timeskew(); + $width(); + endspecify `endif always @ (posedge clk) begin if (cyc!=0) begin - cyc <= cyc + 1; - if (cyc==1) begin - a <= 32'h18f6b034; - b <= 32'h834bf892; - end - if (cyc==2) begin - a <= 32'h529ab56f; - b <= 32'h7835a237; - if (bf !== 3'b100) $stop; - if (nt0 !== 1'b1) $stop; - if (an0 !== 1'b0) $stop; - if (nd0 !== 1'b1) $stop; - if (or0 !== 1'b0) $stop; - if (nr0 !== 1'b1) $stop; - if (xo0 !== 1'b0) $stop; - if (xn0 !== 1'b1) $stop; - if (ba != 32'h18f6b034) $stop; - end - if (cyc==3) begin - if (bf !== 3'b111) $stop; - if (nt0 !== 1'b0) $stop; - if (an0 !== 1'b1) $stop; - if (nd0 !== 1'b0) $stop; - if (or0 !== 1'b1) $stop; - if (nr0 !== 1'b0) $stop; - if (xo0 !== 1'b0) $stop; - if (xn0 !== 1'b0) $stop; - end - if (cyc==4) begin - $write("*-* All Finished *-*\n"); - $finish; - end + cyc <= cyc + 1; + if (cyc==1) begin + a <= 32'h18f6b034; + b <= 32'h834bf892; + end + if (cyc==2) begin + a <= 32'h529ab56f; + b <= 32'h7835a237; + if (bf !== 3'b100) $stop; + if (nt0 !== 1'b1) $stop; + if (an0 !== 1'b0) $stop; + if (nd0 !== 1'b1) $stop; + if (or0 !== 1'b0) $stop; + if (nr0 !== 1'b1) $stop; + if (xo0 !== 1'b0) $stop; + if (xn0 !== 1'b1) $stop; + if (ba != 32'h18f6b034) $stop; + end + if (cyc==3) begin + if (bf !== 3'b111) $stop; + if (nt0 !== 1'b0) $stop; + if (an0 !== 1'b1) $stop; + if (nd0 !== 1'b0) $stop; + if (or0 !== 1'b1) $stop; + if (nr0 !== 1'b0) $stop; + if (xo0 !== 1'b0) $stop; + if (xn0 !== 1'b0) $stop; + end + if (cyc==4) begin + $write("*-* All Finished *-*\n"); + $finish; + end end end diff --git a/test_regress/t/t_gate_ormux.pl b/test_regress/t/t_gate_ormux.pl index 996e05170..e0cf23b95 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, 1058); } execute( diff --git a/test_regress/t/t_gate_strength.pl b/test_regress/t/t_gate_strength.pl new file mode 100755 index 000000000..dd8b670e0 --- /dev/null +++ b/test_regress/t/t_gate_strength.pl @@ -0,0 +1,18 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2004 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + verilator_flags2 => ['-bbox-unsup'], + ); + +ok(1); +1; diff --git a/test_regress/t/t_gate_strength.v b/test_regress/t/t_gate_strength.v new file mode 100644 index 000000000..038ca9cfb --- /dev/null +++ b/test_regress/t/t_gate_strength.v @@ -0,0 +1,75 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2004 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + logic [31:0] a; + + // verilator lint_off IMPLICIT + assign (highz0, supply1) nt00 = a[0]; + assign (supply0, highz1) nt01 = a[0]; + assign (supply0, supply1) nt02 = a[0]; + assign (strong0, strong1) nt03 = a[0]; + assign (pull0, pull1) nt04 = a[0]; + assign (weak0, weak1) nt05 = a[0]; + + assign (highz0, supply1) nt10 = a[0]; + assign (supply0, highz1) nt11 = a[0]; + assign (supply0, supply1) nt12 = a[0]; + assign (strong0, strong1) nt13 = a[0]; + assign (pull0, pull1) nt14 = a[0]; + assign (weak0, weak1) nt15 = a[0]; + // verilator lint_on IMPLICIT + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + a <= 32'h18f6b030; + end + if (cyc==2) begin + a <= 32'h18f6b03f; + if (nt00 !== 1'b0) $stop; + if (nt01 !== 1'b0) $stop; + if (nt02 !== 1'b0) $stop; + if (nt03 !== 1'b0) $stop; + if (nt04 !== 1'b0) $stop; + if (nt05 !== 1'b0) $stop; + if (nt10 !== 1'b0) $stop; + if (nt11 !== 1'b0) $stop; + if (nt12 !== 1'b0) $stop; + if (nt13 !== 1'b0) $stop; + if (nt14 !== 1'b0) $stop; + if (nt15 !== 1'b0) $stop; + end + if (cyc==3) begin + if (nt00 !== 1'b1) $stop; + if (nt01 !== 1'b1) $stop; + if (nt02 !== 1'b1) $stop; + if (nt03 !== 1'b1) $stop; + if (nt04 !== 1'b1) $stop; + if (nt05 !== 1'b1) $stop; + if (nt10 !== 1'b1) $stop; + if (nt11 !== 1'b1) $stop; + if (nt12 !== 1'b1) $stop; + if (nt13 !== 1'b1) $stop; + if (nt14 !== 1'b1) $stop; + if (nt15 !== 1'b1) $stop; + end + if (cyc==4) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule diff --git a/test_regress/t/t_inst_tree_inl0_pub1.pl b/test_regress/t/t_inst_tree_inl0_pub1.pl index 3bf53b1f1..effe5ebbe 100755 --- a/test_regress/t/t_inst_tree_inl0_pub1.pl +++ b/test_regress/t/t_inst_tree_inl0_pub1.pl @@ -23,17 +23,18 @@ sub checkRelativeRefs { my ($mod, $expect_relative) = @_; my $found_relative = 0; - my $file = "$Self->{obj_dir}/V$Self->{name}_${mod}.cpp"; - my $text = file_contents($file); + foreach my $file (glob_all("$Self->{obj_dir}/V$Self->{name}_${mod}*.cpp")) { + my $text = file_contents($file); - if ($text =~ m/this->/ || $text =~ m/vlSelf->/) { - $found_relative = 1; - } + if ($text =~ m/this->/ || $text =~ m/vlSelf->/) { + $found_relative = 1; + } - if ($found_relative != $expect_relative) { - error("$file " . - ($found_relative ? "has" : "does not have") . - " relative variable references."); + if ($found_relative != $expect_relative) { + error("$file " . + ($found_relative ? "has" : "does not have") . + " relative variable references."); + } } } diff --git a/test_regress/t/t_inst_tree_inl1_pub0.pl b/test_regress/t/t_inst_tree_inl1_pub0.pl index ec48a4e06..9a8a6aa92 100755 --- a/test_regress/t/t_inst_tree_inl1_pub0.pl +++ b/test_regress/t/t_inst_tree_inl1_pub0.pl @@ -18,9 +18,9 @@ compile( ); if ($Self->{vlt_all}) { - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); } execute( diff --git a/test_regress/t/t_lint_const_func_dpi_bad.out b/test_regress/t/t_lint_const_func_dpi_bad.out new file mode 100644 index 000000000..6ac7d0ab6 --- /dev/null +++ b/test_regress/t/t_lint_const_func_dpi_bad.out @@ -0,0 +1,11 @@ +%Error: t/t_lint_const_func_dpi_bad.v:8:32: Constant function may not be DPI import (IEEE 1800-2017 13.4.3) + : ... In instance t + 8 | import "DPI-C" function int dpiFunc(); + | ^~~~~~~ +%Error: t/t_lint_const_func_dpi_bad.v:9:23: Expecting expression to be constant, but can't determine constant for FUNCREF 'dpiFunc' + : ... In instance t + t/t_lint_const_func_dpi_bad.v:8:32: ... Location of non-constant FUNC 'dpiFunc': DPI import functions aren't simulatable + t/t_lint_const_func_dpi_bad.v:9:23: ... Called from dpiFunc() with parameters: + 9 | localparam PARAM = dpiFunc(); + | ^~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_param_in_func_bad.pl b/test_regress/t/t_lint_const_func_dpi_bad.pl similarity index 84% rename from test_regress/t/t_param_in_func_bad.pl rename to test_regress/t/t_lint_const_func_dpi_bad.pl index 27159da5b..b5861b2ab 100755 --- a/test_regress/t/t_param_in_func_bad.pl +++ b/test_regress/t/t_lint_const_func_dpi_bad.pl @@ -2,13 +2,13 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2019 by Wilson Snyder. This program is free software; you +# Copyright 2008 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); +scenarios(vlt => 1); lint( fails => 1, diff --git a/test_regress/t/t_lint_const_func_dpi_bad.v b/test_regress/t/t_lint_const_func_dpi_bad.v new file mode 100644 index 000000000..84632841b --- /dev/null +++ b/test_regress/t/t_lint_const_func_dpi_bad.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, 2021 by Donald Owen. +// SPDX-License-Identifier: CC0-1.0 + +module t (); + import "DPI-C" function int dpiFunc(); + localparam PARAM = dpiFunc(); +endmodule diff --git a/test_regress/t/t_lint_const_func_gen_bad.out b/test_regress/t/t_lint_const_func_gen_bad.out new file mode 100644 index 000000000..61bd870a8 --- /dev/null +++ b/test_regress/t/t_lint_const_func_gen_bad.out @@ -0,0 +1,11 @@ +%Error: t/t_lint_const_func_gen_bad.v:11:30: Constant function may not be declared under generate (IEEE 1800-2017 13.4.3) + : ... In instance t + 11 | function automatic bit constFunc(); + | ^~~~~~~~~ +%Error: t/t_lint_const_func_gen_bad.v:15:26: Expecting expression to be constant, but can't determine constant for FUNCREF 'constFunc' + : ... In instance t + t/t_lint_const_func_gen_bad.v:11:30: ... Location of non-constant FUNC 'constFunc': Constant function called under generate + t/t_lint_const_func_gen_bad.v:15:26: ... Called from constFunc() with parameters: + 15 | localparam PARAM = constFunc(); + | ^~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_lint_const_func_gen_bad.pl b/test_regress/t/t_lint_const_func_gen_bad.pl new file mode 100755 index 000000000..b5861b2ab --- /dev/null +++ b/test_regress/t/t_lint_const_func_gen_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 2008 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_lint_const_func_gen_bad.v b/test_regress/t/t_lint_const_func_gen_bad.v new file mode 100644 index 000000000..d2ac25a4d --- /dev/null +++ b/test_regress/t/t_lint_const_func_gen_bad.v @@ -0,0 +1,17 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2021 by Donald Owen. +// SPDX-License-Identifier: CC0-1.0 + +module t (); + if (1) begin: GenConstFunc + // IEEE 1800-2017 13.4.3, constant functions shall not be declared inside a + //generate block + function automatic bit constFunc(); + constFunc = 1'b1; + endfunction + + localparam PARAM = constFunc(); + end +endmodule diff --git a/test_regress/t/t_optm_if_array.pl b/test_regress/t/t_optm_if_array.pl index 2b2ca158f..aa9716f06 100755 --- a/test_regress/t/t_optm_if_array.pl +++ b/test_regress/t/t_optm_if_array.pl @@ -17,8 +17,9 @@ execute( check_finished => 1, ); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/rstn_r/); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__Slow.cpp", qr/rstn_r/); +for my $file (glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root*.cpp")) { + file_grep_not($file, qr/rstn_r/); +} ok(1); 1; diff --git a/test_regress/t/t_optm_if_cond.pl b/test_regress/t/t_optm_if_cond.pl index 3c5833594..b67f09305 100755 --- a/test_regress/t/t_optm_if_cond.pl +++ b/test_regress/t/t_optm_if_cond.pl @@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); compile( - verilator_flags2 => ['--stats'], + verilator_flags2 => ['--stats', "-Ow"], ); if ($Self->{vlt_all}) { diff --git a/test_regress/t/t_optm_redor.pl b/test_regress/t/t_optm_redor.pl index 2b2ca158f..aa9716f06 100755 --- a/test_regress/t/t_optm_redor.pl +++ b/test_regress/t/t_optm_redor.pl @@ -17,8 +17,9 @@ execute( check_finished => 1, ); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/rstn_r/); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__Slow.cpp", qr/rstn_r/); +for my $file (glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root*.cpp")) { + file_grep_not($file, qr/rstn_r/); +} ok(1); 1; diff --git a/test_regress/t/t_order_dpi_export_1.cpp b/test_regress/t/t_order_dpi_export_1.cpp new file mode 100644 index 000000000..1dcec5c1e --- /dev/null +++ b/test_regress/t/t_order_dpi_export_1.cpp @@ -0,0 +1,37 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 + +#include +#include + +int main(int argc, char* argv[]) { + Vt_order_dpi_export_1* const tb = new Vt_order_dpi_export_1; + tb->contextp()->commandArgs(argc, argv); + bool clk = true; + + while (!tb->contextp()->gotFinish()) { + // Timeout + if (tb->contextp()->time() > 100000) break; + // Toggle and set clock + svSetScope(svGetScopeFromName("TOP.testbench")); + clk = !clk; + set_clk(clk); + // Eval + tb->eval(); + // Advance time + tb->contextp()->timeInc(500); + } + + delete tb; + return 0; +} diff --git a/test_regress/t/t_order_dpi_export_1.pl b/test_regress/t/t_order_dpi_export_1.pl new file mode 100755 index 000000000..21b3c76b6 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_1.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(vlt_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_dpi_export_1.v b/test_regress/t/t_order_dpi_export_1.v new file mode 100644 index 000000000..e71069e06 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_1.v @@ -0,0 +1,36 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +module testbench; + + logic clk; + + export "DPI-C" function set_clk; + function void set_clk(bit val); + clk = val; + endfunction; + + // Downstream signal dependent on clk demonstrates scheduling issue. + // The '$c("1") &' simply ensures that dependent_clk does not get + // replaced with clk early and hence hiding the issue + wire dependent_clk = $c1("1") & clk; + + int n = 0; + + always @(posedge dependent_clk) begin + $display("t=%t n=%d", $time, n); + if ($time != (2*n+1) * 500) $stop; + if (n == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + n += 1; + end + +endmodule + diff --git a/test_regress/t/t_order_dpi_export_2.cpp b/test_regress/t/t_order_dpi_export_2.cpp new file mode 100644 index 000000000..3ba47b0d3 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_2.cpp @@ -0,0 +1,38 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 + +#include +#include + +void toggle_other_clk(svBit val) { set_other_clk(val); } + +int main(int argc, char* argv[]) { + Vt_order_dpi_export_2* const tb = new Vt_order_dpi_export_2; + tb->contextp()->commandArgs(argc, argv); + bool clk = true; + + while (!tb->contextp()->gotFinish()) { + // Timeout + if (tb->contextp()->time() > 100000) break; + // Toggle and set main clock + clk = !clk; + tb->clk = clk; + // Eval + tb->eval(); + // Advance time + tb->contextp()->timeInc(500); + } + + delete tb; + return 0; +} diff --git a/test_regress/t/t_order_dpi_export_2.pl b/test_regress/t/t_order_dpi_export_2.pl new file mode 100755 index 000000000..21b3c76b6 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_2.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(vlt_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_dpi_export_2.v b/test_regress/t/t_order_dpi_export_2.v new file mode 100644 index 000000000..a5c1f40a1 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_2.v @@ -0,0 +1,43 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +module testbench( + /*AUTOARG*/ + // Inputs + clk + ); + + input clk; // Top level input clock + logic other_clk; // Dependent clock set via DPI + + export "DPI-C" function set_other_clk; + function void set_other_clk(bit val); + other_clk = val; + endfunction; + + bit even_other = 1; + import "DPI-C" context function void toggle_other_clk(bit val); + always @(posedge clk) begin + even_other <= ~even_other; + toggle_other_clk(even_other); + end + + int n = 0; + + always @(posedge other_clk) begin + $display("t=%t n=%d", $time, n); + if ($time != (4*n+1) * 500) $stop; + if (n == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + n += 1; + end + +endmodule + diff --git a/test_regress/t/t_order_dpi_export_3.cpp b/test_regress/t/t_order_dpi_export_3.cpp new file mode 100644 index 000000000..2855845c1 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_3.cpp @@ -0,0 +1,40 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 + +#include +#include + +void toggle_other_clk(svBit val) { set_other_clk(val); } + +void toggle_third_clk(svBit val) { set_third_clk(val); } + +int main(int argc, char* argv[]) { + Vt_order_dpi_export_3* const tb = new Vt_order_dpi_export_3; + tb->contextp()->commandArgs(argc, argv); + bool clk = true; + + while (!tb->contextp()->gotFinish()) { + // Timeout + if (tb->contextp()->time() > 100000) break; + // Toggle and set main clock + clk = !clk; + tb->clk = clk; + // Eval + tb->eval(); + // Advance time + tb->contextp()->timeInc(500); + } + + delete tb; + return 0; +} diff --git a/test_regress/t/t_order_dpi_export_3.pl b/test_regress/t/t_order_dpi_export_3.pl new file mode 100755 index 000000000..21b3c76b6 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_3.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(vlt_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_dpi_export_3.v b/test_regress/t/t_order_dpi_export_3.v new file mode 100644 index 000000000..8083bae97 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_3.v @@ -0,0 +1,56 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +module testbench( + /*AUTOARG*/ + // Inputs + clk + ); + + input clk; // Top level input clock + logic other_clk; // Dependent clock set via DPI + logic third_clk; // Additional dependent clock set via DPI + + export "DPI-C" function set_other_clk; + function void set_other_clk(bit val); + other_clk = val; + endfunction; + + export "DPI-C" function set_third_clk; + function void set_third_clk(bit val); + third_clk = val; + endfunction; + + bit even_other = 1; + import "DPI-C" context function void toggle_other_clk(bit val); + always @(posedge clk) begin + even_other <= ~even_other; + toggle_other_clk(even_other); + end + + bit even_third = 1; + import "DPI-C" context function void toggle_third_clk(bit val); + always @(posedge other_clk) begin + even_third <= ~even_third; + toggle_third_clk(even_third); + end + + int n = 0; + + always @(posedge third_clk) begin + $display("t=%d n=%d", $time, n); + if ($time != (8*n+1) * 500) $stop; + if (n == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + n += 1; + end + +endmodule + diff --git a/test_regress/t/t_order_dpi_export_4.cpp b/test_regress/t/t_order_dpi_export_4.cpp new file mode 100644 index 000000000..e4ecd5c89 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_4.cpp @@ -0,0 +1,40 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 + +#include +#include + +void toggle_other_clk(svBit val) { set_other_clk(val); } + +void toggle_third_clk(svBit val) { set_third_clk(val); } + +int main(int argc, char* argv[]) { + Vt_order_dpi_export_4* const tb = new Vt_order_dpi_export_4; + tb->contextp()->commandArgs(argc, argv); + bool clk = true; + + while (!tb->contextp()->gotFinish()) { + // Timeout + if (tb->contextp()->time() > 100000) break; + // Toggle and set main clock + clk = !clk; + tb->clk = clk; + // Eval + tb->eval(); + // Advance time + tb->contextp()->timeInc(500); + } + + delete tb; + return 0; +} diff --git a/test_regress/t/t_order_dpi_export_4.pl b/test_regress/t/t_order_dpi_export_4.pl new file mode 100755 index 000000000..21b3c76b6 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_4.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(vlt_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_dpi_export_4.v b/test_regress/t/t_order_dpi_export_4.v new file mode 100644 index 000000000..73600c1c1 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_4.v @@ -0,0 +1,58 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +module testbench( + /*AUTOARG*/ + // Inputs + clk + ); + + input clk; // Top level input clock + logic other_clk; // Dependent clock set via DPI + logic third_clk; // Additional dependent clock set via DPI + + export "DPI-C" function set_other_clk; + function void set_other_clk(bit val); + other_clk = val; + endfunction; + + export "DPI-C" function set_third_clk; + function void set_third_clk(bit val); + third_clk = val; + endfunction; + + bit even_other = 1; + import "DPI-C" context function void toggle_other_clk(bit val); + always @(posedge clk) begin + even_other <= ~even_other; + toggle_other_clk(even_other); + end + + bit even_third = 1; + import "DPI-C" context function void toggle_third_clk(bit val); + always @(posedge other_clk) begin + even_third <= ~even_third; + toggle_third_clk(even_third); + end + + int n = 0; + + wire final_clk = $c1("1") & third_clk; + + always @(posedge final_clk) begin + $display("t=%d n=%d", $time, n); + if ($time != (8*n+1) * 500) $stop; + if (n == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + n += 1; + end + +endmodule + diff --git a/test_regress/t/t_order_dpi_export_5.cpp b/test_regress/t/t_order_dpi_export_5.cpp new file mode 100644 index 000000000..c14bf9ef3 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_5.cpp @@ -0,0 +1,39 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 + +#include +#include + +int main(int argc, char* argv[]) { + Vt_order_dpi_export_5* const tb = new Vt_order_dpi_export_5; + tb->contextp()->commandArgs(argc, argv); + bool clk = true; + + while (!tb->contextp()->gotFinish()) { + // Timeout + if (tb->contextp()->time() > 100000) break; + // Toggle and set main clock + clk = !clk; + tb->clk = clk; + // Reset counter at falling clock edge, once it reached value 4 + svSetScope(svGetScopeFromName("TOP.testbench")); + if (get_cnt() == 4 && !clk) set_cnt(0); + // Eval + tb->eval(); + // Advance time + tb->contextp()->timeInc(500); + } + + delete tb; + return 0; +} diff --git a/test_regress/t/t_order_dpi_export_5.pl b/test_regress/t/t_order_dpi_export_5.pl new file mode 100755 index 000000000..21b3c76b6 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_5.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(vlt_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_dpi_export_5.v b/test_regress/t/t_order_dpi_export_5.v new file mode 100644 index 000000000..744c64ca8 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_5.v @@ -0,0 +1,46 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +module testbench( + /*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + int cnt = 0; + export "DPI-C" function set_cnt; + function void set_cnt(int val); + cnt = val; + endfunction; + export "DPI-C" function get_cnt; + function int get_cnt(); + return cnt; + endfunction; + + always @(posedge clk) cnt += 1; + + // Downstream combinational signal dependent on both input clock and + // DPI export. + wire dependent_clk = cnt == 2; + + int n = 0; + + always @(posedge dependent_clk) begin + $display("t=%t n=%d", $time, n); + if ($time != (8*n+3) * 500) $stop; + if (n == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + n += 1; + end + +endmodule + diff --git a/test_regress/t/t_param_array7.pl b/test_regress/t/t_param_array7.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_param_array7.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_param_array7.v b/test_regress/t/t_param_array7.v new file mode 100644 index 000000000..72380d0a9 --- /dev/null +++ b/test_regress/t/t_param_array7.v @@ -0,0 +1,57 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2021 by Geza Lore. +// SPDX-License-Identifier: CC0-1.0 + +typedef struct packed { + longint a; + longint b; + longint c; +} s_t; + +module t; + localparam int c0 [4] = '{5, 6, 7, 8}; + localparam bit [255:0] c1 [4] = '{9, 10, 11, 12}; + localparam string c2 [2] = '{"baz", "quux"}; + localparam s_t c3 [2] = '{'{a: 100, b: 200, c: 300}, + '{a: 1000, b: 2000, c: 3000}}; + + a #( + .p0(c0), + .p1(c1), + .p2(c2), + .p3(c3) + ) i_a (); +endmodule + +module a + #( + parameter int p0 [4] = '{1, 2, 3, 4}, + parameter bit [255:0] p1 [4] = '{1, 2, 3, 4}, + parameter string p2 [2] = '{"foo", "bar"}, + parameter s_t p3 [2] = '{'{a: 1, b: 2, c: 3}, + '{a: 1, b: 2, c: 3}} + ); + initial begin + // Go via $c to ensure parameters are emitted + if (p0[$c("0")] != 5) $stop; + if (p0[$c("1")] != 6) $stop; + if (p0[$c("2")] != 7) $stop; + if (p0[$c("3")] != 8) $stop; + if (p1[$c("0")] != 9) $stop; + if (p1[$c("1")] != 10) $stop; + if (p1[$c("2")] != 11) $stop; + if (p1[$c("3")] != 12) $stop; + if (p2[$c("0")] != "baz") $stop; + if (p2[$c("1")] != "quux") $stop; + if (p3[$c("0")].a != 100) $stop; + if (p3[$c("0")].b != 200) $stop; + if (p3[$c("0")].c != 300) $stop; + if (p3[$c("1")].a != 1000) $stop; + if (p3[$c("1")].b != 2000) $stop; + if (p3[$c("1")].c != 3000) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_param_in_func.pl b/test_regress/t/t_param_in_func.pl new file mode 100755 index 000000000..849ab9a69 --- /dev/null +++ b/test_regress/t/t_param_in_func.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 2019 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + verilator_flags2 => ["--stats"], + ); + +execute(check_finished => 1); + +# The parameter array should have been put in the constant pool +if ($Self->{vlt_all}) { + file_grep($Self->{stats}, qr/ConstPool, Tables emitted\s+(\d+)/i, 3); +} + +# Shouldn't have any references to the parameter array +foreach my $file ( + glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.h"), + glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.cpp") + ) { + file_grep_not($file, qr/digits/i); +} + +ok(1); +1; diff --git a/test_regress/t/t_param_in_func.v b/test_regress/t/t_param_in_func.v new file mode 100644 index 000000000..62e86b395 --- /dev/null +++ b/test_regress/t/t_param_in_func.v @@ -0,0 +1,130 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2019 by Driss Hafdi. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + initial begin + if (getUnpacked($c("0")) != "0") $stop; + if (getUnpacked($c("1")) != "1") $stop; + if (getUnpacked($c("2")) != "2") $stop; + if (getUnpacked($c("3")) != "3") $stop; + if (getUnpacked($c("4")) != "4") $stop; + if (getUnpacked($c("5")) != "5") $stop; + if (getUnpacked($c("6")) != "6") $stop; + if (getUnpacked($c("7")) != "7") $stop; + if (getUnpacked($c("8")) != "8") $stop; + if (getUnpacked($c("9")) != "9") $stop; + + if (getPacked($c("0")) != "0") $stop; + if (getPacked($c("1")) != "1") $stop; + if (getPacked($c("2")) != "2") $stop; + if (getPacked($c("3")) != "3") $stop; + if (getPacked($c("4")) != "4") $stop; + if (getPacked($c("5")) != "5") $stop; + if (getPacked($c("6")) != "6") $stop; + if (getPacked($c("7")) != "7") $stop; + if (getPacked($c("8")) != "8") $stop; + if (getPacked($c("9")) != "9") $stop; + + if (getString($c("0")) != "0") $stop; + if (getString($c("1")) != "1") $stop; + if (getString($c("2")) != "2") $stop; + if (getString($c("3")) != "3") $stop; + if (getString($c("4")) != "4") $stop; + if (getString($c("5")) != "5") $stop; + if (getString($c("6")) != "6") $stop; + if (getString($c("7")) != "7") $stop; + if (getString($c("8")) != "8") $stop; + if (getString($c("9")) != "9") $stop; + + if (getStruct($c("0")) != "0") $stop; + if (getStruct($c("1")) != "1") $stop; + if (getStruct($c("2")) != "2") $stop; + if (getStruct($c("3")) != "3") $stop; + if (getStruct($c("4")) != "4") $stop; + if (getStruct($c("5")) != "5") $stop; + if (getStruct($c("6")) != "6") $stop; + if (getStruct($c("7")) != "7") $stop; + if (getStruct($c("8")) != "8") $stop; + if (getStruct($c("9")) != "9") $stop; + + if (getType($c("0")) != "0") $stop; + if (getType($c("1")) != "1") $stop; + if (getType($c("2")) != "2") $stop; + if (getType($c("3")) != "3") $stop; + if (getType($c("4")) != "4") $stop; + if (getType($c("5")) != "5") $stop; + if (getType($c("6")) != "6") $stop; + if (getType($c("7")) != "7") $stop; + if (getType($c("8")) != "8") $stop; + if (getType($c("9")) != "9") $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule + +function automatic logic [7:0] getUnpacked(logic[3:0] d); +`ifdef NO_INLINE + /* verilator no_inline_task */ +`endif + localparam logic [7:0] digits [10] = + '{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; + return digits[d]; +endfunction + +function automatic logic [7:0] getPacked(logic[3:0] d); +`ifdef NO_INLINE + /* verilator no_inline_task */ +`endif + localparam logic [9:0][7:0] digits = + {"9", "8", "7", "6", "5", "4", "3", "2", "1", "0"}; + return digits[d]; +endfunction + +function automatic string getString(logic[3:0] d); +`ifdef NO_INLINE + /* verilator no_inline_task */ +`endif + localparam string digits [10] = + '{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; + return digits[d]; +endfunction + +function automatic logic [7:0] getStruct(logic[3:0] d); +`ifdef NO_INLINE + /* verilator no_inline_task */ +`endif + // Silly indirect lookup table because we want to use a struct + typedef struct packed { + logic [7:0] result; + longint index; + } lut_t; + localparam lut_t digits [10] = + '{ + '{result: "1", index: 9}, + '{result: "2", index: 0}, + '{result: "3", index: 1}, + '{result: "4", index: 2}, + '{result: "5", index: 3}, + '{result: "6", index: 4}, + '{result: "7", index: 5}, + '{result: "8", index: 6}, + '{result: "9", index: 7}, + '{result: "0", index: 8} + }; + return digits[4'(digits[d].index)].result; +endfunction + +function automatic logic [7:0] getType(logic[3:0] d); +`ifdef NO_INLINE + /* verilator no_inline_task */ +`endif + localparam type octet_t = logic [7:0]; + localparam octet_t [9:0] digits = + {"9", "8", "7", "6", "5", "4", "3", "2", "1", "0"}; + return digits[d]; +endfunction + diff --git a/test_regress/t/t_param_in_func_bad.out b/test_regress/t/t_param_in_func_bad.out deleted file mode 100644 index 73820ed23..000000000 --- a/test_regress/t/t_param_in_func_bad.out +++ /dev/null @@ -1,6 +0,0 @@ -%Error-UNSUPPORTED: t/t_param_in_func_bad.v:24:26: Unsupported: Parameters in functions with complex assign - : ... In instance t - 24 | localparam logic[7:0] digits[10] - | ^~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to diff --git a/test_regress/t/t_param_in_func_bad.v b/test_regress/t/t_param_in_func_bad.v deleted file mode 100644 index 02a451f42..000000000 --- a/test_regress/t/t_param_in_func_bad.v +++ /dev/null @@ -1,29 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed into the Public Domain, for any use, -// without warranty, 2019 by Driss Hafdi. -// SPDX-License-Identifier: CC0-1.0 - -module t (/*AUTOARG*/ - // Inputs - clk - ); - - input clk; - - logic [7:0] digit = getDigit(4'd1); - - initial begin - if (digit != "1") $stop; - $write("*-* All Finished *-*\n"); - $finish; - end -endmodule - -function automatic logic[7:0] getDigit(logic[3:0] d); - localparam logic[7:0] digits[10] - = '{ - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" - }; - return digits[d]; -endfunction diff --git a/test_regress/t/t_param_in_func_noinline.pl b/test_regress/t/t_param_in_func_noinline.pl new file mode 100755 index 000000000..c089b8b2e --- /dev/null +++ b/test_regress/t/t_param_in_func_noinline.pl @@ -0,0 +1,35 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2019 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +top_filename("t/t_param_in_func.v"); + +compile( + verilator_flags2 => ["--stats", "+define+NO_INLINE=1"], + ); + +execute(check_finished => 1); + +# The parameter array should have been put in the constant pool +if ($Self->{vlt_all}) { + file_grep($Self->{stats}, qr/ConstPool, Tables emitted\s+(\d+)/i, 3); +} + +# Shouldn't have any references to the parameter array +foreach my $file ( + glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.h"), + glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.cpp") + ) { + file_grep_not($file, qr/digits/i); +} + +ok(1); +1; diff --git a/test_regress/t/t_param_pattern.pl b/test_regress/t/t_param_pattern.pl new file mode 100755 index 000000000..e0e57ed54 --- /dev/null +++ b/test_regress/t/t_param_pattern.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + verilator_flags2 => ['--dump-tree'] + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_param_pattern.v b/test_regress/t/t_param_pattern.v new file mode 100644 index 000000000..5f9306d64 --- /dev/null +++ b/test_regress/t/t_param_pattern.v @@ -0,0 +1,45 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2021 by Krzysztof Bieganski. +// SPDX-License-Identifier: CC0-1.0 + +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0); + +package config_pkg; + typedef struct packed { + int UPPER0; + int UPPER2; + int USE_QUAD0; + int USE_QUAD1; + int USE_QUAD2; + } config_struct; + +endpackage : config_pkg + +module t; + import config_pkg::*; + + struct_submodule #(.MY_CONFIG('{ + UPPER0: 10, + UPPER2: 20, + USE_QUAD0: 4, + USE_QUAD1: 5, + USE_QUAD2: 6 + })) a_submodule_I (); +endmodule : t + +module struct_submodule + import config_pkg::*; + #(parameter config_struct MY_CONFIG = '0); + + initial begin + `checkd(MY_CONFIG.UPPER0, 10); + `checkd(MY_CONFIG.USE_QUAD0, 4); + `checkd(MY_CONFIG.USE_QUAD1, 5); + `checkd(MY_CONFIG.USE_QUAD2, 6); + `checkd(MY_CONFIG.UPPER2, 20); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule : struct_submodule diff --git a/test_regress/t/t_preproc.out b/test_regress/t/t_preproc.out index 376c8eb4b..d3d6e5c83 100644 --- a/test_regress/t/t_preproc.out +++ b/test_regress/t/t_preproc.out @@ -325,9 +325,14 @@ ZCBXb3JrIGFzIG== `pragma protect end_protected `line 211 "t/t_preproc.v" 0 + +`pragma protect +`pragma protect end + +`line 215 "t/t_preproc.v" 0 endmodule -`line 213 "t/t_preproc.v" 0 +`line 217 "t/t_preproc.v" 0 @@ -338,17 +343,17 @@ endmodule -`line 223 "t/t_preproc.v" 0 +`line 227 "t/t_preproc.v" 0 begin addr <= (({regs[6], regs[7]} + 1)); rd <= 1; end and begin addr <= (({regs[6], regs[7]})); wdata <= (rdata); wr <= 1; end begin addr <= ({regs[6], regs[7]} + 1); rd <= 1; end begin addr <= ({regs[6], regs[7]}); wdata <= (rdata); wr <= 1; end more -`line 227 "t/t_preproc.v" 0 +`line 231 "t/t_preproc.v" 0 -`line 230 "t/t_preproc.v" 0 +`line 234 "t/t_preproc.v" 0 `line 1 "t/t_preproc_inc4.vh" 1 `line 2 "t/t_preproc_inc4.vh" 0 @@ -360,57 +365,57 @@ begin addr <= ({regs[6], regs[7]}); wdata <= (rdata); wr <= 1; end more `line 8 "t/t_preproc_inc4.vh" 2 -`line 230 "t/t_preproc.v" 0 - -`line 231 "t/t_preproc.v" 0 - - - `line 234 "t/t_preproc.v" 0 - -`line 236 "t/t_preproc.v" 0 +`line 235 "t/t_preproc.v" 0 +`line 238 "t/t_preproc.v" 0 + `line 240 "t/t_preproc.v" 0 + + + +`line 244 "t/t_preproc.v" 0 + -`line 243 "t/t_preproc.v" 0 +`line 247 "t/t_preproc.v" 0 $blah("ab,cd","e,f"); $blah(this.logfile,vec); $blah(this.logfile,vec[1,2,3]); $blah(this.logfile,{blah.name(), " is not foo"}); -`line 249 "t/t_preproc.v" 0 +`line 253 "t/t_preproc.v" 0 -`line 252 "t/t_preproc.v" 0 +`line 256 "t/t_preproc.v" 0 `pragma foo = 1 `default_nettype none `default_nettype uwire -`line 256 "t/t_preproc.v" 0 +`line 260 "t/t_preproc.v" 0 -`line 259 "t/t_preproc.v" 0 +`line 263 "t/t_preproc.v" 0 -`line 263 "t/t_preproc.v" 0 -Line_Preproc_Check 263 +`line 267 "t/t_preproc.v" 0 +Line_Preproc_Check 267 -`line 265 "t/t_preproc.v" 0 +`line 269 "t/t_preproc.v" 0 -`line 268 "t/t_preproc.v" 0 +`line 272 "t/t_preproc.v" 0 @@ -418,15 +423,15 @@ Line_Preproc_Check 263 -`line 275 "t/t_preproc.v" 0 +`line 279 "t/t_preproc.v" 0 (x,y) -Line_Preproc_Check 276 +Line_Preproc_Check 280 -`line 278 "t/t_preproc.v" 0 +`line 282 "t/t_preproc.v" 0 -`line 281 "t/t_preproc.v" 0 +`line 285 "t/t_preproc.v" 0 @@ -435,17 +440,17 @@ beginend beginend "beginend" -`line 289 "t/t_preproc.v" 0 +`line 293 "t/t_preproc.v" 0 `\esc`def -`line 295 "t/t_preproc.v" 0 +`line 299 "t/t_preproc.v" 0 Not a \`define -`line 297 "t/t_preproc.v" 0 +`line 301 "t/t_preproc.v" 0 @@ -454,23 +459,23 @@ Not a \`define x,y)--bee submacro has comma paren -`line 305 "t/t_preproc.v" 0 +`line 309 "t/t_preproc.v" 0 $display("10 %d %d", $bits(foo), 10); -`line 310 "t/t_preproc.v" 0 +`line 314 "t/t_preproc.v" 0 -`line 315 "t/t_preproc.v" 0 +`line 319 "t/t_preproc.v" 0 -`line 318 "t/t_preproc.v" 0 +`line 322 "t/t_preproc.v" 0 @@ -485,30 +490,30 @@ $display("10 %d %d", $bits(foo), 10); -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 assign a3 = ~b3 ; -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 334 "t/t_preproc.v" 0 +`line 338 "t/t_preproc.v" 0 \ @@ -519,56 +524,56 @@ $display("10 %d %d", $bits(foo), 10); -`line 343 "t/t_preproc.v" 0 +`line 347 "t/t_preproc.v" 0 -`line 343 "t/t_preproc.v" 0 +`line 347 "t/t_preproc.v" 0 -`line 343 "t/t_preproc.v" 0 +`line 347 "t/t_preproc.v" 0 def i -`line 345 "t/t_preproc.v" 0 +`line 349 "t/t_preproc.v" 0 -`line 347 "t/t_preproc.v" 0 - - - - - `line 351 "t/t_preproc.v" 0 + + + + +`line 355 "t/t_preproc.v" 0 + -`line 357 "t/t_preproc.v" 0 +`line 361 "t/t_preproc.v" 0 1 /*verilator NOT IN DEFINE*/ (nodef) 2 /*verilator PART OF DEFINE*/ (hasdef) 3 -`line 359 "t/t_preproc.v" 0 +`line 363 "t/t_preproc.v" 0 /*verilator NOT PART OF DEFINE*/ (nodef) -`line 360 "t/t_preproc.v" 0 +`line 364 "t/t_preproc.v" 0 4 -`line 360 "t/t_preproc.v" 0 +`line 364 "t/t_preproc.v" 0 /*verilator PART OF DEFINE*/ (nodef) -`line 361 "t/t_preproc.v" 0 +`line 365 "t/t_preproc.v" 0 5 also in -`line 361 "t/t_preproc.v" 0 +`line 365 "t/t_preproc.v" 0 also3 (nodef) HAS a NEW -`line 364 "t/t_preproc.v" 0 +`line 368 "t/t_preproc.v" 0 LINE -`line 366 "t/t_preproc.v" 0 +`line 370 "t/t_preproc.v" 0 -`line 368 "t/t_preproc.v" 0 +`line 372 "t/t_preproc.v" 0 @@ -582,11 +587,11 @@ LINE -`line 381 "t/t_preproc.v" 0 +`line 385 "t/t_preproc.v" 0 -`line 384 "t/t_preproc.v" 0 +`line 388 "t/t_preproc.v" 0 EXP: clxx_scen clxx_scen EXP: clxx_scen @@ -594,44 +599,44 @@ EXP: clxx_scen EXP: do if (start("verilog/inc1.v", 25)) begin message({"Blah-", "clx_scen", " end"}); end while(0); -`line 390 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 do -`line 390 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 -`line 390 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 -`line 390 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 -`line 390 "t/t_preproc.v" 0 - -`line 390 "t/t_preproc.v" 0 - if (start("t/t_preproc.v", 390)) begin -`line 390 "t/t_preproc.v" 0 - -`line 390 "t/t_preproc.v" 0 - message({"Blah-", "clx_scen", " end"}); -`line 390 "t/t_preproc.v" 0 - end -`line 390 "t/t_preproc.v" 0 - -`line 390 "t/t_preproc.v" 0 - while(0); - -`line 392 "t/t_preproc.v" 0 - - `line 394 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 + if (start("t/t_preproc.v", 394)) begin +`line 394 "t/t_preproc.v" 0 + +`line 394 "t/t_preproc.v" 0 + message({"Blah-", "clx_scen", " end"}); +`line 394 "t/t_preproc.v" 0 + end +`line 394 "t/t_preproc.v" 0 + +`line 394 "t/t_preproc.v" 0 + while(0); - - +`line 396 "t/t_preproc.v" 0 + `line 398 "t/t_preproc.v" 0 + + + + + +`line 402 "t/t_preproc.v" 0 -`line 398 "t/t_preproc.v" 0 +`line 402 "t/t_preproc.v" 0 -`line 399 "t/t_preproc.v" 0 +`line 403 "t/t_preproc.v" 0 EXP: This is fooed @@ -639,7 +644,7 @@ This is fooed EXP: This is fooed_2 This is fooed_2 -`line 406 "t/t_preproc.v" 0 +`line 410 "t/t_preproc.v" 0 np @@ -651,11 +656,11 @@ np -`line 417 "t/t_preproc.v" 0 +`line 421 "t/t_preproc.v" 0 -`line 420 "t/t_preproc.v" 0 +`line 424 "t/t_preproc.v" 0 @@ -664,12 +669,12 @@ np -`line 428 "t/t_preproc.v" 0 - - - - `line 432 "t/t_preproc.v" 0 + + + + +`line 436 "t/t_preproc.v" 0 hello3hello3hello3 hello4hello4hello4hello4 @@ -677,7 +682,7 @@ hello4hello4hello4hello4 -`line 438 "t/t_preproc.v" 0 +`line 442 "t/t_preproc.v" 0 `line 1 "t/t_preproc_inc4.vh" 1 `line 2 "t/t_preproc_inc4.vh" 0 @@ -689,9 +694,9 @@ hello4hello4hello4hello4 `line 8 "t/t_preproc_inc4.vh" 2 -`line 438 "t/t_preproc.v" 0 +`line 442 "t/t_preproc.v" 0 -`line 439 "t/t_preproc.v" 0 +`line 443 "t/t_preproc.v" 0 @@ -701,28 +706,28 @@ hello4hello4hello4hello4 -`line 447 "t/t_preproc.v" 0 +`line 451 "t/t_preproc.v" 0 -Line_Preproc_Check 451 +Line_Preproc_Check 455 -Line_Preproc_Check 457 +Line_Preproc_Check 461 "FOO \ BAR " "arg_line1 \ arg_line2" "FOO \ BAR " -`line 460 "t/t_preproc.v" 0 -Line_Preproc_Check 460 +`line 464 "t/t_preproc.v" 0 +Line_Preproc_Check 464 -`line 464 "t/t_preproc.v" 0 +`line 468 "t/t_preproc.v" 0 @@ -733,14 +738,14 @@ abc -`line 474 "t/t_preproc.v" 0 +`line 478 "t/t_preproc.v" 0 EXP: sonet_frame sonet_frame -`line 480 "t/t_preproc.v" 0 +`line 484 "t/t_preproc.v" 0 EXP: sonet_frame @@ -751,7 +756,7 @@ sonet_frame EXP: sonet_frame sonet_frame -`line 490 "t/t_preproc.v" 0 +`line 494 "t/t_preproc.v" 0 @@ -759,13 +764,13 @@ EXP: module zzz ; endmodule module zzz ; endmodule module zzz ; endmodule -`line 497 "t/t_preproc.v" 0 +`line 501 "t/t_preproc.v" 0 EXP: module a_b ; endmodule module a_b ; endmodule module a_b ; endmodule -`line 502 "t/t_preproc.v" 0 +`line 506 "t/t_preproc.v" 0 integer foo; @@ -779,7 +784,7 @@ module t; initial begin : \`LEX_CAT(a[0],_assignment) -`line 514 "t/t_preproc.v" 0 +`line 518 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\`LEX_CAT(a[0],_assignment) "); end @@ -788,7 +793,7 @@ module t; initial begin : \a[0]_assignment_a[1] -`line 521 "t/t_preproc.v" 0 +`line 525 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\a[0]_assignment_a[1] "); end @@ -804,7 +809,7 @@ module t; initial begin : \`CAT(ff,bb) -`line 535 "t/t_preproc.v" 0 +`line 539 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\`CAT(ff,bb) "); end @@ -812,7 +817,7 @@ module t; initial begin : \`zzz -`line 541 "t/t_preproc.v" 0 +`line 545 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\`zzz "); end @@ -821,11 +826,11 @@ module t; initial begin : \`FOO -`line 548 "t/t_preproc.v" 0 +`line 552 "t/t_preproc.v" 0 $write("GOT%%m='%m' OTHER_EXP='%s'\n OUR_EXP='%s'", "t.bar ","t.\\`FOO "); end initial begin : \xx`FOO -`line 550 "t/t_preproc.v" 0 +`line 554 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\xx`FOO "); end @@ -858,27 +863,27 @@ module t; initial -`line 581 "t/t_preproc.v" 0 +`line 585 "t/t_preproc.v" 0 $display("%s%s","a1","b2c3\n"); endmodule -`line 584 "t/t_preproc.v" 0 +`line 588 "t/t_preproc.v" 0 -`line 587 "t/t_preproc.v" 0 +`line 591 "t/t_preproc.v" 0 $display("RAM0"); $display("CPU"); -`line 592 "t/t_preproc.v" 0 +`line 596 "t/t_preproc.v" 0 -`line 597 "t/t_preproc.v" 0 +`line 601 "t/t_preproc.v" 0 XXE_FAMILY = XXE_ @@ -886,7 +891,7 @@ XXE_FAMILY = XXE_ $display("XXE_ is defined"); -`line 604 "t/t_preproc.v" 0 +`line 608 "t/t_preproc.v" 0 XYE_FAMILY = XYE_ @@ -894,7 +899,7 @@ XYE_FAMILY = XYE_ $display("XYE_ is defined"); -`line 611 "t/t_preproc.v" 0 +`line 615 "t/t_preproc.v" 0 XXS_FAMILY = XXS_some @@ -902,7 +907,7 @@ XXS_FAMILY = XXS_some $display("XXS_some is defined"); -`line 618 "t/t_preproc.v" 0 +`line 622 "t/t_preproc.v" 0 XYS_FAMILY = XYS_foo @@ -910,10 +915,10 @@ XYS_FAMILY = XYS_foo $display("XYS_foo is defined"); -`line 625 "t/t_preproc.v" 0 +`line 629 "t/t_preproc.v" 0 -`line 627 "t/t_preproc.v" 0 +`line 631 "t/t_preproc.v" 0 @@ -922,7 +927,7 @@ XYS_FAMILY = XYS_foo -`line 635 "t/t_preproc.v" 0 +`line 639 "t/t_preproc.v" 0 @@ -930,7 +935,7 @@ XYS_FAMILY = XYS_foo -`line 642 "t/t_preproc.v" 0 +`line 646 "t/t_preproc.v" 0 @@ -938,7 +943,7 @@ XYS_FAMILY = XYS_foo -`line 649 "t/t_preproc.v" 0 +`line 653 "t/t_preproc.v" 0 @@ -946,26 +951,26 @@ XYS_FAMILY = XYS_foo -`line 656 "t/t_preproc.v" 0 - - -`line 658 "t/t_preproc.v" 0 - - `line 660 "t/t_preproc.v" 0 - - -(.mySig (myInterface.pa5), + + +`line 662 "t/t_preproc.v" 0 + `line 664 "t/t_preproc.v" 0 +(.mySig (myInterface.pa5), -`line 667 "t/t_preproc.v" 0 +`line 668 "t/t_preproc.v" 0 + + + +`line 671 "t/t_preproc.v" 0 `dbg_hdl(UVM_LOW, ("Functional coverage enabled: paramgrp")); -`line 670 "t/t_preproc.v" 0 +`line 674 "t/t_preproc.v" 0 @@ -974,28 +979,28 @@ XYS_FAMILY = XYS_foo -`line 678 "t/t_preproc.v" 0 +`line 682 "t/t_preproc.v" 0 module pcc2_cfg; generate -`line 680 "t/t_preproc.v" 0 +`line 684 "t/t_preproc.v" 0 covergroup a @(posedge b); -`line 680 "t/t_preproc.v" 0 +`line 684 "t/t_preproc.v" 0 c: coverpoint d iff ((c) === 1'b1); endgroup -`line 680 "t/t_preproc.v" 0 +`line 684 "t/t_preproc.v" 0 a u_a; -`line 680 "t/t_preproc.v" 0 +`line 684 "t/t_preproc.v" 0 initial do begin $display ("DEBUG : %s [%m]", $sformatf ("Functional coverage enabled: u_a")); end while(0); endgenerate endmodule -`line 684 "t/t_preproc.v" 0 +`line 688 "t/t_preproc.v" 0 "`NOT_DEFINED_STR" -`line 689 "t/t_preproc.v" 0 +`line 693 "t/t_preproc.v" 0 @@ -1018,4 +1023,4 @@ predef 2 2 -`line 711 "t/t_preproc.v" 2 +`line 715 "t/t_preproc.v" 2 diff --git a/test_regress/t/t_preproc.v b/test_regress/t/t_preproc.v index a52ce02c1..50f18e53d 100644 --- a/test_regress/t/t_preproc.v +++ b/test_regress/t/t_preproc.v @@ -208,6 +208,10 @@ ZCBXb3JrIGFzIG== `pragma protect end_protected +// encoding envelope +`pragma protect +`pragma protect end + endmodule //====================================================================== diff --git a/test_regress/t/t_preproc_comments.out b/test_regress/t/t_preproc_comments.out index 1d2795895..881de7e8d 100644 --- a/test_regress/t/t_preproc_comments.out +++ b/test_regress/t/t_preproc_comments.out @@ -325,9 +325,14 @@ ZCBXb3JrIGFzIG== `pragma protect end_protected `line 211 "t/t_preproc.v" 0 +// encoding envelope +`pragma protect +`pragma protect end + +`line 215 "t/t_preproc.v" 0 endmodule -`line 213 "t/t_preproc.v" 0 +`line 217 "t/t_preproc.v" 0 //====================================================================== // macro call with define that has comma @@ -338,17 +343,17 @@ endmodule -`line 223 "t/t_preproc.v" 0 +`line 227 "t/t_preproc.v" 0 begin addr <= (({regs[6], regs[7]} + 1)); rd <= 1; end and begin addr <= (({regs[6], regs[7]})); wdata <= (rdata); wr <= 1; end begin addr <= ({regs[6], regs[7]} + 1); rd <= 1; end begin addr <= ({regs[6], regs[7]}); wdata <= (rdata); wr <= 1; end more -`line 227 "t/t_preproc.v" 0 +`line 231 "t/t_preproc.v" 0 //====================================================================== // include of parameterized file -`line 230 "t/t_preproc.v" 0 +`line 234 "t/t_preproc.v" 0 `line 1 "t/t_preproc_inc4.vh" 1 // DESCRIPTION: Verilog::Preproc: Example source code `line 2 "t/t_preproc_inc4.vh" 0 @@ -360,57 +365,57 @@ begin addr <= ({regs[6], regs[7]}); wdata <= (rdata); wr <= 1; end more `line 8 "t/t_preproc_inc4.vh" 2 -`line 230 "t/t_preproc.v" 0 - -`line 231 "t/t_preproc.v" 0 - - - `line 234 "t/t_preproc.v" 0 - -`line 236 "t/t_preproc.v" 0 +`line 235 "t/t_preproc.v" 0 +`line 238 "t/t_preproc.v" 0 + `line 240 "t/t_preproc.v" 0 + + + + +`line 244 "t/t_preproc.v" 0 //====================================================================== // macro call with , in {} -`line 243 "t/t_preproc.v" 0 +`line 247 "t/t_preproc.v" 0 $blah("ab,cd","e,f"); $blah(this.logfile,vec); $blah(this.logfile,vec[1,2,3]); $blah(this.logfile,{blah.name(), " is not foo"}); -`line 249 "t/t_preproc.v" 0 +`line 253 "t/t_preproc.v" 0 //====================================================================== // pragma/default net type -`line 252 "t/t_preproc.v" 0 +`line 256 "t/t_preproc.v" 0 `pragma foo = 1 `default_nettype none `default_nettype uwire -`line 256 "t/t_preproc.v" 0 +`line 260 "t/t_preproc.v" 0 //====================================================================== // Ifdef -`line 259 "t/t_preproc.v" 0 +`line 263 "t/t_preproc.v" 0 -`line 263 "t/t_preproc.v" 0 -Line_Preproc_Check 263 +`line 267 "t/t_preproc.v" 0 +Line_Preproc_Check 267 -`line 265 "t/t_preproc.v" 0 +`line 269 "t/t_preproc.v" 0 //====================================================================== // bug84 -`line 268 "t/t_preproc.v" 0 +`line 272 "t/t_preproc.v" 0 // Hello, comments MIGHT not be legal /*more,,)cmts*/ // But newlines ARE legal... who speced THAT? @@ -418,15 +423,15 @@ Line_Preproc_Check 263 -`line 275 "t/t_preproc.v" 0 +`line 279 "t/t_preproc.v" 0 (//Here x,y //Too) -Line_Preproc_Check 276 +Line_Preproc_Check 280 -`line 278 "t/t_preproc.v" 0 +`line 282 "t/t_preproc.v" 0 //====================================================================== // defines split arguments -`line 281 "t/t_preproc.v" 0 +`line 285 "t/t_preproc.v" 0 @@ -435,17 +440,17 @@ beginend // 2001 spec doesn't require two tokens, so "beginend" ok beginend // 2001 spec doesn't require two tokens, so "beginend" ok "beginend" // No space "beginend" -`line 289 "t/t_preproc.v" 0 +`line 293 "t/t_preproc.v" 0 //====================================================================== // bug106 `\esc`def -`line 295 "t/t_preproc.v" 0 +`line 299 "t/t_preproc.v" 0 Not a \`define -`line 297 "t/t_preproc.v" 0 +`line 301 "t/t_preproc.v" 0 //====================================================================== // misparsed comma in submacro @@ -454,23 +459,23 @@ Not a \`define x,y)--bee submacro has comma paren -`line 305 "t/t_preproc.v" 0 +`line 309 "t/t_preproc.v" 0 //====================================================================== // bug191 $display("10 %d %d", $bits(foo), 10); -`line 310 "t/t_preproc.v" 0 +`line 314 "t/t_preproc.v" 0 //====================================================================== // 1800-2009 -`line 315 "t/t_preproc.v" 0 +`line 319 "t/t_preproc.v" 0 -`line 318 "t/t_preproc.v" 0 +`line 322 "t/t_preproc.v" 0 //====================================================================== // bug202 @@ -485,34 +490,34 @@ $display("10 %d %d", $bits(foo), 10); -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 assign a3 = ~b3 ; -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 334 "t/t_preproc.v" 0 +`line 338 "t/t_preproc.v" 0 /* multi \ line1*/ \ -`line 336 "t/t_preproc.v" 0 +`line 340 "t/t_preproc.v" 0 /*multi \ line2*/ @@ -521,59 +526,59 @@ $display("10 %d %d", $bits(foo), 10); -`line 343 "t/t_preproc.v" 0 +`line 347 "t/t_preproc.v" 0 -`line 343 "t/t_preproc.v" 0 +`line 347 "t/t_preproc.v" 0 -`line 343 "t/t_preproc.v" 0 +`line 347 "t/t_preproc.v" 0 /* multi line 3*/ -`line 343 "t/t_preproc.v" 0 +`line 347 "t/t_preproc.v" 0 def i -`line 345 "t/t_preproc.v" 0 +`line 349 "t/t_preproc.v" 0 //====================================================================== -`line 347 "t/t_preproc.v" 0 - - - - - `line 351 "t/t_preproc.v" 0 + + + + +`line 355 "t/t_preproc.v" 0 + -`line 357 "t/t_preproc.v" 0 +`line 361 "t/t_preproc.v" 0 1 // verilator NOT IN DEFINE (nodef) 2 /* verilator PART OF DEFINE */ (hasdef) 3 -`line 359 "t/t_preproc.v" 0 +`line 363 "t/t_preproc.v" 0 /* verilator NOT PART OF DEFINE */ (nodef) -`line 360 "t/t_preproc.v" 0 +`line 364 "t/t_preproc.v" 0 4 -`line 360 "t/t_preproc.v" 0 +`line 364 "t/t_preproc.v" 0 /* verilator PART OF DEFINE */ (nodef) -`line 361 "t/t_preproc.v" 0 +`line 365 "t/t_preproc.v" 0 5 also in -`line 361 "t/t_preproc.v" 0 +`line 365 "t/t_preproc.v" 0 also3 // CMT NOT (nodef) HAS a NEW -`line 364 "t/t_preproc.v" 0 +`line 368 "t/t_preproc.v" 0 LINE -`line 366 "t/t_preproc.v" 0 +`line 370 "t/t_preproc.v" 0 //====================================================================== -`line 368 "t/t_preproc.v" 0 +`line 372 "t/t_preproc.v" 0 @@ -587,11 +592,11 @@ LINE -`line 381 "t/t_preproc.v" 0 +`line 385 "t/t_preproc.v" 0 -`line 384 "t/t_preproc.v" 0 +`line 388 "t/t_preproc.v" 0 EXP: clxx_scen clxx_scen EXP: clxx_scen @@ -599,44 +604,44 @@ EXP: clxx_scen EXP: do if (start("verilog/inc1.v", 25)) begin message({"Blah-", "clx_scen", " end"}); end while(0); -`line 390 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 do -`line 390 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 /* synopsys translate_off */ -`line 390 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 -`line 390 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 -`line 390 "t/t_preproc.v" 0 - -`line 390 "t/t_preproc.v" 0 - if (start("t/t_preproc.v", 390)) begin -`line 390 "t/t_preproc.v" 0 - -`line 390 "t/t_preproc.v" 0 - message({"Blah-", "clx_scen", " end"}); -`line 390 "t/t_preproc.v" 0 - end -`line 390 "t/t_preproc.v" 0 - /* synopsys translate_on */ -`line 390 "t/t_preproc.v" 0 - while(0); - -`line 392 "t/t_preproc.v" 0 -//====================================================================== - `line 394 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 + if (start("t/t_preproc.v", 394)) begin +`line 394 "t/t_preproc.v" 0 + +`line 394 "t/t_preproc.v" 0 + message({"Blah-", "clx_scen", " end"}); +`line 394 "t/t_preproc.v" 0 + end +`line 394 "t/t_preproc.v" 0 + /* synopsys translate_on */ +`line 394 "t/t_preproc.v" 0 + while(0); - - +`line 396 "t/t_preproc.v" 0 +//====================================================================== `line 398 "t/t_preproc.v" 0 + + + + + +`line 402 "t/t_preproc.v" 0 -`line 398 "t/t_preproc.v" 0 +`line 402 "t/t_preproc.v" 0 -`line 399 "t/t_preproc.v" 0 +`line 403 "t/t_preproc.v" 0 //`ifndef def_fooed_2 `error "No def_fooed_2" `endif EXP: This is fooed @@ -644,7 +649,7 @@ This is fooed EXP: This is fooed_2 This is fooed_2 -`line 406 "t/t_preproc.v" 0 +`line 410 "t/t_preproc.v" 0 //====================================================================== np @@ -656,11 +661,11 @@ np -`line 417 "t/t_preproc.v" 0 +`line 421 "t/t_preproc.v" 0 -`line 420 "t/t_preproc.v" 0 +`line 424 "t/t_preproc.v" 0 //====================================================================== // Metaprogramming @@ -669,12 +674,12 @@ np -`line 428 "t/t_preproc.v" 0 +`line 432 "t/t_preproc.v" 0 -`line 432 "t/t_preproc.v" 0 +`line 436 "t/t_preproc.v" 0 hello3hello3hello3 hello4hello4hello4hello4 //====================================================================== @@ -682,7 +687,7 @@ hello4hello4hello4hello4 -`line 438 "t/t_preproc.v" 0 +`line 442 "t/t_preproc.v" 0 `line 1 "t/t_preproc_inc4.vh" 1 // DESCRIPTION: Verilog::Preproc: Example source code `line 2 "t/t_preproc_inc4.vh" 0 @@ -694,9 +699,9 @@ hello4hello4hello4hello4 `line 8 "t/t_preproc_inc4.vh" 2 -`line 438 "t/t_preproc.v" 0 +`line 442 "t/t_preproc.v" 0 -`line 439 "t/t_preproc.v" 0 +`line 443 "t/t_preproc.v" 0 //====================================================================== // Defines doing defines @@ -706,28 +711,28 @@ hello4hello4hello4hello4 -`line 447 "t/t_preproc.v" 0 +`line 451 "t/t_preproc.v" 0 -Line_Preproc_Check 451 +Line_Preproc_Check 455 //====================================================================== // Quoted multiline - track line numbers, and ensure \\n gets propagated -Line_Preproc_Check 457 +Line_Preproc_Check 461 "FOO \ BAR " "arg_line1 \ arg_line2" "FOO \ BAR " -`line 460 "t/t_preproc.v" 0 -Line_Preproc_Check 460 +`line 464 "t/t_preproc.v" 0 +Line_Preproc_Check 464 //====================================================================== // bug283 -`line 464 "t/t_preproc.v" 0 +`line 468 "t/t_preproc.v" 0 @@ -738,14 +743,14 @@ abc -`line 474 "t/t_preproc.v" 0 +`line 478 "t/t_preproc.v" 0 EXP: sonet_frame sonet_frame -`line 480 "t/t_preproc.v" 0 +`line 484 "t/t_preproc.v" 0 EXP: sonet_frame @@ -756,7 +761,7 @@ sonet_frame EXP: sonet_frame sonet_frame -`line 490 "t/t_preproc.v" 0 +`line 494 "t/t_preproc.v" 0 // The existance of non-existance of a base define can make a difference @@ -764,13 +769,13 @@ EXP: module zzz ; endmodule module zzz ; endmodule module zzz ; endmodule -`line 497 "t/t_preproc.v" 0 +`line 501 "t/t_preproc.v" 0 EXP: module a_b ; endmodule module a_b ; endmodule module a_b ; endmodule -`line 502 "t/t_preproc.v" 0 +`line 506 "t/t_preproc.v" 0 //====================================================================== // bug311 integer/*NEED_SPACE*/ foo; @@ -784,7 +789,7 @@ module t; initial begin : \`LEX_CAT(a[0],_assignment) -`line 514 "t/t_preproc.v" 0 +`line 518 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\`LEX_CAT(a[0],_assignment) "); end //----- // SHOULD(simulator-dependant): Backslash doesn't prevent arguments from @@ -793,7 +798,7 @@ module t; initial begin : \a[0]_assignment_a[1] -`line 521 "t/t_preproc.v" 0 +`line 525 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\a[0]_assignment_a[1] "); end //----- @@ -809,7 +814,7 @@ module t; // Similar to above; \ does not allow expansion after substitution initial begin : \`CAT(ff,bb) -`line 535 "t/t_preproc.v" 0 +`line 539 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\`CAT(ff,bb) "); end //----- @@ -817,7 +822,7 @@ module t; // MUST: Unknown macro with backslash escape stays as escaped symbol name initial begin : \`zzz -`line 541 "t/t_preproc.v" 0 +`line 545 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\`zzz "); end //----- @@ -826,11 +831,11 @@ module t; // SHOULD(simulator-dependant): Known macro with backslash escape expands initial begin : \`FOO -`line 548 "t/t_preproc.v" 0 +`line 552 "t/t_preproc.v" 0 $write("GOT%%m='%m' OTHER_EXP='%s'\n OUR_EXP='%s'", "t.bar ","t.\\`FOO "); end // SHOULD(simulator-dependant): Prefix breaks the above initial begin : \xx`FOO -`line 550 "t/t_preproc.v" 0 +`line 554 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\xx`FOO "); end //----- @@ -863,27 +868,27 @@ module t; initial -`line 581 "t/t_preproc.v" 0 +`line 585 "t/t_preproc.v" 0 $display("%s%s","a1","b2c3\n"); endmodule -`line 584 "t/t_preproc.v" 0 +`line 588 "t/t_preproc.v" 0 //====================================================================== //bug1225 -`line 587 "t/t_preproc.v" 0 +`line 591 "t/t_preproc.v" 0 $display("RAM0"); $display("CPU"); -`line 592 "t/t_preproc.v" 0 +`line 596 "t/t_preproc.v" 0 -`line 597 "t/t_preproc.v" 0 +`line 601 "t/t_preproc.v" 0 XXE_FAMILY = XXE_ @@ -891,7 +896,7 @@ XXE_FAMILY = XXE_ $display("XXE_ is defined"); -`line 604 "t/t_preproc.v" 0 +`line 608 "t/t_preproc.v" 0 XYE_FAMILY = XYE_ @@ -899,7 +904,7 @@ XYE_FAMILY = XYE_ $display("XYE_ is defined"); -`line 611 "t/t_preproc.v" 0 +`line 615 "t/t_preproc.v" 0 XXS_FAMILY = XXS_some @@ -907,7 +912,7 @@ XXS_FAMILY = XXS_some $display("XXS_some is defined"); -`line 618 "t/t_preproc.v" 0 +`line 622 "t/t_preproc.v" 0 XYS_FAMILY = XYS_foo @@ -915,10 +920,10 @@ XYS_FAMILY = XYS_foo $display("XYS_foo is defined"); -`line 625 "t/t_preproc.v" 0 +`line 629 "t/t_preproc.v" 0 //==== -`line 627 "t/t_preproc.v" 0 +`line 631 "t/t_preproc.v" 0 @@ -927,7 +932,7 @@ XYS_FAMILY = XYS_foo -`line 635 "t/t_preproc.v" 0 +`line 639 "t/t_preproc.v" 0 @@ -935,7 +940,7 @@ XYS_FAMILY = XYS_foo -`line 642 "t/t_preproc.v" 0 +`line 646 "t/t_preproc.v" 0 @@ -943,7 +948,7 @@ XYS_FAMILY = XYS_foo -`line 649 "t/t_preproc.v" 0 +`line 653 "t/t_preproc.v" 0 @@ -951,26 +956,26 @@ XYS_FAMILY = XYS_foo -`line 656 "t/t_preproc.v" 0 - - -`line 658 "t/t_preproc.v" 0 - // NEVER - `line 660 "t/t_preproc.v" 0 + + +`line 662 "t/t_preproc.v" 0 + // NEVER + +`line 664 "t/t_preproc.v" 0 //bug1227 (.mySig (myInterface.pa5), -`line 664 "t/t_preproc.v" 0 +`line 668 "t/t_preproc.v" 0 //====================================================================== // Stringify bug -`line 667 "t/t_preproc.v" 0 +`line 671 "t/t_preproc.v" 0 `dbg_hdl(UVM_LOW, ("Functional coverage enabled: paramgrp")); -`line 670 "t/t_preproc.v" 0 +`line 674 "t/t_preproc.v" 0 @@ -979,28 +984,28 @@ XYS_FAMILY = XYS_foo -`line 678 "t/t_preproc.v" 0 +`line 682 "t/t_preproc.v" 0 module pcc2_cfg; generate -`line 680 "t/t_preproc.v" 0 +`line 684 "t/t_preproc.v" 0 covergroup a @(posedge b); -`line 680 "t/t_preproc.v" 0 +`line 684 "t/t_preproc.v" 0 c: coverpoint d iff ((c) === 1'b1); endgroup -`line 680 "t/t_preproc.v" 0 +`line 684 "t/t_preproc.v" 0 a u_a; -`line 680 "t/t_preproc.v" 0 +`line 684 "t/t_preproc.v" 0 initial do begin $display ("DEBUG : %s [%m]", $sformatf ("Functional coverage enabled: u_a")); end while(0); endgenerate endmodule -`line 684 "t/t_preproc.v" 0 +`line 688 "t/t_preproc.v" 0 //====================================================================== // Verilog-Perl bug1668 "`NOT_DEFINED_STR" -`line 689 "t/t_preproc.v" 0 +`line 693 "t/t_preproc.v" 0 //====================================================================== // IEEE mandated predefines // undefineall should have no effect on these @@ -1023,4 +1028,4 @@ predef 2 2 // After `undefineall above, for testing --dump-defines -`line 711 "t/t_preproc.v" 2 +`line 715 "t/t_preproc.v" 2 diff --git a/test_regress/t/t_protect_ids_key.out b/test_regress/t/t_protect_ids_key.out index 7a7d53d4b..4575b30b0 100644 --- a/test_regress/t/t_protect_ids_key.out +++ b/test_regress/t/t_protect_ids_key.out @@ -22,8 +22,6 @@ - - diff --git a/test_regress/t/t_queue_empty_bad.out b/test_regress/t/t_queue_empty_bad.out new file mode 100755 index 000000000..a9762673b --- /dev/null +++ b/test_regress/t/t_queue_empty_bad.out @@ -0,0 +1,10 @@ +%Error-UNSUPPORTED: t/t_queue_empty_bad.v:11:11: Unsupported/Illegal: empty queue ('{}') in this context + : ... In instance t + 11 | i = {} + 1; + | ^ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_queue_empty_bad.v:13:9: Unsupported/Illegal: empty queue ('{}') in this assign context + : ... In instance t + 13 | i = {}; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_queue_empty_bad.pl b/test_regress/t/t_queue_empty_bad.pl new file mode 100755 index 000000000..a5846c699 --- /dev/null +++ b/test_regress/t/t_queue_empty_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(vlt => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_queue_empty_bad.v b/test_regress/t/t_queue_empty_bad.v new file mode 100644 index 000000000..09695b14a --- /dev/null +++ b/test_regress/t/t_queue_empty_bad.v @@ -0,0 +1,18 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2019 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + initial begin + int i; + + i = {} + 1; + + i = {}; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_queue_slice.v b/test_regress/t/t_queue_slice.v index 3ee6e2844..fde0ebd62 100644 --- a/test_regress/t/t_queue_slice.v +++ b/test_regress/t/t_queue_slice.v @@ -26,6 +26,9 @@ module t (/*AUTOARG*/); q = '{"q"}; v = $sformatf("%p", q); `checks(v, "'{\"q\"} "); + q = {}; + i = q.size(); `checkh(i, 0); + q = '{"q", "b", "c", "d", "e", "f"}; if (q[0] !== "q") $stop; v = $sformatf("%p", q); `checks(v, "'{\"q\", \"b\", \"c\", \"d\", \"e\", \"f\"} "); diff --git a/test_regress/t/t_split_var_1_bad.out b/test_regress/t/t_split_var_1_bad.out index 40dab2d0e..6249c2943 100644 --- a/test_regress/t/t_split_var_1_bad.out +++ b/test_regress/t/t_split_var_1_bad.out @@ -35,35 +35,35 @@ : ... In instance t.i_sub3 90 | assign outwires[12] = inwires[13]; | ^~ -%Warning-SPLITVAR: t/t_split_var_1_bad.v:17:9: 'should_show_warning0' has split_var metacomment but will not be split because it is not an aggregate type of bit nor logic +%Warning-SPLITVAR: t/t_split_var_1_bad.v:17:9: 'should_show_warning0' has split_var metacomment but will not be split because it is not an aggregate type of bit nor logic. : ... In instance t 17 | real should_show_warning0 /*verilator split_var*/ ; | ^~~~~~~~~~~~~~~~~~~~ -%Warning-SPLITVAR: t/t_split_var_1_bad.v:18:11: 'should_show_warning1' has split_var metacomment but will not be split because it is not an aggregate type of bit nor logic +%Warning-SPLITVAR: t/t_split_var_1_bad.v:18:11: 'should_show_warning1' has split_var metacomment but will not be split because it is not an aggregate type of bit nor logic. : ... In instance t 18 | string should_show_warning1 /*verilator split_var*/ ; | ^~~~~~~~~~~~~~~~~~~~ -%Warning-SPLITVAR: t/t_split_var_1_bad.v:19:11: 'should_show_warning2' has split_var metacomment but will not be split because its bitwidth is 1 +%Warning-SPLITVAR: t/t_split_var_1_bad.v:19:11: 'should_show_warning2' has split_var metacomment but will not be split because its bitwidth is 1. : ... In instance t 19 | wire should_show_warning2 /*verilator split_var*/ ; | ^~~~~~~~~~~~~~~~~~~~ -%Warning-SPLITVAR: t/t_split_var_1_bad.v:23:16: 'public_signal' has split_var metacomment but will not be split because it is public +%Warning-SPLITVAR: t/t_split_var_1_bad.v:23:16: 'public_signal' has split_var metacomment but will not be split because it is public. : ... In instance t 23 | logic [1:0] public_signal /*verilator public*/ /*verilator split_var*/ ; | ^~~~~~~~~~~~~ -%Warning-SPLITVAR: t/t_split_var_1_bad.v:31:44: 'inout_port' has split_var metacomment but will not be split because it is an inout port +%Warning-SPLITVAR: t/t_split_var_1_bad.v:31:44: 'inout_port' has split_var metacomment but will not be split because it is an inout port. : ... In instance t 31 | function int bad_func(inout logic [3:0] inout_port /*verilator split_var*/ , | ^~~~~~~~~~ -%Warning-SPLITVAR: t/t_split_var_1_bad.v:32:42: 'ref_port' has split_var metacomment but will not be split because it is a ref argument +%Warning-SPLITVAR: t/t_split_var_1_bad.v:32:42: 'ref_port' has split_var metacomment but will not be split because it is a ref argument. : ... In instance t 32 | ref logic [7:0] ref_port /*verilator split_var*/ ); | ^~~~~~~~ -%Warning-SPLITVAR: t/t_split_var_1_bad.v:37:19: 'loop_idx' has split_var metacomment but will not be split because it is used as a loop variable +%Warning-SPLITVAR: t/t_split_var_1_bad.v:37:19: 'loop_idx' has split_var metacomment but will not be split because it is used as a loop variable. : ... In instance t 37 | logic [7:0] loop_idx /*verilator split_var*/ ; | ^~~~~~~~ -%Warning-SPLITVAR: t/t_split_var_1_bad.v:62:11: 'cannot_split_genvar' has split_var metacomment but will not be split because it is not an aggregate type of bit nor logic +%Warning-SPLITVAR: t/t_split_var_1_bad.v:62:11: 'cannot_split_genvar' has split_var metacomment but will not be split because it is not an aggregate type of bit nor logic. : ... In instance t.i_sub1 62 | genvar cannot_split_genvar /*verilator split_var*/ ; | ^~~~~~~~~~~~~~~~~~~ diff --git a/test_regress/t/t_split_var_2_trace.out b/test_regress/t/t_split_var_2_trace.out index 7bbee9244..f03a9303f 100644 --- a/test_regress/t/t_split_var_2_trace.out +++ b/test_regress/t/t_split_var_2_trace.out @@ -1,28 +1,27 @@ $version Generated by VerilatedVcd $end -$date Mon Aug 24 21:54:18 2020 - $end -$timescale 1ps $end +$date Wed Aug 11 12:35:42 2021 $end +$timescale 1ps $end $scope module top $end - $var wire 1 U" clk $end + $var wire 1 T" clk $end $scope module t $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 X" NUMSUB [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 1 U" clk $end - $var wire 64 Z" expc [63:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 x! out(0) [7:0] $end - $var wire 8 y! out(1) [7:0] $end - $var wire 8 z! out(2) [7:0] $end - $var wire 8 {! out(3) [7:0] $end - $var wire 8 |! out(4) [7:0] $end - $var wire 8 }! out(5) [7:0] $end - $var wire 8 ~! out(6) [7:0] $end - $var wire 8 !" out(7) [7:0] $end - $var wire 8 "" out(8) [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 W" NUMSUB [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 1 T" clk $end + $var wire 64 Y" expc [63:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 w! out[0] [7:0] $end + $var wire 8 x! out[1] [7:0] $end + $var wire 8 y! out[2] [7:0] $end + $var wire 8 z! out[3] [7:0] $end + $var wire 8 {! out[4] [7:0] $end + $var wire 8 |! out[5] [7:0] $end + $var wire 8 }! out[6] [7:0] $end + $var wire 8 ~! out[7] [7:0] $end + $var wire 8 !" out[8] [7:0] $end $var wire 3 - shift [2:0] $end - $var wire 8 $" through_tmp [7:0] $end + $var wire 8 #" through_tmp [7:0] $end $scope module always_block $end $var wire 1 . failed $end $scope module unnamedblk1 $end @@ -30,43 +29,43 @@ $timescale 1ps $end $upscope $end $upscope $end $scope module delay0 $end - $var wire 32 u! c [31:0] $end - $var wire 1 U" clk $end - $var wire 1 z" unpack_sig0(10) $end - $var wire 1 {" unpack_sig0(11) $end - $var wire 1 |" unpack_sig0(12) $end - $var wire 1 e! unpack_sig0(13) $end - $var wire 1 f! unpack_sig0(14) $end - $var wire 1 g! unpack_sig0(15) $end - $var wire 1 h! unpack_sig0(16) $end - $var wire 1 i! unpack_sig1(13) $end - $var wire 1 j! unpack_sig1(14) $end - $var wire 1 k! unpack_sig1(15) $end - $var wire 1 l! unpack_sig1(16) $end - $var wire 1 }" unpack_sig2(10) $end - $var wire 1 ~" unpack_sig2(11) $end - $var wire 1 !# unpack_sig2(12) $end - $var wire 1 m! unpack_sig2(13) $end - $var wire 1 n! unpack_sig2(14) $end - $var wire 1 o! unpack_sig2(15) $end - $var wire 1 p! unpack_sig2(16) $end - $var wire 1 q! unpack_sig3(13) $end - $var wire 1 r! unpack_sig3(14) $end - $var wire 1 s! unpack_sig3(15) $end - $var wire 1 t! unpack_sig3(16) $end + $var wire 32 t! c [31:0] $end + $var wire 1 T" clk $end + $var wire 1 y" unpack_sig0(10) $end + $var wire 1 z" unpack_sig0(11) $end + $var wire 1 {" unpack_sig0(12) $end + $var wire 1 d! unpack_sig0(13) $end + $var wire 1 e! unpack_sig0(14) $end + $var wire 1 f! unpack_sig0(15) $end + $var wire 1 g! unpack_sig0(16) $end + $var wire 1 h! unpack_sig1(13) $end + $var wire 1 i! unpack_sig1(14) $end + $var wire 1 j! unpack_sig1(15) $end + $var wire 1 k! unpack_sig1(16) $end + $var wire 1 |" unpack_sig2(10) $end + $var wire 1 }" unpack_sig2(11) $end + $var wire 1 ~" unpack_sig2(12) $end + $var wire 1 l! unpack_sig2(13) $end + $var wire 1 m! unpack_sig2(14) $end + $var wire 1 n! unpack_sig2(15) $end + $var wire 1 o! unpack_sig2(16) $end + $var wire 1 p! unpack_sig3(13) $end + $var wire 1 q! unpack_sig3(14) $end + $var wire 1 r! unpack_sig3(15) $end + $var wire 1 s! unpack_sig3(16) $end $upscope $end $scope module i_t_array_rev $end $var wire 1 ' arrd(0) $end $var wire 1 ( arrd(1) $end - $var wire 1 U" clk $end - $var wire 32 "# cyc [31:0] $end - $var wire 1 v! localbkw(0) $end - $var wire 1 w! localbkw(1) $end + $var wire 1 T" clk $end + $var wire 32 !# cyc [31:0] $end + $var wire 1 u! localbkw(0) $end + $var wire 1 v! localbkw(1) $end $var wire 1 ) y0 $end $var wire 1 * y1 $end $scope module arr_rev_u $end - $var wire 1 + arrbkw(0) $end - $var wire 1 , arrbkw(1) $end + $var wire 1 + arrbkw[0] $end + $var wire 1 , arrbkw[1] $end $var wire 1 ) y0 $end $var wire 1 * y1 $end $upscope $end @@ -78,312 +77,312 @@ $timescale 1ps $end $var wire 32 & var3 [30:-1] $end $upscope $end $scope module shifter0 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 \" OFFSET [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 [" OFFSET [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end $var wire 8 0 out [7:0] $end $var wire 3 - shift [2:0] $end $var wire 8 2 tmp(-1) [7:0] $end $var wire 8 1 tmp(-2) [7:0] $end - $var wire 8 Y" tmp(-3) [7:0] $end - $var wire 8 3 tmp(0) [7:0] $end + $var wire 8 X" tmp(-3) [7:0] $end + $var wire 8 0 tmp(0) [7:0] $end $upscope $end $scope module shifter1 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 \" OFFSET [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 4 out [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 [" OFFSET [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 3 out [7:0] $end $var wire 3 - shift [2:0] $end - $var wire 8 5 tmp(-1) [7:0] $end + $var wire 8 4 tmp(-1) [7:0] $end $var wire 8 1 tmp(-2) [7:0] $end - $var wire 8 Y" tmp(-3) [7:0] $end - $var wire 8 4 tmp(0) [7:0] $end + $var wire 8 X" tmp(-3) [7:0] $end + $var wire 8 3 tmp(0) [7:0] $end $upscope $end $scope module shifter2 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 ]" OFFSET [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 6 out [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 \" OFFSET [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 5 out [7:0] $end $var wire 3 - shift [2:0] $end - $var wire 8 Y" tmp(1) [7:0] $end - $var wire 8 7 tmp(2) [7:0] $end - $var wire 8 8 tmp(3) [7:0] $end - $var wire 8 6 tmp(4) [7:0] $end + $var wire 8 X" tmp(1) [7:0] $end + $var wire 8 6 tmp(2) [7:0] $end + $var wire 8 7 tmp(3) [7:0] $end + $var wire 8 5 tmp(4) [7:0] $end $upscope $end $scope module shifter3 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 V" N [31:0] $end - $var wire 32 ]" OFFSET [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 %" out [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 U" N [31:0] $end + $var wire 32 \" OFFSET [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 $" out [7:0] $end $var wire 3 - shift [2:0] $end - $var wire 8 Y" tmp0(1)(1) [7:0] $end - $var wire 8 Y" tmp0(1)(2) [7:0] $end - $var wire 8 Y" tmp0(1)(3) [7:0] $end + $var wire 8 X" tmp0(1)(1) [7:0] $end + $var wire 8 X" tmp0(1)(2) [7:0] $end + $var wire 8 X" tmp0(1)(3) [7:0] $end $var wire 8 1 tmp0(2)(1) [7:0] $end $var wire 8 1 tmp0(2)(2) [7:0] $end $var wire 8 1 tmp0(2)(3) [7:0] $end - $var wire 8 9 tmp0(3)(1) [7:0] $end - $var wire 8 : tmp0(3)(2) [7:0] $end - $var wire 8 ; tmp0(3)(3) [7:0] $end - $var wire 8 < tmp0(4)(1) [7:0] $end - $var wire 8 = tmp0(4)(2) [7:0] $end - $var wire 8 > tmp0(4)(3) [7:0] $end - $var wire 8 ? tmp1(1)(1) [7:0] $end - $var wire 8 @ tmp1(1)(2) [7:0] $end - $var wire 8 A tmp1(1)(3) [7:0] $end - $var wire 8 B tmp1(2)(1) [7:0] $end - $var wire 8 C tmp1(2)(2) [7:0] $end - $var wire 8 D tmp1(2)(3) [7:0] $end - $var wire 8 E tmp1(3)(1) [7:0] $end - $var wire 8 F tmp1(3)(2) [7:0] $end - $var wire 8 G tmp1(3)(3) [7:0] $end - $var wire 8 H tmp1(4)(1) [7:0] $end - $var wire 8 I tmp1(4)(2) [7:0] $end - $var wire 8 J tmp1(4)(3) [7:0] $end - $var wire 8 M! tmp10(1)(1) [7:0] $end - $var wire 8 N! tmp10(1)(2) [7:0] $end - $var wire 8 O! tmp10(1)(3) [7:0] $end - $var wire 8 P! tmp10(2)(1) [7:0] $end - $var wire 8 Q! tmp10(2)(2) [7:0] $end - $var wire 8 R! tmp10(2)(3) [7:0] $end - $var wire 8 S! tmp10(3)(1) [7:0] $end - $var wire 8 T! tmp10(3)(2) [7:0] $end - $var wire 8 U! tmp10(3)(3) [7:0] $end - $var wire 8 V! tmp10(4)(1) [7:0] $end - $var wire 8 W! tmp10(4)(2) [7:0] $end - $var wire 8 X! tmp10(4)(3) [7:0] $end - $var wire 8 &" tmp12(-1)(1)(1) [7:0] $end - $var wire 8 '" tmp12(-1)(1)(2) [7:0] $end - $var wire 8 (" tmp12(-1)(1)(3) [7:0] $end - $var wire 8 )" tmp12(-1)(2)(1) [7:0] $end - $var wire 8 *" tmp12(-1)(2)(2) [7:0] $end - $var wire 8 +" tmp12(-1)(2)(3) [7:0] $end - $var wire 8 ," tmp12(-1)(3)(1) [7:0] $end - $var wire 8 -" tmp12(-1)(3)(2) [7:0] $end - $var wire 8 ." tmp12(-1)(3)(3) [7:0] $end - $var wire 8 %" tmp12(-1)(4)(1) [7:0] $end - $var wire 8 /" tmp12(-1)(4)(2) [7:0] $end - $var wire 8 0" tmp12(-1)(4)(3) [7:0] $end - $var wire 8 1" tmp12(0)(1)(1) [7:0] $end - $var wire 8 2" tmp12(0)(1)(2) [7:0] $end - $var wire 8 3" tmp12(0)(1)(3) [7:0] $end - $var wire 8 4" tmp12(0)(2)(1) [7:0] $end - $var wire 8 5" tmp12(0)(2)(2) [7:0] $end - $var wire 8 6" tmp12(0)(2)(3) [7:0] $end - $var wire 8 7" tmp12(0)(3)(1) [7:0] $end - $var wire 8 8" tmp12(0)(3)(2) [7:0] $end - $var wire 8 9" tmp12(0)(3)(3) [7:0] $end - $var wire 8 :" tmp12(0)(4)(1) [7:0] $end - $var wire 8 ;" tmp12(0)(4)(2) [7:0] $end - $var wire 8 <" tmp12(0)(4)(3) [7:0] $end - $var wire 8 j" tmp13(1)(1) [7:0] $end - $var wire 8 k" tmp13(1)(2) [7:0] $end - $var wire 8 l" tmp13(1)(3) [7:0] $end - $var wire 8 m" tmp13(2)(1) [7:0] $end - $var wire 8 n" tmp13(2)(2) [7:0] $end - $var wire 8 o" tmp13(2)(3) [7:0] $end - $var wire 8 p" tmp13(3)(1) [7:0] $end - $var wire 8 q" tmp13(3)(2) [7:0] $end - $var wire 8 r" tmp13(3)(3) [7:0] $end - $var wire 8 s" tmp13(4)(1) [7:0] $end - $var wire 8 t" tmp13(4)(2) [7:0] $end - $var wire 8 u" tmp13(4)(3) [7:0] $end - $var wire 8 K tmp2(1)(1) [7:0] $end - $var wire 8 L tmp2(1)(2) [7:0] $end - $var wire 8 M tmp2(1)(3) [7:0] $end - $var wire 8 N tmp2(2)(1) [7:0] $end - $var wire 8 O tmp2(2)(2) [7:0] $end - $var wire 8 P tmp2(2)(3) [7:0] $end - $var wire 8 Q tmp2(3)(1) [7:0] $end - $var wire 8 R tmp2(3)(2) [7:0] $end - $var wire 8 S tmp2(3)(3) [7:0] $end - $var wire 8 T tmp2(4)(1) [7:0] $end - $var wire 8 U tmp2(4)(2) [7:0] $end - $var wire 8 V tmp2(4)(3) [7:0] $end - $var wire 8 W tmp3(1)(1) [7:0] $end - $var wire 8 X tmp3(1)(2) [7:0] $end - $var wire 8 Y tmp3(1)(3) [7:0] $end - $var wire 8 Z tmp3(2)(1) [7:0] $end - $var wire 8 [ tmp3(2)(2) [7:0] $end - $var wire 8 \ tmp3(2)(3) [7:0] $end - $var wire 8 ] tmp3(3)(1) [7:0] $end - $var wire 8 ^ tmp3(3)(2) [7:0] $end - $var wire 8 _ tmp3(3)(3) [7:0] $end - $var wire 8 ` tmp3(4)(1) [7:0] $end - $var wire 8 a tmp3(4)(2) [7:0] $end - $var wire 8 b tmp3(4)(3) [7:0] $end - $var wire 8 c tmp4(1)(1) [7:0] $end - $var wire 8 d tmp4(1)(2) [7:0] $end - $var wire 8 e tmp4(1)(3) [7:0] $end - $var wire 8 f tmp4(2)(1) [7:0] $end - $var wire 8 g tmp4(2)(2) [7:0] $end - $var wire 8 h tmp4(2)(3) [7:0] $end - $var wire 8 i tmp4(3)(1) [7:0] $end - $var wire 8 j tmp4(3)(2) [7:0] $end - $var wire 8 k tmp4(3)(3) [7:0] $end - $var wire 8 l tmp4(4)(1) [7:0] $end - $var wire 8 m tmp4(4)(2) [7:0] $end - $var wire 8 n tmp4(4)(3) [7:0] $end - $var wire 8 o tmp5(1)(1) [7:0] $end - $var wire 8 p tmp5(1)(2) [7:0] $end - $var wire 8 q tmp5(1)(3) [7:0] $end - $var wire 8 r tmp5(2)(1) [7:0] $end - $var wire 8 s tmp5(2)(2) [7:0] $end - $var wire 8 t tmp5(2)(3) [7:0] $end - $var wire 8 u tmp5(3)(1) [7:0] $end - $var wire 8 v tmp5(3)(2) [7:0] $end - $var wire 8 w tmp5(3)(3) [7:0] $end - $var wire 8 x tmp5(4)(1) [7:0] $end - $var wire 8 y tmp5(4)(2) [7:0] $end - $var wire 8 z tmp5(4)(3) [7:0] $end - $var wire 8 { tmp6(1)(1) [7:0] $end - $var wire 8 | tmp6(1)(2) [7:0] $end - $var wire 8 } tmp6(1)(3) [7:0] $end - $var wire 8 ~ tmp6(2)(1) [7:0] $end - $var wire 8 !! tmp6(2)(2) [7:0] $end - $var wire 8 "! tmp6(2)(3) [7:0] $end - $var wire 8 #! tmp6(3)(1) [7:0] $end - $var wire 8 $! tmp6(3)(2) [7:0] $end - $var wire 8 %! tmp6(3)(3) [7:0] $end - $var wire 8 &! tmp6(4)(1) [7:0] $end - $var wire 8 '! tmp6(4)(2) [7:0] $end - $var wire 8 (! tmp6(4)(3) [7:0] $end - $var wire 8 )! tmp7(2)(1) [7:0] $end - $var wire 8 *! tmp7(2)(2) [7:0] $end - $var wire 8 +! tmp7(2)(3) [7:0] $end - $var wire 8 ,! tmp7(3)(1) [7:0] $end - $var wire 8 -! tmp7(3)(2) [7:0] $end - $var wire 8 .! tmp7(3)(3) [7:0] $end - $var wire 8 /! tmp7(4)(1) [7:0] $end - $var wire 8 0! tmp7(4)(2) [7:0] $end - $var wire 8 1! tmp7(4)(3) [7:0] $end - $var wire 8 2! tmp7(5)(1) [7:0] $end - $var wire 8 3! tmp7(5)(2) [7:0] $end - $var wire 8 4! tmp7(5)(3) [7:0] $end - $var wire 8 ^" tmp8(0)(1) [7:0] $end - $var wire 8 _" tmp8(0)(2) [7:0] $end - $var wire 8 `" tmp8(0)(3) [7:0] $end - $var wire 8 a" tmp8(1)(1) [7:0] $end - $var wire 8 b" tmp8(1)(2) [7:0] $end - $var wire 8 c" tmp8(1)(3) [7:0] $end - $var wire 8 5! tmp8(2)(1) [7:0] $end - $var wire 8 6! tmp8(2)(2) [7:0] $end - $var wire 8 7! tmp8(2)(3) [7:0] $end - $var wire 8 8! tmp8(3)(1) [7:0] $end - $var wire 8 9! tmp8(3)(2) [7:0] $end - $var wire 8 :! tmp8(3)(3) [7:0] $end - $var wire 8 ;! tmp8(4)(1) [7:0] $end - $var wire 8 ! tmp8(5)(1) [7:0] $end - $var wire 8 ?! tmp8(5)(2) [7:0] $end - $var wire 8 @! tmp8(5)(3) [7:0] $end - $var wire 8 d" tmp8(6)(1) [7:0] $end - $var wire 8 e" tmp8(6)(2) [7:0] $end - $var wire 8 f" tmp8(6)(3) [7:0] $end - $var wire 8 g" tmp8(7)(1) [7:0] $end - $var wire 8 h" tmp8(7)(2) [7:0] $end - $var wire 8 i" tmp8(7)(3) [7:0] $end - $var wire 8 A! tmp9(4)(1) [7:0] $end - $var wire 8 B! tmp9(4)(2) [7:0] $end - $var wire 8 C! tmp9(4)(3) [7:0] $end - $var wire 8 D! tmp9(5)(1) [7:0] $end - $var wire 8 E! tmp9(5)(2) [7:0] $end - $var wire 8 F! tmp9(5)(3) [7:0] $end - $var wire 8 G! tmp9(6)(1) [7:0] $end - $var wire 8 H! tmp9(6)(2) [7:0] $end - $var wire 8 I! tmp9(6)(3) [7:0] $end - $var wire 8 J! tmp9(7)(1) [7:0] $end - $var wire 8 K! tmp9(7)(2) [7:0] $end - $var wire 8 L! tmp9(7)(3) [7:0] $end + $var wire 8 8 tmp0(3)(1) [7:0] $end + $var wire 8 9 tmp0(3)(2) [7:0] $end + $var wire 8 : tmp0(3)(3) [7:0] $end + $var wire 8 ; tmp0(4)(1) [7:0] $end + $var wire 8 < tmp0(4)(2) [7:0] $end + $var wire 8 = tmp0(4)(3) [7:0] $end + $var wire 8 > tmp1(1)(1) [7:0] $end + $var wire 8 ? tmp1(1)(2) [7:0] $end + $var wire 8 @ tmp1(1)(3) [7:0] $end + $var wire 8 A tmp1(2)(1) [7:0] $end + $var wire 8 B tmp1(2)(2) [7:0] $end + $var wire 8 C tmp1(2)(3) [7:0] $end + $var wire 8 D tmp1(3)(1) [7:0] $end + $var wire 8 E tmp1(3)(2) [7:0] $end + $var wire 8 F tmp1(3)(3) [7:0] $end + $var wire 8 G tmp1(4)(1) [7:0] $end + $var wire 8 H tmp1(4)(2) [7:0] $end + $var wire 8 I tmp1(4)(3) [7:0] $end + $var wire 8 L! tmp10(1)(1) [7:0] $end + $var wire 8 M! tmp10(1)(2) [7:0] $end + $var wire 8 N! tmp10(1)(3) [7:0] $end + $var wire 8 O! tmp10(2)(1) [7:0] $end + $var wire 8 P! tmp10(2)(2) [7:0] $end + $var wire 8 Q! tmp10(2)(3) [7:0] $end + $var wire 8 R! tmp10(3)(1) [7:0] $end + $var wire 8 S! tmp10(3)(2) [7:0] $end + $var wire 8 T! tmp10(3)(3) [7:0] $end + $var wire 8 U! tmp10(4)(1) [7:0] $end + $var wire 8 V! tmp10(4)(2) [7:0] $end + $var wire 8 W! tmp10(4)(3) [7:0] $end + $var wire 8 %" tmp12(-1)(1)(1) [7:0] $end + $var wire 8 &" tmp12(-1)(1)(2) [7:0] $end + $var wire 8 '" tmp12(-1)(1)(3) [7:0] $end + $var wire 8 (" tmp12(-1)(2)(1) [7:0] $end + $var wire 8 )" tmp12(-1)(2)(2) [7:0] $end + $var wire 8 *" tmp12(-1)(2)(3) [7:0] $end + $var wire 8 +" tmp12(-1)(3)(1) [7:0] $end + $var wire 8 ," tmp12(-1)(3)(2) [7:0] $end + $var wire 8 -" tmp12(-1)(3)(3) [7:0] $end + $var wire 8 $" tmp12(-1)(4)(1) [7:0] $end + $var wire 8 ." tmp12(-1)(4)(2) [7:0] $end + $var wire 8 /" tmp12(-1)(4)(3) [7:0] $end + $var wire 8 0" tmp12(0)(1)(1) [7:0] $end + $var wire 8 1" tmp12(0)(1)(2) [7:0] $end + $var wire 8 2" tmp12(0)(1)(3) [7:0] $end + $var wire 8 3" tmp12(0)(2)(1) [7:0] $end + $var wire 8 4" tmp12(0)(2)(2) [7:0] $end + $var wire 8 5" tmp12(0)(2)(3) [7:0] $end + $var wire 8 6" tmp12(0)(3)(1) [7:0] $end + $var wire 8 7" tmp12(0)(3)(2) [7:0] $end + $var wire 8 8" tmp12(0)(3)(3) [7:0] $end + $var wire 8 9" tmp12(0)(4)(1) [7:0] $end + $var wire 8 :" tmp12(0)(4)(2) [7:0] $end + $var wire 8 ;" tmp12(0)(4)(3) [7:0] $end + $var wire 8 i" tmp13(1)(1) [7:0] $end + $var wire 8 j" tmp13(1)(2) [7:0] $end + $var wire 8 k" tmp13(1)(3) [7:0] $end + $var wire 8 l" tmp13(2)(1) [7:0] $end + $var wire 8 m" tmp13(2)(2) [7:0] $end + $var wire 8 n" tmp13(2)(3) [7:0] $end + $var wire 8 o" tmp13(3)(1) [7:0] $end + $var wire 8 p" tmp13(3)(2) [7:0] $end + $var wire 8 q" tmp13(3)(3) [7:0] $end + $var wire 8 r" tmp13(4)(1) [7:0] $end + $var wire 8 s" tmp13(4)(2) [7:0] $end + $var wire 8 t" tmp13(4)(3) [7:0] $end + $var wire 8 J tmp2[1][1] [7:0] $end + $var wire 8 K tmp2[1][2] [7:0] $end + $var wire 8 L tmp2[1][3] [7:0] $end + $var wire 8 M tmp2[2][1] [7:0] $end + $var wire 8 N tmp2[2][2] [7:0] $end + $var wire 8 O tmp2[2][3] [7:0] $end + $var wire 8 P tmp2[3][1] [7:0] $end + $var wire 8 Q tmp2[3][2] [7:0] $end + $var wire 8 R tmp2[3][3] [7:0] $end + $var wire 8 S tmp2[4][1] [7:0] $end + $var wire 8 T tmp2[4][2] [7:0] $end + $var wire 8 U tmp2[4][3] [7:0] $end + $var wire 8 V tmp3(1)(1) [7:0] $end + $var wire 8 W tmp3(1)(2) [7:0] $end + $var wire 8 X tmp3(1)(3) [7:0] $end + $var wire 8 Y tmp3(2)(1) [7:0] $end + $var wire 8 Z tmp3(2)(2) [7:0] $end + $var wire 8 [ tmp3(2)(3) [7:0] $end + $var wire 8 \ tmp3(3)(1) [7:0] $end + $var wire 8 ] tmp3(3)(2) [7:0] $end + $var wire 8 ^ tmp3(3)(3) [7:0] $end + $var wire 8 _ tmp3(4)(1) [7:0] $end + $var wire 8 ` tmp3(4)(2) [7:0] $end + $var wire 8 a tmp3(4)(3) [7:0] $end + $var wire 8 b tmp4(1)(1) [7:0] $end + $var wire 8 c tmp4(1)(2) [7:0] $end + $var wire 8 d tmp4(1)(3) [7:0] $end + $var wire 8 e tmp4(2)(1) [7:0] $end + $var wire 8 f tmp4(2)(2) [7:0] $end + $var wire 8 g tmp4(2)(3) [7:0] $end + $var wire 8 h tmp4(3)(1) [7:0] $end + $var wire 8 i tmp4(3)(2) [7:0] $end + $var wire 8 j tmp4(3)(3) [7:0] $end + $var wire 8 k tmp4(4)(1) [7:0] $end + $var wire 8 l tmp4(4)(2) [7:0] $end + $var wire 8 m tmp4(4)(3) [7:0] $end + $var wire 8 n tmp5[1][1] [7:0] $end + $var wire 8 o tmp5[1][2] [7:0] $end + $var wire 8 p tmp5[1][3] [7:0] $end + $var wire 8 q tmp5[2][1] [7:0] $end + $var wire 8 r tmp5[2][2] [7:0] $end + $var wire 8 s tmp5[2][3] [7:0] $end + $var wire 8 t tmp5[3][1] [7:0] $end + $var wire 8 u tmp5[3][2] [7:0] $end + $var wire 8 v tmp5[3][3] [7:0] $end + $var wire 8 w tmp5[4][1] [7:0] $end + $var wire 8 x tmp5[4][2] [7:0] $end + $var wire 8 y tmp5[4][3] [7:0] $end + $var wire 8 z tmp6(1)(1) [7:0] $end + $var wire 8 { tmp6(1)(2) [7:0] $end + $var wire 8 | tmp6(1)(3) [7:0] $end + $var wire 8 } tmp6(2)(1) [7:0] $end + $var wire 8 ~ tmp6(2)(2) [7:0] $end + $var wire 8 !! tmp6(2)(3) [7:0] $end + $var wire 8 "! tmp6(3)(1) [7:0] $end + $var wire 8 #! tmp6(3)(2) [7:0] $end + $var wire 8 $! tmp6(3)(3) [7:0] $end + $var wire 8 %! tmp6(4)(1) [7:0] $end + $var wire 8 &! tmp6(4)(2) [7:0] $end + $var wire 8 '! tmp6(4)(3) [7:0] $end + $var wire 8 (! tmp7(2)(1) [7:0] $end + $var wire 8 )! tmp7(2)(2) [7:0] $end + $var wire 8 *! tmp7(2)(3) [7:0] $end + $var wire 8 +! tmp7(3)(1) [7:0] $end + $var wire 8 ,! tmp7(3)(2) [7:0] $end + $var wire 8 -! tmp7(3)(3) [7:0] $end + $var wire 8 .! tmp7(4)(1) [7:0] $end + $var wire 8 /! tmp7(4)(2) [7:0] $end + $var wire 8 0! tmp7(4)(3) [7:0] $end + $var wire 8 1! tmp7(5)(1) [7:0] $end + $var wire 8 2! tmp7(5)(2) [7:0] $end + $var wire 8 3! tmp7(5)(3) [7:0] $end + $var wire 8 ]" tmp8(0)(1) [7:0] $end + $var wire 8 ^" tmp8(0)(2) [7:0] $end + $var wire 8 _" tmp8(0)(3) [7:0] $end + $var wire 8 `" tmp8(1)(1) [7:0] $end + $var wire 8 a" tmp8(1)(2) [7:0] $end + $var wire 8 b" tmp8(1)(3) [7:0] $end + $var wire 8 4! tmp8(2)(1) [7:0] $end + $var wire 8 5! tmp8(2)(2) [7:0] $end + $var wire 8 6! tmp8(2)(3) [7:0] $end + $var wire 8 7! tmp8(3)(1) [7:0] $end + $var wire 8 8! tmp8(3)(2) [7:0] $end + $var wire 8 9! tmp8(3)(3) [7:0] $end + $var wire 8 :! tmp8(4)(1) [7:0] $end + $var wire 8 ;! tmp8(4)(2) [7:0] $end + $var wire 8 ! tmp8(5)(2) [7:0] $end + $var wire 8 ?! tmp8(5)(3) [7:0] $end + $var wire 8 c" tmp8(6)(1) [7:0] $end + $var wire 8 d" tmp8(6)(2) [7:0] $end + $var wire 8 e" tmp8(6)(3) [7:0] $end + $var wire 8 f" tmp8(7)(1) [7:0] $end + $var wire 8 g" tmp8(7)(2) [7:0] $end + $var wire 8 h" tmp8(7)(3) [7:0] $end + $var wire 8 @! tmp9(4)(1) [7:0] $end + $var wire 8 A! tmp9(4)(2) [7:0] $end + $var wire 8 B! tmp9(4)(3) [7:0] $end + $var wire 8 C! tmp9(5)(1) [7:0] $end + $var wire 8 D! tmp9(5)(2) [7:0] $end + $var wire 8 E! tmp9(5)(3) [7:0] $end + $var wire 8 F! tmp9(6)(1) [7:0] $end + $var wire 8 G! tmp9(6)(2) [7:0] $end + $var wire 8 H! tmp9(6)(3) [7:0] $end + $var wire 8 I! tmp9(7)(1) [7:0] $end + $var wire 8 J! tmp9(7)(2) [7:0] $end + $var wire 8 K! tmp9(7)(3) [7:0] $end $upscope $end $scope module shifter4 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 v" OFFSET [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 Y! out [7:0] $end - $var wire 24 x" pad [23:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 u" OFFSET [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 X! out [7:0] $end + $var wire 24 w" pad [23:0] $end $var wire 3 - shift [2:0] $end - $var wire 32 w" tmp(2) [31:0] $end - $var wire 32 Z! tmp(3) [31:0] $end - $var wire 32 [! tmp(4) [31:0] $end - $var wire 32 \! tmp(5) [31:0] $end + $var wire 32 v" tmp(2) [31:0] $end + $var wire 32 Y! tmp(3) [31:0] $end + $var wire 32 Z! tmp(4) [31:0] $end + $var wire 32 [! tmp(5) [31:0] $end $upscope $end $scope module shifter5 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 y" OFFSET [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 ]! out [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 x" OFFSET [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 \! out [7:0] $end $var wire 3 - shift [2:0] $end - $var wire 32 ^! tmp [31:0] $end + $var wire 32 ]! tmp [31:0] $end $upscope $end $scope module shifter6 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 y" OFFSET [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 _! out [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 x" OFFSET [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 ^! out [7:0] $end $var wire 3 - shift [2:0] $end - $var wire 32 `! tmp [31:0] $end + $var wire 32 _! tmp [31:0] $end $upscope $end $scope module shifter7 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 a! out [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 `! out [7:0] $end $var wire 3 - shift [2:0] $end - $var wire 32 b! tmp [31:0] $end + $var wire 32 a! tmp [31:0] $end $upscope $end $scope module shifter8 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 c! out [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 b! out [7:0] $end $var wire 3 - shift [2:0] $end - $var wire 32 d! tmp [0:31] $end + $var wire 32 c! tmp [0:31] $end $upscope $end $scope module though0 $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 #" in [7:0] $end - $var wire 8 $" out [7:0] $end - $var wire 1 =" unpack_tmp(0) $end - $var wire 1 >" unpack_tmp(1) $end - $var wire 1 ?" unpack_tmp(2) $end - $var wire 1 @" unpack_tmp(3) $end - $var wire 1 A" unpack_tmp(4) $end - $var wire 1 B" unpack_tmp(5) $end - $var wire 1 C" unpack_tmp(6) $end - $var wire 1 D" unpack_tmp(7) $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 "" in [7:0] $end + $var wire 8 #" out [7:0] $end + $var wire 1 <" unpack_tmp(0) $end + $var wire 1 =" unpack_tmp(1) $end + $var wire 1 >" unpack_tmp(2) $end + $var wire 1 ?" unpack_tmp(3) $end + $var wire 1 @" unpack_tmp(4) $end + $var wire 1 A" unpack_tmp(5) $end + $var wire 1 B" unpack_tmp(6) $end + $var wire 1 C" unpack_tmp(7) $end $scope module i_pack2unpack $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 #" in [7:0] $end - $var wire 1 E" out(0) $end - $var wire 1 F" out(1) $end - $var wire 1 G" out(2) $end - $var wire 1 H" out(3) $end - $var wire 1 I" out(4) $end - $var wire 1 J" out(5) $end - $var wire 1 K" out(6) $end - $var wire 1 L" out(7) $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 "" in [7:0] $end + $var wire 1 D" out[0] $end + $var wire 1 E" out[1] $end + $var wire 1 F" out[2] $end + $var wire 1 G" out[3] $end + $var wire 1 H" out[4] $end + $var wire 1 I" out[5] $end + $var wire 1 J" out[6] $end + $var wire 1 K" out[7] $end $upscope $end $scope module i_unpack2pack $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 1 M" in(0) $end - $var wire 1 N" in(1) $end - $var wire 1 O" in(2) $end - $var wire 1 P" in(3) $end - $var wire 1 Q" in(4) $end - $var wire 1 R" in(5) $end - $var wire 1 S" in(6) $end - $var wire 1 T" in(7) $end - $var wire 8 $" out [7:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 1 L" in[0] $end + $var wire 1 M" in[1] $end + $var wire 1 N" in[2] $end + $var wire 1 O" in[3] $end + $var wire 1 P" in[4] $end + $var wire 1 Q" in[5] $end + $var wire 1 R" in[6] $end + $var wire 1 S" in[7] $end + $var wire 8 #" out [7:0] $end $upscope $end $upscope $end $upscope $end @@ -540,18 +539,18 @@ b10001110 U! b10001110 V! b10001110 W! b10001110 X! -b10001110 Y! +b00000000000000000000000010001110 Y! b00000000000000000000000010001110 Z! b00000000000000000000000010001110 [! -b00000000000000000000000010001110 \! -b10001110 ]! -b10001110100011101000111010001110 ^! -b10001110 _! -b10001110100011101000111010001110 `! -b10001110 a! -b10001110100011101000111010001110 b! -b10001110 c! -b10001110100011101000111010001110 d! +b10001110 \! +b10001110100011101000111010001110 ]! +b10001110 ^! +b10001110100011101000111010001110 _! +b10001110 `! +b10001110100011101000111010001110 a! +b10001110 b! +b10001110100011101000111010001110 c! +0d! 0e! 0f! 0g! @@ -567,10 +566,10 @@ b10001110100011101000111010001110 d! 0q! 0r! 0s! -0t! -b00000000000000000000000000000000 u! +b00000000000000000000000000000000 t! +0u! 0v! -0w! +b10001110 w! b10001110 x! b10001110 y! b10001110 z! @@ -605,39 +604,39 @@ b10001110 8" b10001110 9" b10001110 :" b10001110 ;" -b10001110 <" -1=" +1<" +0=" 0>" 0?" -0@" +1@" 1A" 1B" -1C" +0C" 0D" -0E" +1E" 1F" 1G" -1H" +0H" 0I" 0J" -0K" -1L" -0M" +1K" +0L" +1M" 1N" 1O" -1P" +0P" 0Q" 0R" -0S" -1T" -0U" -b00000000000000000000000000000011 V" -b00000000000000000000000000001000 W" -b00000000000000000000000000001001 X" -b10001110 Y" -b1000111001000111101000111101000111101000011101000011101000011101 Z" -b11111111111111111111111111111101 \" -b00000000000000000000000000000001 ]" +1S" +0T" +b00000000000000000000000000000011 U" +b00000000000000000000000000001000 V" +b00000000000000000000000000001001 W" +b10001110 X" +b1000111001000111101000111101000111101000011101000011101000011101 Y" +b11111111111111111111111111111101 [" +b00000000000000000000000000000001 \" +b00000000 ]" b00000000 ^" b00000000 _" b00000000 `" @@ -661,18 +660,17 @@ b00000000 q" b00000000 r" b00000000 s" b00000000 t" -b00000000 u" -b00000000000000000000000000000010 v" -b00000000000000000000000010001110 w" -b000000000000000000000000 x" -b11111111111111111111111111111110 y" +b00000000000000000000000000000010 u" +b00000000000000000000000010001110 v" +b000000000000000000000000 w" +b11111111111111111111111111111110 x" +0y" 0z" 0{" 0|" 0}" 0~" -0!# -b00000000000000000000000000000000 "# +b00000000000000000000000000000000 !# #10 b001 - b00000000000000000000000000001001 / @@ -690,7 +688,7 @@ b01000111 : b01000111 ; b01000111 < b01000111 = -b01000111 > +b01000111 A b01000111 B b01000111 C b01000111 D @@ -699,7 +697,7 @@ b01000111 F b01000111 G b01000111 H b01000111 I -b01000111 J +b01000111 M b01000111 N b01000111 O b01000111 P @@ -708,7 +706,7 @@ b01000111 R b01000111 S b01000111 T b01000111 U -b01000111 V +b01000111 Y b01000111 Z b01000111 [ b01000111 \ @@ -717,7 +715,7 @@ b01000111 ^ b01000111 _ b01000111 ` b01000111 a -b01000111 b +b01000111 e b01000111 f b01000111 g b01000111 h @@ -726,7 +724,7 @@ b01000111 j b01000111 k b01000111 l b01000111 m -b01000111 n +b01000111 q b01000111 r b01000111 s b01000111 t @@ -735,7 +733,7 @@ b01000111 v b01000111 w b01000111 x b01000111 y -b01000111 z +b01000111 } b01000111 ~ b01000111 !! b01000111 "! @@ -744,7 +742,7 @@ b01000111 $! b01000111 %! b01000111 &! b01000111 '! -b01000111 (! +b01000111 +! b01000111 ,! b01000111 -! b01000111 .! @@ -753,7 +751,7 @@ b01000111 0! b01000111 1! b01000111 2! b01000111 3! -b01000111 4! +b01000111 7! b01000111 8! b01000111 9! b01000111 :! @@ -762,7 +760,7 @@ b01000111 ! b01000111 ?! -b01000111 @! +b01000111 C! b01000111 D! b01000111 E! b01000111 F! @@ -771,7 +769,7 @@ b01000111 H! b01000111 I! b01000111 J! b01000111 K! -b01000111 L! +b01000111 O! b01000111 P! b01000111 Q! b01000111 R! @@ -781,24 +779,24 @@ b01000111 U! b01000111 V! b01000111 W! b01000111 X! -b01000111 Y! +b00000000000000000000000001000111 Y! b00000000000000000000000001000111 Z! b00000000000000000000000001000111 [! -b00000000000000000000000001000111 \! -b01000111 ]! -b10001110010001110100011101000111 ^! -b01000111 _! -b10001110010001110100011101000111 `! -b01000111 a! -b10001110010001110100011101000111 b! -b01000111 c! -b10001110010001110100011101000111 d! -1e! -1i! -1m! -1q! -b00000000000000000000000000000001 u! -1w! +b01000111 \! +b10001110010001110100011101000111 ]! +b01000111 ^! +b10001110010001110100011101000111 _! +b01000111 `! +b10001110010001110100011101000111 a! +b01000111 b! +b10001110010001110100011101000111 c! +1d! +1h! +1l! +1p! +b00000000000000000000000000000001 t! +1v! +b01000111 w! b01000111 x! b01000111 y! b01000111 z! @@ -810,7 +808,7 @@ b01000111 !" b01000111 "" b01000111 #" b01000111 $" -b01000111 %" +b01000111 (" b01000111 )" b01000111 *" b01000111 +" @@ -818,7 +816,7 @@ b01000111 ," b01000111 -" b01000111 ." b01000111 /" -b01000111 0" +b01000111 3" b01000111 4" b01000111 5" b01000111 6" @@ -827,22 +825,21 @@ b01000111 8" b01000111 9" b01000111 :" b01000111 ;" -b01000111 <" -0=" -1>" -0A" +0<" +1=" +0@" +1C" 1D" -1E" -0H" -1K" -0L" -1M" -0P" -1S" -0T" -1U" +0G" +1J" +0K" +1L" +0O" +1R" +0S" +1T" #15 -0U" +0T" #20 b010 - b10100011 0 @@ -851,122 +848,122 @@ b10100011 2 b10100011 3 b10100011 4 b10100011 5 -b10100011 6 -b10001110 7 +b10001110 6 +b10100011 7 b10100011 8 b10100011 9 b10100011 : b10100011 ; b10100011 < b10100011 = -b10100011 > +b10001110 A b10001110 B b10001110 C -b10001110 D +b10100011 D b10100011 E b10100011 F b10100011 G b10100011 H b10100011 I -b10100011 J +b10001110 M b10001110 N b10001110 O -b10001110 P +b10100011 P b10100011 Q b10100011 R b10100011 S b10100011 T b10100011 U -b10100011 V +b10001110 Y b10001110 Z b10001110 [ -b10001110 \ +b10100011 \ b10100011 ] b10100011 ^ b10100011 _ b10100011 ` b10100011 a -b10100011 b +b10001110 e b10001110 f b10001110 g -b10001110 h +b10100011 h b10100011 i b10100011 j b10100011 k b10100011 l b10100011 m -b10100011 n +b10001110 q b10001110 r b10001110 s -b10001110 t +b10100011 t b10100011 u b10100011 v b10100011 w b10100011 x b10100011 y -b10100011 z +b10001110 } b10001110 ~ b10001110 !! -b10001110 "! +b10100011 "! b10100011 #! b10100011 $! b10100011 %! b10100011 &! b10100011 '! -b10100011 (! +b10001110 +! b10001110 ,! b10001110 -! -b10001110 .! +b10100011 .! b10100011 /! b10100011 0! b10100011 1! b10100011 2! b10100011 3! -b10100011 4! +b10001110 7! b10001110 8! b10001110 9! -b10001110 :! +b10100011 :! b10100011 ;! b10100011 ! b10100011 ?! -b10100011 @! +b10001110 C! b10001110 D! b10001110 E! -b10001110 F! +b10100011 F! b10100011 G! b10100011 H! b10100011 I! b10100011 J! b10100011 K! -b10100011 L! +b10001110 O! b10001110 P! b10001110 Q! -b10001110 R! +b10100011 R! b10100011 S! b10100011 T! b10100011 U! b10100011 V! b10100011 W! b10100011 X! -b10100011 Y! -b00000000000000000000000010001110 Z! +b00000000000000000000000010001110 Y! +b00000000000000000000000010100011 Z! b00000000000000000000000010100011 [! -b00000000000000000000000010100011 \! -b10100011 ]! -b10001110100011101010001110100011 ^! -b10100011 _! -b10001110100011101010001110100011 `! -b10100011 a! -b10001110100011101010001110100011 b! -b10100011 c! -b10001110100011101010001110100011 d! -1f! -1j! -1n! -1r! -b00000000000000000000000000000010 u! +b10100011 \! +b10001110100011101010001110100011 ]! +b10100011 ^! +b10001110100011101010001110100011 _! +b10100011 `! +b10001110100011101010001110100011 a! +b10100011 b! +b10001110100011101010001110100011 c! +1e! +1i! +1m! +1q! +b00000000000000000000000000000010 t! +b10100011 w! b10100011 x! b10100011 y! b10100011 z! @@ -978,39 +975,38 @@ b10100011 !" b10100011 "" b10100011 #" b10100011 $" -b10100011 %" +b10001110 (" b10001110 )" b10001110 *" -b10001110 +" +b10100011 +" b10100011 ," b10100011 -" b10100011 ." b10100011 /" -b10100011 0" +b10001110 3" b10001110 4" b10001110 5" -b10001110 6" +b10100011 6" b10100011 7" b10100011 8" b10100011 9" b10100011 :" b10100011 ;" -b10100011 <" -1=" -0>" -1?" -0B" -0G" -1J" -0K" -1L" -0O" -1R" -0S" +1<" +0=" +1>" +0A" +0F" +1I" +0J" +1K" +0N" +1Q" +0R" +1S" 1T" -1U" #25 -0U" +0T" #30 b011 - b11010001 0 @@ -1019,122 +1015,122 @@ b11010001 2 b11010001 3 b11010001 4 b11010001 5 -b11010001 6 -b01000111 7 +b01000111 6 +b11010001 7 b11010001 8 b11010001 9 b11010001 : b11010001 ; b11010001 < b11010001 = -b11010001 > +b01000111 A b01000111 B b01000111 C -b01000111 D +b11010001 D b11010001 E b11010001 F b11010001 G b11010001 H b11010001 I -b11010001 J +b01000111 M b01000111 N b01000111 O -b01000111 P +b11010001 P b11010001 Q b11010001 R b11010001 S b11010001 T b11010001 U -b11010001 V +b01000111 Y b01000111 Z b01000111 [ -b01000111 \ +b11010001 \ b11010001 ] b11010001 ^ b11010001 _ b11010001 ` b11010001 a -b11010001 b +b01000111 e b01000111 f b01000111 g -b01000111 h +b11010001 h b11010001 i b11010001 j b11010001 k b11010001 l b11010001 m -b11010001 n +b01000111 q b01000111 r b01000111 s -b01000111 t +b11010001 t b11010001 u b11010001 v b11010001 w b11010001 x b11010001 y -b11010001 z +b01000111 } b01000111 ~ b01000111 !! -b01000111 "! +b11010001 "! b11010001 #! b11010001 $! b11010001 %! b11010001 &! b11010001 '! -b11010001 (! +b01000111 +! b01000111 ,! b01000111 -! -b01000111 .! +b11010001 .! b11010001 /! b11010001 0! b11010001 1! b11010001 2! b11010001 3! -b11010001 4! +b01000111 7! b01000111 8! b01000111 9! -b01000111 :! +b11010001 :! b11010001 ;! b11010001 ! b11010001 ?! -b11010001 @! +b01000111 C! b01000111 D! b01000111 E! -b01000111 F! +b11010001 F! b11010001 G! b11010001 H! b11010001 I! b11010001 J! b11010001 K! -b11010001 L! +b01000111 O! b01000111 P! b01000111 Q! -b01000111 R! +b11010001 R! b11010001 S! b11010001 T! b11010001 U! b11010001 V! b11010001 W! b11010001 X! -b11010001 Y! -b00000000000000000000000001000111 Z! +b00000000000000000000000001000111 Y! +b00000000000000000000000011010001 Z! b00000000000000000000000011010001 [! -b00000000000000000000000011010001 \! -b11010001 ]! -b10001110010001111101000111010001 ^! -b11010001 _! -b10001110010001111101000111010001 `! -b11010001 a! -b10001110010001111101000111010001 b! -b11010001 c! -b10001110010001111101000111010001 d! -1g! -1k! -1o! -1s! -b00000000000000000000000000000011 u! +b11010001 \! +b10001110010001111101000111010001 ]! +b11010001 ^! +b10001110010001111101000111010001 _! +b11010001 `! +b10001110010001111101000111010001 a! +b11010001 b! +b10001110010001111101000111010001 c! +1f! +1j! +1n! +1r! +b00000000000000000000000000000011 t! +b11010001 w! b11010001 x! b11010001 y! b11010001 z! @@ -1146,163 +1142,162 @@ b11010001 !" b11010001 "" b11010001 #" b11010001 $" -b11010001 %" +b01000111 (" b01000111 )" b01000111 *" -b01000111 +" +b11010001 +" b11010001 ," b11010001 -" b11010001 ." b11010001 /" -b11010001 0" +b01000111 3" b01000111 4" b01000111 5" -b01000111 6" +b11010001 6" b11010001 7" b11010001 8" b11010001 9" b11010001 :" b11010001 ;" -b11010001 <" -1>" -0?" -1@" -0C" -0F" -1I" -0J" -1K" -0N" -1Q" -0R" -1S" -1U" +1=" +0>" +1?" +0B" +0E" +1H" +0I" +1J" +0M" +1P" +0Q" +1R" +1T" #35 -0U" +0T" #40 b100 - b11101000 0 b10001110 1 b10001110 2 b11101000 3 -b11101000 4 -b10001110 5 -b11101000 6 +b10001110 4 +b11101000 5 +b10001110 6 b10001110 7 b10001110 8 b10001110 9 b10001110 : -b10001110 ; +b11101000 ; b11101000 < b11101000 = -b11101000 > +b10001110 A b10001110 B b10001110 C b10001110 D b10001110 E b10001110 F -b10001110 G +b11101000 G b11101000 H b11101000 I -b11101000 J +b10001110 M b10001110 N b10001110 O b10001110 P b10001110 Q b10001110 R -b10001110 S +b11101000 S b11101000 T b11101000 U -b11101000 V +b10001110 Y b10001110 Z b10001110 [ b10001110 \ b10001110 ] b10001110 ^ -b10001110 _ +b11101000 _ b11101000 ` b11101000 a -b11101000 b +b10001110 e b10001110 f b10001110 g b10001110 h b10001110 i b10001110 j -b10001110 k +b11101000 k b11101000 l b11101000 m -b11101000 n +b10001110 q b10001110 r b10001110 s b10001110 t b10001110 u b10001110 v -b10001110 w +b11101000 w b11101000 x b11101000 y -b11101000 z +b10001110 } b10001110 ~ b10001110 !! b10001110 "! b10001110 #! b10001110 $! -b10001110 %! +b11101000 %! b11101000 &! b11101000 '! -b11101000 (! +b10001110 +! b10001110 ,! b10001110 -! b10001110 .! b10001110 /! b10001110 0! -b10001110 1! +b11101000 1! b11101000 2! b11101000 3! -b11101000 4! +b10001110 7! b10001110 8! b10001110 9! b10001110 :! b10001110 ;! b10001110 ! b11101000 ?! -b11101000 @! +b10001110 C! b10001110 D! b10001110 E! b10001110 F! b10001110 G! b10001110 H! -b10001110 I! +b11101000 I! b11101000 J! b11101000 K! -b11101000 L! +b10001110 O! b10001110 P! b10001110 Q! b10001110 R! b10001110 S! b10001110 T! -b10001110 U! +b11101000 U! b11101000 V! b11101000 W! b11101000 X! -b11101000 Y! +b00000000000000000000000010001110 Y! b00000000000000000000000010001110 Z! -b00000000000000000000000010001110 [! -b00000000000000000000000011101000 \! -b11101000 ]! -b10001110100011101000111011101000 ^! -b11101000 _! -b10001110100011101000111011101000 `! -b11101000 a! -b10001110100011101000111011101000 b! -b11101000 c! -b10001110100011101000111011101000 d! -1h! -1l! -1p! -1t! -b00000000000000000000000000000100 u! +b00000000000000000000000011101000 [! +b11101000 \! +b10001110100011101000111011101000 ]! +b11101000 ^! +b10001110100011101000111011101000 _! +b11101000 `! +b10001110100011101000111011101000 a! +b11101000 b! +b10001110100011101000111011101000 c! +1g! +1k! +1o! +1s! +b00000000000000000000000000000100 t! +b11101000 w! b11101000 x! b11101000 y! b11101000 z! @@ -1314,159 +1309,158 @@ b11101000 !" b11101000 "" b11101000 #" b11101000 $" -b11101000 %" +b10001110 (" b10001110 )" b10001110 *" b10001110 +" b10001110 ," b10001110 -" -b10001110 ." +b11101000 ." b11101000 /" -b11101000 0" +b10001110 3" b10001110 4" b10001110 5" b10001110 6" b10001110 7" b10001110 8" -b10001110 9" +b11101000 9" b11101000 :" b11101000 ;" -b11101000 <" -1?" -0@" -1A" +1>" +0?" +1@" +0C" 0D" -0E" -1H" -0I" -1J" -0M" -1P" -0Q" -1R" -1U" +1G" +0H" +1I" +0L" +1O" +0P" +1Q" +1T" #45 -0U" +0T" #50 b101 - b01110100 0 b01000111 1 b01000111 2 b01110100 3 -b01110100 4 -b01000111 5 -b01110100 6 +b01000111 4 +b01110100 5 +b01000111 6 b01000111 7 b01000111 8 b01000111 9 b01000111 : -b01000111 ; +b01110100 ; b01110100 < b01110100 = -b01110100 > +b01000111 A b01000111 B b01000111 C b01000111 D b01000111 E b01000111 F -b01000111 G +b01110100 G b01110100 H b01110100 I -b01110100 J +b01000111 M b01000111 N b01000111 O b01000111 P b01000111 Q b01000111 R -b01000111 S +b01110100 S b01110100 T b01110100 U -b01110100 V +b01000111 Y b01000111 Z b01000111 [ b01000111 \ b01000111 ] b01000111 ^ -b01000111 _ +b01110100 _ b01110100 ` b01110100 a -b01110100 b +b01000111 e b01000111 f b01000111 g b01000111 h b01000111 i b01000111 j -b01000111 k +b01110100 k b01110100 l b01110100 m -b01110100 n +b01000111 q b01000111 r b01000111 s b01000111 t b01000111 u b01000111 v -b01000111 w +b01110100 w b01110100 x b01110100 y -b01110100 z +b01000111 } b01000111 ~ b01000111 !! b01000111 "! b01000111 #! b01000111 $! -b01000111 %! +b01110100 %! b01110100 &! b01110100 '! -b01110100 (! +b01000111 +! b01000111 ,! b01000111 -! b01000111 .! b01000111 /! b01000111 0! -b01000111 1! +b01110100 1! b01110100 2! b01110100 3! -b01110100 4! +b01000111 7! b01000111 8! b01000111 9! b01000111 :! b01000111 ;! b01000111 ! b01110100 ?! -b01110100 @! +b01000111 C! b01000111 D! b01000111 E! b01000111 F! b01000111 G! b01000111 H! -b01000111 I! +b01110100 I! b01110100 J! b01110100 K! -b01110100 L! +b01000111 O! b01000111 P! b01000111 Q! b01000111 R! b01000111 S! b01000111 T! -b01000111 U! +b01110100 U! b01110100 V! b01110100 W! b01110100 X! -b01110100 Y! +b00000000000000000000000001000111 Y! b00000000000000000000000001000111 Z! -b00000000000000000000000001000111 [! -b00000000000000000000000001110100 \! -b01110100 ]! -b10001110010001110100011101110100 ^! -b01110100 _! -b10001110010001110100011101110100 `! -b01110100 a! -b10001110010001110100011101110100 b! -b01110100 c! -b10001110010001110100011101110100 d! -b00000000000000000000000000000101 u! +b00000000000000000000000001110100 [! +b01110100 \! +b10001110010001110100011101110100 ]! +b01110100 ^! +b10001110010001110100011101110100 _! +b01110100 `! +b10001110010001110100011101110100 a! +b01110100 b! +b10001110010001110100011101110100 c! +b00000000000000000000000000000101 t! +b01110100 w! b01110100 x! b01110100 y! b01110100 z! @@ -1478,159 +1472,158 @@ b01110100 !" b01110100 "" b01110100 #" b01110100 $" -b01110100 %" +b01000111 (" b01000111 )" b01000111 *" b01000111 +" b01000111 ," b01000111 -" -b01000111 ." +b01110100 ." b01110100 /" -b01110100 0" +b01000111 3" b01000111 4" b01000111 5" b01000111 6" b01000111 7" b01000111 8" -b01000111 9" +b01110100 9" b01110100 :" b01110100 ;" -b01110100 <" -0=" -1@" -0A" -1B" -1G" -0H" -1I" -0L" -1O" -0P" -1Q" -0T" -1U" +0<" +1?" +0@" +1A" +1F" +0G" +1H" +0K" +1N" +0O" +1P" +0S" +1T" #55 -0U" +0T" #60 b110 - b00111010 0 b10001110 1 b10100011 2 b00111010 3 -b00111010 4 -b10100011 5 -b00111010 6 -b10001110 7 +b10100011 4 +b00111010 5 +b10001110 6 +b10100011 7 b10100011 8 b10100011 9 b10100011 : -b10100011 ; +b00111010 ; b00111010 < b00111010 = -b00111010 > +b10001110 A b10001110 B b10001110 C -b10001110 D +b10100011 D b10100011 E b10100011 F -b10100011 G +b00111010 G b00111010 H b00111010 I -b00111010 J +b10001110 M b10001110 N b10001110 O -b10001110 P +b10100011 P b10100011 Q b10100011 R -b10100011 S +b00111010 S b00111010 T b00111010 U -b00111010 V +b10001110 Y b10001110 Z b10001110 [ -b10001110 \ +b10100011 \ b10100011 ] b10100011 ^ -b10100011 _ +b00111010 _ b00111010 ` b00111010 a -b00111010 b +b10001110 e b10001110 f b10001110 g -b10001110 h +b10100011 h b10100011 i b10100011 j -b10100011 k +b00111010 k b00111010 l b00111010 m -b00111010 n +b10001110 q b10001110 r b10001110 s -b10001110 t +b10100011 t b10100011 u b10100011 v -b10100011 w +b00111010 w b00111010 x b00111010 y -b00111010 z +b10001110 } b10001110 ~ b10001110 !! -b10001110 "! +b10100011 "! b10100011 #! b10100011 $! -b10100011 %! +b00111010 %! b00111010 &! b00111010 '! -b00111010 (! +b10001110 +! b10001110 ,! b10001110 -! -b10001110 .! +b10100011 .! b10100011 /! b10100011 0! -b10100011 1! +b00111010 1! b00111010 2! b00111010 3! -b00111010 4! +b10001110 7! b10001110 8! b10001110 9! -b10001110 :! +b10100011 :! b10100011 ;! b10100011 ! b00111010 ?! -b00111010 @! +b10001110 C! b10001110 D! b10001110 E! -b10001110 F! +b10100011 F! b10100011 G! b10100011 H! -b10100011 I! +b00111010 I! b00111010 J! b00111010 K! -b00111010 L! +b10001110 O! b10001110 P! b10001110 Q! -b10001110 R! +b10100011 R! b10100011 S! b10100011 T! -b10100011 U! +b00111010 U! b00111010 V! b00111010 W! b00111010 X! -b00111010 Y! -b00000000000000000000000010001110 Z! -b00000000000000000000000010100011 [! -b00000000000000000000000000111010 \! -b00111010 ]! -b10001110100011101010001100111010 ^! -b00111010 _! -b10001110100011101010001100111010 `! -b00111010 a! -b10001110100011101010001100111010 b! -b00111010 c! -b10001110100011101010001100111010 d! -b00000000000000000000000000000110 u! +b00000000000000000000000010001110 Y! +b00000000000000000000000010100011 Z! +b00000000000000000000000000111010 [! +b00111010 \! +b10001110100011101010001100111010 ]! +b00111010 ^! +b10001110100011101010001100111010 _! +b00111010 `! +b10001110100011101010001100111010 a! +b00111010 b! +b10001110100011101010001100111010 c! +b00000000000000000000000000000110 t! +b00111010 w! b00111010 x! b00111010 y! b00111010 z! @@ -1642,159 +1635,158 @@ b00111010 !" b00111010 "" b00111010 #" b00111010 $" -b00111010 %" +b10001110 (" b10001110 )" b10001110 *" -b10001110 +" +b10100011 +" b10100011 ," b10100011 -" -b10100011 ." +b00111010 ." b00111010 /" -b00111010 0" +b10001110 3" b10001110 4" b10001110 5" -b10001110 6" +b10100011 6" b10100011 7" b10100011 8" -b10100011 9" +b00111010 9" b00111010 :" b00111010 ;" -b00111010 <" -0>" -1A" -0B" -1C" -1F" -0G" -1H" -0K" -1N" -0O" -1P" -0S" -1U" +0=" +1@" +0A" +1B" +1E" +0F" +1G" +0J" +1M" +0N" +1O" +0R" +1T" #65 -0U" +0T" #70 b111 - b00011101 0 b01000111 1 b11010001 2 b00011101 3 -b00011101 4 -b11010001 5 -b00011101 6 -b01000111 7 +b11010001 4 +b00011101 5 +b01000111 6 +b11010001 7 b11010001 8 b11010001 9 b11010001 : -b11010001 ; +b00011101 ; b00011101 < b00011101 = -b00011101 > +b01000111 A b01000111 B b01000111 C -b01000111 D +b11010001 D b11010001 E b11010001 F -b11010001 G +b00011101 G b00011101 H b00011101 I -b00011101 J +b01000111 M b01000111 N b01000111 O -b01000111 P +b11010001 P b11010001 Q b11010001 R -b11010001 S +b00011101 S b00011101 T b00011101 U -b00011101 V +b01000111 Y b01000111 Z b01000111 [ -b01000111 \ +b11010001 \ b11010001 ] b11010001 ^ -b11010001 _ +b00011101 _ b00011101 ` b00011101 a -b00011101 b +b01000111 e b01000111 f b01000111 g -b01000111 h +b11010001 h b11010001 i b11010001 j -b11010001 k +b00011101 k b00011101 l b00011101 m -b00011101 n +b01000111 q b01000111 r b01000111 s -b01000111 t +b11010001 t b11010001 u b11010001 v -b11010001 w +b00011101 w b00011101 x b00011101 y -b00011101 z +b01000111 } b01000111 ~ b01000111 !! -b01000111 "! +b11010001 "! b11010001 #! b11010001 $! -b11010001 %! +b00011101 %! b00011101 &! b00011101 '! -b00011101 (! +b01000111 +! b01000111 ,! b01000111 -! -b01000111 .! +b11010001 .! b11010001 /! b11010001 0! -b11010001 1! +b00011101 1! b00011101 2! b00011101 3! -b00011101 4! +b01000111 7! b01000111 8! b01000111 9! -b01000111 :! +b11010001 :! b11010001 ;! b11010001 ! b00011101 ?! -b00011101 @! +b01000111 C! b01000111 D! b01000111 E! -b01000111 F! +b11010001 F! b11010001 G! b11010001 H! -b11010001 I! +b00011101 I! b00011101 J! b00011101 K! -b00011101 L! +b01000111 O! b01000111 P! b01000111 Q! -b01000111 R! +b11010001 R! b11010001 S! b11010001 T! -b11010001 U! +b00011101 U! b00011101 V! b00011101 W! b00011101 X! -b00011101 Y! -b00000000000000000000000001000111 Z! -b00000000000000000000000011010001 [! -b00000000000000000000000000011101 \! -b00011101 ]! -b10001110010001111101000100011101 ^! -b00011101 _! -b10001110010001111101000100011101 `! -b00011101 a! -b10001110010001111101000100011101 b! -b00011101 c! -b10001110010001111101000100011101 d! -b00000000000000000000000000000111 u! +b00000000000000000000000001000111 Y! +b00000000000000000000000011010001 Z! +b00000000000000000000000000011101 [! +b00011101 \! +b10001110010001111101000100011101 ]! +b00011101 ^! +b10001110010001111101000100011101 _! +b00011101 `! +b10001110010001111101000100011101 a! +b00011101 b! +b10001110010001111101000100011101 c! +b00000000000000000000000000000111 t! +b00011101 w! b00011101 x! b00011101 y! b00011101 z! @@ -1806,39 +1798,38 @@ b00011101 !" b00011101 "" b00011101 #" b00011101 $" -b00011101 %" +b01000111 (" b01000111 )" b01000111 *" -b01000111 +" +b11010001 +" b11010001 ," b11010001 -" -b11010001 ." +b00011101 ." b00011101 /" -b00011101 0" +b01000111 3" b01000111 4" b01000111 5" -b01000111 6" +b11010001 6" b11010001 7" b11010001 8" -b11010001 9" +b00011101 9" b00011101 :" b00011101 ;" -b00011101 <" -0?" -1B" -0C" +0>" +1A" +0B" +1C" 1D" -1E" -0F" -1G" -0J" -1M" -0N" -1O" -0R" -1U" +0E" +1F" +0I" +1L" +0M" +1N" +0Q" +1T" #75 -0U" +0T" #80 b000 - b10001110 0 @@ -1855,7 +1846,7 @@ b10001110 : b10001110 ; b10001110 < b10001110 = -b10001110 > +b10001110 A b10001110 B b10001110 C b10001110 D @@ -1864,7 +1855,7 @@ b10001110 F b10001110 G b10001110 H b10001110 I -b10001110 J +b10001110 M b10001110 N b10001110 O b10001110 P @@ -1873,7 +1864,7 @@ b10001110 R b10001110 S b10001110 T b10001110 U -b10001110 V +b10001110 Y b10001110 Z b10001110 [ b10001110 \ @@ -1882,7 +1873,7 @@ b10001110 ^ b10001110 _ b10001110 ` b10001110 a -b10001110 b +b10001110 e b10001110 f b10001110 g b10001110 h @@ -1891,7 +1882,7 @@ b10001110 j b10001110 k b10001110 l b10001110 m -b10001110 n +b10001110 q b10001110 r b10001110 s b10001110 t @@ -1900,7 +1891,7 @@ b10001110 v b10001110 w b10001110 x b10001110 y -b10001110 z +b10001110 } b10001110 ~ b10001110 !! b10001110 "! @@ -1909,7 +1900,7 @@ b10001110 $! b10001110 %! b10001110 &! b10001110 '! -b10001110 (! +b10001110 +! b10001110 ,! b10001110 -! b10001110 .! @@ -1918,7 +1909,7 @@ b10001110 0! b10001110 1! b10001110 2! b10001110 3! -b10001110 4! +b10001110 7! b10001110 8! b10001110 9! b10001110 :! @@ -1927,7 +1918,7 @@ b10001110 ! b10001110 ?! -b10001110 @! +b10001110 C! b10001110 D! b10001110 E! b10001110 F! @@ -1936,7 +1927,7 @@ b10001110 H! b10001110 I! b10001110 J! b10001110 K! -b10001110 L! +b10001110 O! b10001110 P! b10001110 Q! b10001110 R! @@ -1946,19 +1937,19 @@ b10001110 U! b10001110 V! b10001110 W! b10001110 X! -b10001110 Y! +b00000000000000000000000010001110 Y! b00000000000000000000000010001110 Z! b00000000000000000000000010001110 [! -b00000000000000000000000010001110 \! -b10001110 ]! -b10001110100011101000111010001110 ^! -b10001110 _! -b10001110100011101000111010001110 `! -b10001110 a! -b10001110100011101000111010001110 b! -b10001110 c! -b10001110100011101000111010001110 d! -b00000000000000000000000000001000 u! +b10001110 \! +b10001110100011101000111010001110 ]! +b10001110 ^! +b10001110100011101000111010001110 _! +b10001110 `! +b10001110100011101000111010001110 a! +b10001110 b! +b10001110100011101000111010001110 c! +b00000000000000000000000000001000 t! +b10001110 w! b10001110 x! b10001110 y! b10001110 z! @@ -1970,7 +1961,7 @@ b10001110 !" b10001110 "" b10001110 #" b10001110 $" -b10001110 %" +b10001110 (" b10001110 )" b10001110 *" b10001110 +" @@ -1978,7 +1969,7 @@ b10001110 ," b10001110 -" b10001110 ." b10001110 /" -b10001110 0" +b10001110 3" b10001110 4" b10001110 5" b10001110 6" @@ -1987,17 +1978,16 @@ b10001110 8" b10001110 9" b10001110 :" b10001110 ;" -b10001110 <" -1=" -0@" -1C" +1<" +0?" +1B" +0C" 0D" -0E" -1F" -0I" -1L" -0M" -1N" -0Q" +1E" +0H" +1K" +0L" +1M" +0P" +1S" 1T" -1U" diff --git a/test_regress/t/t_timescale_unit.out b/test_regress/t/t_timescale_unit.out new file mode 100644 index 000000000..6f3bba3f0 --- /dev/null +++ b/test_regress/t/t_timescale_unit.out @@ -0,0 +1,4 @@ +Time scale of __024unit is 10ps / 10ps +Time scale of from_unit is 10ps / 10ps +Time scale of t is 100ps / 10ps +*-* All Finished *-* diff --git a/test_regress/t/t_timescale_unit.pl b/test_regress/t/t_timescale_unit.pl new file mode 100755 index 000000000..88b7809fc --- /dev/null +++ b/test_regress/t/t_timescale_unit.pl @@ -0,0 +1,23 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); + +1; diff --git a/test_regress/t/t_timescale_unit.v b/test_regress/t/t_timescale_unit.v new file mode 100644 index 000000000..71825185c --- /dev/null +++ b/test_regress/t/t_timescale_unit.v @@ -0,0 +1,30 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under The Creative Commons Public Domain, for +// any use, without warranty, 2021 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +timeunit 10ps; +timeprecision 10ps; + +task show; + $printtimescale; +endtask + +module from_unit; + task show; + $printtimescale; + endtask +endmodule + +module t; + from_unit from_unit(); + timeunit 100ps; + initial begin + show(); + from_unit.show(); + $printtimescale; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_trace_complex.out b/test_regress/t/t_trace_complex.out index 74c7eb8c7..62c142c2b 100644 --- a/test_regress/t/t_trace_complex.out +++ b/test_regress/t/t_trace_complex.out @@ -1,7 +1,6 @@ $version Generated by VerilatedVcd $end -$date Mon Nov 16 17:48:27 2020 - $end -$timescale 1ps $end +$date Wed Aug 11 12:40:46 2021 $end +$timescale 1ps $end $scope module top $end $var wire 1 = clk $end @@ -12,24 +11,24 @@ $timescale 1ps $end $var wire 1 G LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $var wire 1 = clk $end $var wire 32 $ cyc [31:0] $end - $var wire 8 E unpacked_array(-1) [7:0] $end - $var wire 8 D unpacked_array(-2) [7:0] $end - $var wire 8 F unpacked_array(0) [7:0] $end - $var real 64 1 v_arr_real(0) $end - $var real 64 3 v_arr_real(1) $end + $var wire 8 E unpacked_array[-1] [7:0] $end + $var wire 8 D unpacked_array[-2] [7:0] $end + $var wire 8 F unpacked_array[0] [7:0] $end + $var real 64 1 v_arr_real[0] $end + $var real 64 3 v_arr_real[1] $end $var wire 2 ( v_arrp [2:1] $end $var wire 4 ) v_arrp_arrp [3:0] $end $var wire 4 * v_arrp_strp [3:0] $end - $var wire 1 > v_arru(1) $end - $var wire 1 ? v_arru(2) $end - $var wire 2 + v_arru_arrp(3) [2:1] $end - $var wire 2 , v_arru_arrp(4) [2:1] $end - $var wire 1 @ v_arru_arru(3)(1) $end - $var wire 1 A v_arru_arru(3)(2) $end - $var wire 1 B v_arru_arru(4)(1) $end - $var wire 1 C v_arru_arru(4)(2) $end - $var wire 2 - v_arru_strp(3) [1:0] $end - $var wire 2 . v_arru_strp(4) [1:0] $end + $var wire 1 > v_arru[1] $end + $var wire 1 ? v_arru[2] $end + $var wire 2 + v_arru_arrp[3] [2:1] $end + $var wire 2 , v_arru_arrp[4] [2:1] $end + $var wire 1 @ v_arru_arru[3][1] $end + $var wire 1 A v_arru_arru[3][2] $end + $var wire 1 B v_arru_arru[4][1] $end + $var wire 1 C v_arru_arru[4][2] $end + $var wire 2 - v_arru_strp[3] [1:0] $end + $var wire 2 . v_arru_strp[4] [1:0] $end $var wire 3 9 v_enumb [2:0] $end $var wire 6 : v_enumb2_str [5:0] $end $var wire 32 7 v_enumed [31:0] $end diff --git a/test_regress/t/t_trace_complex.pl b/test_regress/t/t_trace_complex.pl index 49c306eb5..8400cbf09 100755 --- a/test_regress/t/t_trace_complex.pl +++ b/test_regress/t/t_trace_complex.pl @@ -23,10 +23,10 @@ file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_strp_strp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_arrp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_strp /); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\(/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\[/); vcd_identical ("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); diff --git a/test_regress/t/t_trace_complex_fst.out b/test_regress/t/t_trace_complex_fst.out index d57c690c6..3fbfab803 100644 --- a/test_regress/t/t_trace_complex_fst.out +++ b/test_regress/t/t_trace_complex_fst.out @@ -1,5 +1,5 @@ $date - Mon Nov 16 17:51:08 2020 + Wed Aug 11 12:40:48 2021 $end $version @@ -19,19 +19,19 @@ $var logic 2 % v_unip_strp $end $var logic 2 & v_arrp $end $var logic 4 ' v_arrp_arrp $end $var logic 4 ( v_arrp_strp $end -$var logic 1 ) v_arru(1) $end -$var logic 1 * v_arru(2) $end -$var logic 1 + v_arru_arru(3)(1) $end -$var logic 1 , v_arru_arru(3)(2) $end -$var logic 1 - v_arru_arru(4)(1) $end -$var logic 1 . v_arru_arru(4)(2) $end -$var logic 2 / v_arru_arrp(3) $end -$var logic 2 0 v_arru_arrp(4) $end -$var logic 2 1 v_arru_strp(3) $end -$var logic 2 2 v_arru_strp(4) $end +$var logic 1 ) v_arru[1] $end +$var logic 1 * v_arru[2] $end +$var logic 1 + v_arru_arru[3][1] $end +$var logic 1 , v_arru_arru[3][2] $end +$var logic 1 - v_arru_arru[4][1] $end +$var logic 1 . v_arru_arru[4][2] $end +$var logic 2 / v_arru_arrp[3] $end +$var logic 2 0 v_arru_arrp[4] $end +$var logic 2 1 v_arru_strp[3] $end +$var logic 2 2 v_arru_strp[4] $end $var real 64 3 v_real $end -$var real 64 4 v_arr_real(0) $end -$var real 64 5 v_arr_real(1) $end +$var real 64 4 v_arr_real[0] $end +$var real 64 5 v_arr_real[1] $end $var logic 64 6 v_str32x2 $end $attrbegin misc 07 t.enumed_t 4 ZERO ONE TWO THREE 00000000000000000000000000000000 00000000000000000000000000000001 00000000000000000000000000000010 00000000000000000000000000000011 1 $end $attrbegin misc 07 "" 1 $end @@ -42,9 +42,9 @@ $attrbegin misc 07 t.enumb_t 4 BZERO BONE BTWO BTHREE 000 001 010 011 2 $end $attrbegin misc 07 "" 2 $end $var logic 3 9 v_enumb $end $var logic 6 : v_enumb2_str $end -$var logic 8 ; unpacked_array(-2) $end -$var logic 8 < unpacked_array(-1) $end -$var logic 8 = unpacked_array(0) $end +$var logic 8 ; unpacked_array[-2] $end +$var logic 8 < unpacked_array[-1] $end +$var logic 8 = unpacked_array[0] $end $var bit 1 > LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $scope module unnamedblk1 $end $var integer 32 ? b $end @@ -67,8 +67,8 @@ $var bit 1 D global_bit $end $upscope $end $upscope $end $enddefinitions $end -$dumpvars #0 +$dumpvars 1D b00000000000000000000000000000100 C b00000000000000000000000000000011 B @@ -105,6 +105,7 @@ b0000 $ b00 # b00000000000000000000000000000000 " 0! +$end #10 1! b00000000000000000000000000000001 " diff --git a/test_regress/t/t_trace_complex_fst_sc.out b/test_regress/t/t_trace_complex_fst_sc.out index f3da1ace4..f56182246 100644 --- a/test_regress/t/t_trace_complex_fst_sc.out +++ b/test_regress/t/t_trace_complex_fst_sc.out @@ -1,5 +1,5 @@ $date - Mon Apr 19 17:05:53 2021 + Wed Aug 11 12:40:52 2021 $end $version @@ -18,19 +18,19 @@ $var logic 2 % v_unip_strp $end $var logic 2 & v_arrp $end $var logic 4 ' v_arrp_arrp $end $var logic 4 ( v_arrp_strp $end -$var logic 1 ) v_arru(1) $end -$var logic 1 * v_arru(2) $end -$var logic 1 + v_arru_arru(3)(1) $end -$var logic 1 , v_arru_arru(3)(2) $end -$var logic 1 - v_arru_arru(4)(1) $end -$var logic 1 . v_arru_arru(4)(2) $end -$var logic 2 / v_arru_arrp(3) $end -$var logic 2 0 v_arru_arrp(4) $end -$var logic 2 1 v_arru_strp(3) $end -$var logic 2 2 v_arru_strp(4) $end +$var logic 1 ) v_arru[1] $end +$var logic 1 * v_arru[2] $end +$var logic 1 + v_arru_arru[3][1] $end +$var logic 1 , v_arru_arru[3][2] $end +$var logic 1 - v_arru_arru[4][1] $end +$var logic 1 . v_arru_arru[4][2] $end +$var logic 2 / v_arru_arrp[3] $end +$var logic 2 0 v_arru_arrp[4] $end +$var logic 2 1 v_arru_strp[3] $end +$var logic 2 2 v_arru_strp[4] $end $var real 64 3 v_real $end -$var real 64 4 v_arr_real(0) $end -$var real 64 5 v_arr_real(1) $end +$var real 64 4 v_arr_real[0] $end +$var real 64 5 v_arr_real[1] $end $var logic 64 6 v_str32x2 $end $attrbegin misc 07 t.enumed_t 4 ZERO ONE TWO THREE 00000000000000000000000000000000 00000000000000000000000000000001 00000000000000000000000000000010 00000000000000000000000000000011 1 $end $attrbegin misc 07 "" 1 $end @@ -41,9 +41,9 @@ $attrbegin misc 07 t.enumb_t 4 BZERO BONE BTWO BTHREE 000 001 010 011 2 $end $attrbegin misc 07 "" 2 $end $var logic 3 9 v_enumb $end $var logic 6 : v_enumb2_str $end -$var logic 8 ; unpacked_array(-2) $end -$var logic 8 < unpacked_array(-1) $end -$var logic 8 = unpacked_array(0) $end +$var logic 8 ; unpacked_array[-2] $end +$var logic 8 < unpacked_array[-1] $end +$var logic 8 = unpacked_array[0] $end $var bit 1 > LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $scope module unnamedblk1 $end $var integer 32 ? b $end @@ -66,8 +66,8 @@ $var bit 1 D global_bit $end $upscope $end $upscope $end $enddefinitions $end -$dumpvars #0 +$dumpvars 1D b00000000000000000000000000000100 C b00000000000000000000000000000011 B @@ -104,6 +104,7 @@ b0000 $ b00 # b00000000000000000000000000000000 " 0! +$end #10 1! b00000000000000000000000000000001 " diff --git a/test_regress/t/t_trace_complex_old_api.pl b/test_regress/t/t_trace_complex_old_api.pl index bdf9a0dec..4150e31af 100755 --- a/test_regress/t/t_trace_complex_old_api.pl +++ b/test_regress/t/t_trace_complex_old_api.pl @@ -28,10 +28,10 @@ file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_strp_strp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_arrp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_strp /); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\(/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\[/); vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); diff --git a/test_regress/t/t_trace_complex_params.out b/test_regress/t/t_trace_complex_params.out index 8d2cd1890..4fd754aed 100644 --- a/test_regress/t/t_trace_complex_params.out +++ b/test_regress/t/t_trace_complex_params.out @@ -1,7 +1,6 @@ $version Generated by VerilatedVcd $end -$date Mon Nov 16 17:51:08 2020 - $end -$timescale 1ps $end +$date Wed Aug 11 12:41:11 2021 $end +$timescale 1ps $end $scope module top $end $var wire 1 = clk $end @@ -12,24 +11,24 @@ $timescale 1ps $end $var wire 1 G LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $var wire 1 = clk $end $var wire 32 $ cyc [31:0] $end - $var wire 8 E unpacked_array(-1) [7:0] $end - $var wire 8 D unpacked_array(-2) [7:0] $end - $var wire 8 F unpacked_array(0) [7:0] $end - $var real 64 1 v_arr_real(0) $end - $var real 64 3 v_arr_real(1) $end + $var wire 8 E unpacked_array[-1] [7:0] $end + $var wire 8 D unpacked_array[-2] [7:0] $end + $var wire 8 F unpacked_array[0] [7:0] $end + $var real 64 1 v_arr_real[0] $end + $var real 64 3 v_arr_real[1] $end $var wire 2 ( v_arrp [2:1] $end $var wire 4 ) v_arrp_arrp [3:0] $end $var wire 4 * v_arrp_strp [3:0] $end - $var wire 1 > v_arru(1) $end - $var wire 1 ? v_arru(2) $end - $var wire 2 + v_arru_arrp(3) [2:1] $end - $var wire 2 , v_arru_arrp(4) [2:1] $end - $var wire 1 @ v_arru_arru(3)(1) $end - $var wire 1 A v_arru_arru(3)(2) $end - $var wire 1 B v_arru_arru(4)(1) $end - $var wire 1 C v_arru_arru(4)(2) $end - $var wire 2 - v_arru_strp(3) [1:0] $end - $var wire 2 . v_arru_strp(4) [1:0] $end + $var wire 1 > v_arru[1] $end + $var wire 1 ? v_arru[2] $end + $var wire 2 + v_arru_arrp[3] [2:1] $end + $var wire 2 , v_arru_arrp[4] [2:1] $end + $var wire 1 @ v_arru_arru[3][1] $end + $var wire 1 A v_arru_arru[3][2] $end + $var wire 1 B v_arru_arru[4][1] $end + $var wire 1 C v_arru_arru[4][2] $end + $var wire 2 - v_arru_strp[3] [1:0] $end + $var wire 2 . v_arru_strp[4] [1:0] $end $var wire 3 9 v_enumb [2:0] $end $var wire 6 : v_enumb2_str [5:0] $end $var wire 32 7 v_enumed [31:0] $end diff --git a/test_regress/t/t_trace_complex_params_fst.out b/test_regress/t/t_trace_complex_params_fst.out index d57c690c6..1825f3bd9 100644 --- a/test_regress/t/t_trace_complex_params_fst.out +++ b/test_regress/t/t_trace_complex_params_fst.out @@ -1,5 +1,5 @@ $date - Mon Nov 16 17:51:08 2020 + Wed Aug 11 12:41:14 2021 $end $version @@ -19,19 +19,19 @@ $var logic 2 % v_unip_strp $end $var logic 2 & v_arrp $end $var logic 4 ' v_arrp_arrp $end $var logic 4 ( v_arrp_strp $end -$var logic 1 ) v_arru(1) $end -$var logic 1 * v_arru(2) $end -$var logic 1 + v_arru_arru(3)(1) $end -$var logic 1 , v_arru_arru(3)(2) $end -$var logic 1 - v_arru_arru(4)(1) $end -$var logic 1 . v_arru_arru(4)(2) $end -$var logic 2 / v_arru_arrp(3) $end -$var logic 2 0 v_arru_arrp(4) $end -$var logic 2 1 v_arru_strp(3) $end -$var logic 2 2 v_arru_strp(4) $end +$var logic 1 ) v_arru[1] $end +$var logic 1 * v_arru[2] $end +$var logic 1 + v_arru_arru[3][1] $end +$var logic 1 , v_arru_arru[3][2] $end +$var logic 1 - v_arru_arru[4][1] $end +$var logic 1 . v_arru_arru[4][2] $end +$var logic 2 / v_arru_arrp[3] $end +$var logic 2 0 v_arru_arrp[4] $end +$var logic 2 1 v_arru_strp[3] $end +$var logic 2 2 v_arru_strp[4] $end $var real 64 3 v_real $end -$var real 64 4 v_arr_real(0) $end -$var real 64 5 v_arr_real(1) $end +$var real 64 4 v_arr_real[0] $end +$var real 64 5 v_arr_real[1] $end $var logic 64 6 v_str32x2 $end $attrbegin misc 07 t.enumed_t 4 ZERO ONE TWO THREE 00000000000000000000000000000000 00000000000000000000000000000001 00000000000000000000000000000010 00000000000000000000000000000011 1 $end $attrbegin misc 07 "" 1 $end @@ -42,9 +42,9 @@ $attrbegin misc 07 t.enumb_t 4 BZERO BONE BTWO BTHREE 000 001 010 011 2 $end $attrbegin misc 07 "" 2 $end $var logic 3 9 v_enumb $end $var logic 6 : v_enumb2_str $end -$var logic 8 ; unpacked_array(-2) $end -$var logic 8 < unpacked_array(-1) $end -$var logic 8 = unpacked_array(0) $end +$var logic 8 ; unpacked_array[-2] $end +$var logic 8 < unpacked_array[-1] $end +$var logic 8 = unpacked_array[0] $end $var bit 1 > LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $scope module unnamedblk1 $end $var integer 32 ? b $end @@ -67,8 +67,8 @@ $var bit 1 D global_bit $end $upscope $end $upscope $end $enddefinitions $end -$dumpvars #0 +$dumpvars 1D b00000000000000000000000000000100 C b00000000000000000000000000000011 B @@ -105,6 +105,7 @@ b0000 $ b00 # b00000000000000000000000000000000 " 0! +$end #10 1! b00000000000000000000000000000001 " diff --git a/test_regress/t/t_trace_complex_params_fst_sc.out b/test_regress/t/t_trace_complex_params_fst_sc.out index b5e67d184..fd56fca23 100644 --- a/test_regress/t/t_trace_complex_params_fst_sc.out +++ b/test_regress/t/t_trace_complex_params_fst_sc.out @@ -1,5 +1,5 @@ $date - Mon Apr 19 17:07:10 2021 + Wed Aug 11 12:41:17 2021 $end $version @@ -18,19 +18,19 @@ $var logic 2 % v_unip_strp $end $var logic 2 & v_arrp $end $var logic 4 ' v_arrp_arrp $end $var logic 4 ( v_arrp_strp $end -$var logic 1 ) v_arru(1) $end -$var logic 1 * v_arru(2) $end -$var logic 1 + v_arru_arru(3)(1) $end -$var logic 1 , v_arru_arru(3)(2) $end -$var logic 1 - v_arru_arru(4)(1) $end -$var logic 1 . v_arru_arru(4)(2) $end -$var logic 2 / v_arru_arrp(3) $end -$var logic 2 0 v_arru_arrp(4) $end -$var logic 2 1 v_arru_strp(3) $end -$var logic 2 2 v_arru_strp(4) $end +$var logic 1 ) v_arru[1] $end +$var logic 1 * v_arru[2] $end +$var logic 1 + v_arru_arru[3][1] $end +$var logic 1 , v_arru_arru[3][2] $end +$var logic 1 - v_arru_arru[4][1] $end +$var logic 1 . v_arru_arru[4][2] $end +$var logic 2 / v_arru_arrp[3] $end +$var logic 2 0 v_arru_arrp[4] $end +$var logic 2 1 v_arru_strp[3] $end +$var logic 2 2 v_arru_strp[4] $end $var real 64 3 v_real $end -$var real 64 4 v_arr_real(0) $end -$var real 64 5 v_arr_real(1) $end +$var real 64 4 v_arr_real[0] $end +$var real 64 5 v_arr_real[1] $end $var logic 64 6 v_str32x2 $end $attrbegin misc 07 t.enumed_t 4 ZERO ONE TWO THREE 00000000000000000000000000000000 00000000000000000000000000000001 00000000000000000000000000000010 00000000000000000000000000000011 1 $end $attrbegin misc 07 "" 1 $end @@ -41,9 +41,9 @@ $attrbegin misc 07 t.enumb_t 4 BZERO BONE BTWO BTHREE 000 001 010 011 2 $end $attrbegin misc 07 "" 2 $end $var logic 3 9 v_enumb $end $var logic 6 : v_enumb2_str $end -$var logic 8 ; unpacked_array(-2) $end -$var logic 8 < unpacked_array(-1) $end -$var logic 8 = unpacked_array(0) $end +$var logic 8 ; unpacked_array[-2] $end +$var logic 8 < unpacked_array[-1] $end +$var logic 8 = unpacked_array[0] $end $var bit 1 > LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $scope module unnamedblk1 $end $var integer 32 ? b $end @@ -66,8 +66,8 @@ $var bit 1 D global_bit $end $upscope $end $upscope $end $enddefinitions $end -$dumpvars #0 +$dumpvars 1D b00000000000000000000000000000100 C b00000000000000000000000000000011 B @@ -104,6 +104,7 @@ b0000 $ b00 # b00000000000000000000000000000000 " 0! +$end #10 1! b00000000000000000000000000000001 " diff --git a/test_regress/t/t_trace_complex_portable.pl b/test_regress/t/t_trace_complex_portable.pl index ec7e5462e..56c794042 100755 --- a/test_regress/t/t_trace_complex_portable.pl +++ b/test_regress/t/t_trace_complex_portable.pl @@ -28,10 +28,10 @@ file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_strp_strp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_arrp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_strp /); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\(/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\[/); vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); diff --git a/test_regress/t/t_trace_complex_structs.out b/test_regress/t/t_trace_complex_structs.out index 6fc0af14a..c421a6293 100644 --- a/test_regress/t/t_trace_complex_structs.out +++ b/test_regress/t/t_trace_complex_structs.out @@ -1,7 +1,6 @@ $version Generated by VerilatedVcd $end -$date Mon Nov 16 17:51:09 2020 - $end -$timescale 1ps $end +$date Wed Aug 11 12:41:22 2021 $end +$timescale 1ps $end $scope module top $end $var wire 1 I clk $end @@ -12,22 +11,22 @@ $timescale 1ps $end $var wire 1 S LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $var wire 1 I clk $end $var wire 32 $ cyc [31:0] $end - $var wire 8 Q unpacked_array(-1) [7:0] $end - $var wire 8 P unpacked_array(-2) [7:0] $end - $var wire 8 R unpacked_array(0) [7:0] $end - $var real 64 < v_arr_real(0) $end - $var real 64 > v_arr_real(1) $end + $var wire 8 Q unpacked_array[-1] [7:0] $end + $var wire 8 P unpacked_array[-2] [7:0] $end + $var wire 8 R unpacked_array[0] [7:0] $end + $var real 64 < v_arr_real[0] $end + $var real 64 > v_arr_real[1] $end $var wire 2 - v_arrp [2:1] $end - $var wire 2 . v_arrp_arrp(3) [2:1] $end - $var wire 2 / v_arrp_arrp(4) [2:1] $end - $var wire 1 J v_arru(1) $end - $var wire 1 K v_arru(2) $end - $var wire 2 4 v_arru_arrp(3) [2:1] $end - $var wire 2 5 v_arru_arrp(4) [2:1] $end - $var wire 1 L v_arru_arru(3)(1) $end - $var wire 1 M v_arru_arru(3)(2) $end - $var wire 1 N v_arru_arru(4)(1) $end - $var wire 1 O v_arru_arru(4)(2) $end + $var wire 2 . v_arrp_arrp[3] [2:1] $end + $var wire 2 / v_arrp_arrp[4] [2:1] $end + $var wire 1 J v_arru[1] $end + $var wire 1 K v_arru[2] $end + $var wire 2 4 v_arru_arrp[3] [2:1] $end + $var wire 2 5 v_arru_arrp[4] [2:1] $end + $var wire 1 L v_arru_arru[3][1] $end + $var wire 1 M v_arru_arru[3][2] $end + $var wire 1 N v_arru_arru[4][1] $end + $var wire 1 O v_arru_arru[4][2] $end $var wire 3 D v_enumb [2:0] $end $var wire 32 B v_enumed [31:0] $end $var wire 32 C v_enumed2 [31:0] $end @@ -38,19 +37,19 @@ $timescale 1ps $end $var wire 32 H a [31:0] $end $upscope $end $upscope $end - $scope struct v_arrp_strp(3) $end + $scope struct v_arrp_strp[3] $end $var wire 1 1 b0 $end $var wire 1 0 b1 $end $upscope $end - $scope struct v_arrp_strp(4) $end + $scope struct v_arrp_strp[4] $end $var wire 1 3 b0 $end $var wire 1 2 b1 $end $upscope $end - $scope struct v_arru_strp(3) $end + $scope struct v_arru_strp[3] $end $var wire 1 7 b0 $end $var wire 1 6 b1 $end $upscope $end - $scope struct v_arru_strp(4) $end + $scope struct v_arru_strp[4] $end $var wire 1 9 b0 $end $var wire 1 8 b1 $end $upscope $end @@ -58,16 +57,12 @@ $timescale 1ps $end $var wire 3 E a [2:0] $end $var wire 3 F b [2:0] $end $upscope $end - $scope struct v_str32x2(0) $end + $scope struct v_str32x2[0] $end $var wire 32 @ data [31:0] $end $upscope $end - $scope struct v_str32x2(1) $end + $scope struct v_str32x2[1] $end $var wire 32 A data [31:0] $end $upscope $end - $scope struct v_strp $end - $var wire 1 & b0 $end - $var wire 1 % b1 $end - $upscope $end $scope struct v_strp_strp $end $scope struct x0 $end $var wire 1 * b0 $end @@ -78,7 +73,11 @@ $timescale 1ps $end $var wire 1 ' b1 $end $upscope $end $upscope $end - $scope struct v_unip_strp $end + $scope struct v_strp $end + $var wire 1 & b0 $end + $var wire 1 % b1 $end + $upscope $end + $scope union v_unip_strp $end $scope struct x0 $end $var wire 1 , b0 $end $var wire 1 + b1 $end diff --git a/test_regress/t/t_trace_complex_structs.pl b/test_regress/t/t_trace_complex_structs.pl index 6234bcdde..dbbf55c04 100755 --- a/test_regress/t/t_trace_complex_structs.pl +++ b/test_regress/t/t_trace_complex_structs.pl @@ -25,10 +25,10 @@ file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_strp_strp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp /); file_grep_not ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_arrp /); file_grep_not ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_strp /); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\(/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\[/); vcd_identical ("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); diff --git a/test_regress/t/t_trace_complex_structs_fst.out b/test_regress/t/t_trace_complex_structs_fst.out index 48f9b2fed..8ba8df6f3 100644 --- a/test_regress/t/t_trace_complex_structs_fst.out +++ b/test_regress/t/t_trace_complex_structs_fst.out @@ -1,5 +1,5 @@ $date - Mon Nov 16 17:51:13 2020 + Wed Aug 11 12:41:25 2021 $end $version @@ -38,39 +38,39 @@ $var logic 1 * b0 $end $upscope $end $upscope $end $var logic 2 + v_arrp $end -$var logic 2 , v_arrp_arrp(3) $end -$var logic 2 - v_arrp_arrp(4) $end -$scope struct v_arrp_strp(3) $end +$var logic 2 , v_arrp_arrp[3] $end +$var logic 2 - v_arrp_arrp[4] $end +$scope struct v_arrp_strp[3] $end $var logic 1 . b1 $end $var logic 1 / b0 $end $upscope $end -$scope struct v_arrp_strp(4) $end +$scope struct v_arrp_strp[4] $end $var logic 1 0 b1 $end $var logic 1 1 b0 $end $upscope $end -$var logic 1 2 v_arru(1) $end -$var logic 1 3 v_arru(2) $end -$var logic 1 4 v_arru_arru(3)(1) $end -$var logic 1 5 v_arru_arru(3)(2) $end -$var logic 1 6 v_arru_arru(4)(1) $end -$var logic 1 7 v_arru_arru(4)(2) $end -$var logic 2 8 v_arru_arrp(3) $end -$var logic 2 9 v_arru_arrp(4) $end -$scope struct v_arru_strp(3) $end +$var logic 1 2 v_arru[1] $end +$var logic 1 3 v_arru[2] $end +$var logic 1 4 v_arru_arru[3][1] $end +$var logic 1 5 v_arru_arru[3][2] $end +$var logic 1 6 v_arru_arru[4][1] $end +$var logic 1 7 v_arru_arru[4][2] $end +$var logic 2 8 v_arru_arrp[3] $end +$var logic 2 9 v_arru_arrp[4] $end +$scope struct v_arru_strp[3] $end $var logic 1 : b1 $end $var logic 1 ; b0 $end $upscope $end -$scope struct v_arru_strp(4) $end +$scope struct v_arru_strp[4] $end $var logic 1 < b1 $end $var logic 1 = b0 $end $upscope $end $var real 64 > v_real $end -$var real 64 ? v_arr_real(0) $end -$var real 64 @ v_arr_real(1) $end -$scope struct v_str32x2(0) $end +$var real 64 ? v_arr_real[0] $end +$var real 64 @ v_arr_real[1] $end +$scope struct v_str32x2[0] $end $var logic 32 A data $end $upscope $end -$scope struct v_str32x2(1) $end +$scope struct v_str32x2[1] $end $var logic 32 B data $end $attrbegin misc 07 t.enumed_t 4 ZERO ONE TWO THREE 00000000000000000000000000000000 00000000000000000000000000000001 00000000000000000000000000000010 00000000000000000000000000000011 1 $end $upscope $end @@ -87,9 +87,9 @@ $var logic 3 F a $end $attrbegin misc 07 "" 2 $end $var logic 3 G b $end $upscope $end -$var logic 8 H unpacked_array(-2) $end -$var logic 8 I unpacked_array(-1) $end -$var logic 8 J unpacked_array(0) $end +$var logic 8 H unpacked_array[-2] $end +$var logic 8 I unpacked_array[-1] $end +$var logic 8 J unpacked_array[0] $end $var bit 1 K LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $scope module unnamedblk1 $end $var integer 32 L b $end diff --git a/test_regress/t/t_trace_complex_structs_fst_sc.out b/test_regress/t/t_trace_complex_structs_fst_sc.out index d666dae4d..515dd4625 100644 --- a/test_regress/t/t_trace_complex_structs_fst_sc.out +++ b/test_regress/t/t_trace_complex_structs_fst_sc.out @@ -1,5 +1,5 @@ $date - Thu Apr 1 15:22:14 2021 + Wed Aug 11 12:41:29 2021 $end $version @@ -37,39 +37,39 @@ $var logic 1 * b0 $end $upscope $end $upscope $end $var logic 2 + v_arrp $end -$var logic 2 , v_arrp_arrp(3) $end -$var logic 2 - v_arrp_arrp(4) $end -$scope struct v_arrp_strp(3) $end +$var logic 2 , v_arrp_arrp[3] $end +$var logic 2 - v_arrp_arrp[4] $end +$scope struct v_arrp_strp[3] $end $var logic 1 . b1 $end $var logic 1 / b0 $end $upscope $end -$scope struct v_arrp_strp(4) $end +$scope struct v_arrp_strp[4] $end $var logic 1 0 b1 $end $var logic 1 1 b0 $end $upscope $end -$var logic 1 2 v_arru(1) $end -$var logic 1 3 v_arru(2) $end -$var logic 1 4 v_arru_arru(3)(1) $end -$var logic 1 5 v_arru_arru(3)(2) $end -$var logic 1 6 v_arru_arru(4)(1) $end -$var logic 1 7 v_arru_arru(4)(2) $end -$var logic 2 8 v_arru_arrp(3) $end -$var logic 2 9 v_arru_arrp(4) $end -$scope struct v_arru_strp(3) $end +$var logic 1 2 v_arru[1] $end +$var logic 1 3 v_arru[2] $end +$var logic 1 4 v_arru_arru[3][1] $end +$var logic 1 5 v_arru_arru[3][2] $end +$var logic 1 6 v_arru_arru[4][1] $end +$var logic 1 7 v_arru_arru[4][2] $end +$var logic 2 8 v_arru_arrp[3] $end +$var logic 2 9 v_arru_arrp[4] $end +$scope struct v_arru_strp[3] $end $var logic 1 : b1 $end $var logic 1 ; b0 $end $upscope $end -$scope struct v_arru_strp(4) $end +$scope struct v_arru_strp[4] $end $var logic 1 < b1 $end $var logic 1 = b0 $end $upscope $end $var real 64 > v_real $end -$var real 64 ? v_arr_real(0) $end -$var real 64 @ v_arr_real(1) $end -$scope struct v_str32x2(0) $end +$var real 64 ? v_arr_real[0] $end +$var real 64 @ v_arr_real[1] $end +$scope struct v_str32x2[0] $end $var logic 32 A data $end $upscope $end -$scope struct v_str32x2(1) $end +$scope struct v_str32x2[1] $end $var logic 32 B data $end $attrbegin misc 07 t.enumed_t 4 ZERO ONE TWO THREE 00000000000000000000000000000000 00000000000000000000000000000001 00000000000000000000000000000010 00000000000000000000000000000011 1 $end $upscope $end @@ -86,9 +86,9 @@ $var logic 3 F a $end $attrbegin misc 07 "" 2 $end $var logic 3 G b $end $upscope $end -$var logic 8 H unpacked_array(-2) $end -$var logic 8 I unpacked_array(-1) $end -$var logic 8 J unpacked_array(0) $end +$var logic 8 H unpacked_array[-2] $end +$var logic 8 I unpacked_array[-1] $end +$var logic 8 J unpacked_array[0] $end $var bit 1 K LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $scope module unnamedblk1 $end $var integer 32 L b $end diff --git a/test_regress/t/t_trace_complex_threads_1.pl b/test_regress/t/t_trace_complex_threads_1.pl index 3c7085576..9a6e6d621 100755 --- a/test_regress/t/t_trace_complex_threads_1.pl +++ b/test_regress/t/t_trace_complex_threads_1.pl @@ -26,10 +26,10 @@ file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_strp_strp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_arrp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_strp /); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\(/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\[/); vcd_identical ("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); diff --git a/test_regress/t/t_trace_ena_cc.pl b/test_regress/t/t_trace_ena_cc.pl index ff3debaf3..ea30e0880 100755 --- a/test_regress/t/t_trace_ena_cc.pl +++ b/test_regress/t/t_trace_ena_cc.pl @@ -21,8 +21,8 @@ execute( ); if ($Self->{vlt_all}) { - file_grep ("$Self->{obj_dir}/V$Self->{name}__Trace__Slow.cpp", qr/c_trace_on\"/x); - file_grep_not ("$Self->{obj_dir}/V$Self->{name}__Trace__Slow.cpp", qr/_trace_off\"/x); + file_grep ("$Self->{obj_dir}/V$Self->{name}__Trace__0__Slow.cpp", qr/c_trace_on\"/x); + file_grep_not ("$Self->{obj_dir}/V$Self->{name}__Trace__0__Slow.cpp", qr/_trace_off\"/x); file_grep ("$Self->{obj_dir}/simx.vcd", qr/\$enddefinitions/x); file_grep_not ("$Self->{obj_dir}/simx.vcd", qr/inside_sub/x); diff --git a/test_regress/t/t_trace_fst.out b/test_regress/t/t_trace_fst.out index dde4f07e0..16e4dc7d7 100644 --- a/test_regress/t/t_trace_fst.out +++ b/test_regress/t/t_trace_fst.out @@ -1,5 +1,5 @@ $date - Sun Apr 19 04:15:36 2020 + Tue Aug 10 15:48:30 2021 $end $version @@ -39,9 +39,9 @@ $var wire 1 ! clk $end $var wire 1 $ rstn $end $var wire 5 " state $end $var logic 5 6 state_w $end -$var logic 5 7 state_array(0) $end -$var logic 5 8 state_array(1) $end -$var logic 5 9 state_array(2) $end +$var logic 5 7 state_array[0] $end +$var logic 5 8 state_array[1] $end +$var logic 5 9 state_array[2] $end $scope module unnamedblk2 $end $var int 32 : i $end $upscope $end @@ -52,43 +52,45 @@ $upscope $end $upscope $end $upscope $end $enddefinitions $end +#0 $dumpvars -0! -b00000 " -b00000000000000000000000000000000 # -0$ -r1.23 % -r4.56 & -b00000000000000000000000000000000 ' -0( -0) -b00000000000000000000000000000000 * -b0000000000000000 + -b0000000000000000000000000000000000000000000000000000000000000000 , -b00000000 - -b00000000000000000000000001111011 . -b00000000000000000000000111001000 / -00 -11 -02 -13 -04 -05 -b00000 6 -b00000 7 -b00000 8 -b00000 9 -b00000000000000000000000000000000 : b00000000000000000000000000000000 ; +b00000000000000000000000000000000 : +b00000 9 +b00000 8 +b00000 7 +b00000 6 +05 +04 +13 +02 +11 +00 +b00000000000000000000000111001000 / +b00000000000000000000000001111011 . +b00000000 - +b0000000000000000000000000000000000000000000000000000000000000000 , +b0000000000000000 + +b00000000000000000000000000000000 * +0) +0( +b00000000000000000000000000000000 ' +r4.56 & +r1.23 % +0$ +b00000000000000000000000000000000 # +b00000 " +0! +$end #10 -b00000000000000000000000000000011 ; -b00001 9 -b00001 8 -b00001 7 -b10100 6 -b00000000000000000000000000000001 # -b00001 " 1! +b00001 " +b00000000000000000000000000000001 # +b10100 6 +b00001 7 +b00001 8 +b00001 9 +b00000000000000000000000000000011 ; #15 0! #20 @@ -145,15 +147,15 @@ b00000000000000000000000000001011 # #120 1! b00000000000000000000000000001100 # -b01010 6 b10100 9 +b01010 6 b00000000000000000000000000000010 : #125 0! #130 1! -b01010 9 b00101 6 +b01010 9 b00000000000000000000000000001101 # b10100 8 #135 @@ -162,18 +164,18 @@ b10100 8 1! b01010 8 b00000000000000000000000000001110 # -b10110 6 b00101 9 -b10100 " +b10110 6 b10100 7 +b10100 " #145 0! #150 1! -b01010 7 b01010 " -b10110 9 +b01010 7 b01011 6 +b10110 9 b00000000000000000000000000001111 # b00101 8 #155 @@ -182,18 +184,18 @@ b00101 8 1! b10110 8 b00000000000000000000000000010000 # -b10001 6 b01011 9 -b00101 " +b10001 6 b00101 7 +b00101 " #165 0! #170 1! -b10110 7 b10110 " -b10001 9 +b10110 7 b11100 6 +b10001 9 b00000000000000000000000000010001 # b01011 8 #175 @@ -202,18 +204,18 @@ b01011 8 1! b10001 8 b00000000000000000000000000010010 # -b01110 6 b11100 9 -b01011 " +b01110 6 b01011 7 +b01011 " #185 0! #190 1! -b10001 7 b10001 " -b01110 9 +b10001 7 b00111 6 +b01110 9 b00000000000000000000000000010011 # b11100 8 #195 @@ -222,18 +224,18 @@ b11100 8 1! b01110 8 b00000000000000000000000000010100 # -b10111 6 b00111 9 -b11100 " +b10111 6 b11100 7 +b11100 " #205 0! #210 1! -b01110 7 b01110 " -b10111 9 +b01110 7 b11111 6 +b10111 9 b00000000000000000000000000010101 # b00111 8 #215 @@ -242,18 +244,18 @@ b00111 8 1! b10111 8 b00000000000000000000000000010110 # -b11011 6 b11111 9 -b00111 " +b11011 6 b00111 7 +b00111 " #225 0! #230 1! -b10111 7 b10111 " -b11011 9 +b10111 7 b11001 6 +b11011 9 b00000000000000000000000000010111 # b11111 8 #235 @@ -262,18 +264,18 @@ b11111 8 1! b11011 8 b00000000000000000000000000011000 # -b11000 6 b11001 9 -b11111 " +b11000 6 b11111 7 +b11111 " #245 0! #250 1! -b11011 7 b11011 " -b11000 9 +b11011 7 b01100 6 +b11000 9 b00000000000000000000000000011001 # b11001 8 #255 @@ -282,18 +284,18 @@ b11001 8 1! b11000 8 b00000000000000000000000000011010 # -b00110 6 b01100 9 -b11001 " +b00110 6 b11001 7 +b11001 " #265 0! #270 1! -b11000 7 b11000 " -b00110 9 +b11000 7 b00011 6 +b00110 9 b00000000000000000000000000011011 # b01100 8 #275 @@ -302,18 +304,18 @@ b01100 8 1! b00110 8 b00000000000000000000000000011100 # -b10101 6 b00011 9 -b01100 " +b10101 6 b01100 7 +b01100 " #285 0! #290 1! -b00110 7 b00110 " -b10101 9 +b00110 7 b11110 6 +b10101 9 b00000000000000000000000000011101 # b00011 8 #295 @@ -322,18 +324,18 @@ b00011 8 1! b10101 8 b00000000000000000000000000011110 # -b01111 6 b11110 9 -b00011 " +b01111 6 b00011 7 +b00011 " #305 0! #310 1! -b10101 7 b10101 " -b01111 9 +b10101 7 b10011 6 +b01111 9 b00000000000000000000000000011111 # b11110 8 #315 @@ -342,18 +344,18 @@ b11110 8 1! b01111 8 b00000000000000000000000000100000 # -b11101 6 b10011 9 -b11110 " +b11101 6 b11110 7 +b11110 " #325 0! #330 1! -b01111 7 b01111 " -b11101 9 +b01111 7 b11010 6 +b11101 9 b00000000000000000000000000100001 # b10011 8 #335 @@ -362,18 +364,18 @@ b10011 8 1! b11101 8 b00000000000000000000000000100010 # -b01101 6 b11010 9 -b10011 " +b01101 6 b10011 7 +b10011 " #345 0! #350 1! -b11101 7 b11101 " -b01101 9 +b11101 7 b10010 6 +b01101 9 b00000000000000000000000000100011 # b11010 8 #355 @@ -382,18 +384,18 @@ b11010 8 1! b01101 8 b00000000000000000000000000100100 # -b01001 6 b10010 9 -b11010 " +b01001 6 b11010 7 +b11010 " #365 0! #370 1! -b01101 7 b01101 " -b01001 9 +b01101 7 b10000 6 +b01001 9 b00000000000000000000000000100101 # b10010 8 #375 @@ -402,18 +404,18 @@ b10010 8 1! b01001 8 b00000000000000000000000000100110 # -b01000 6 b10000 9 -b10010 " +b01000 6 b10010 7 +b10010 " #385 0! #390 1! -b01001 7 b01001 " -b01000 9 +b01001 7 b00100 6 +b01000 9 b00000000000000000000000000100111 # b10000 8 #395 @@ -422,18 +424,18 @@ b10000 8 1! b01000 8 b00000000000000000000000000101000 # -b00010 6 b00100 9 -b10000 " +b00010 6 b10000 7 +b10000 " #405 0! #410 1! -b01000 7 b01000 " -b00010 9 +b01000 7 b00001 6 +b00010 9 b00000000000000000000000000101001 # b00100 8 #415 @@ -442,18 +444,18 @@ b00100 8 1! b00010 8 b00000000000000000000000000101010 # -b10100 6 b00001 9 -b00100 " +b10100 6 b00100 7 +b00100 " #425 0! #430 1! -b00010 7 b00010 " -b10100 9 +b00010 7 b01010 6 +b10100 9 b00000000000000000000000000101011 # b00001 8 #435 @@ -462,18 +464,18 @@ b00001 8 1! b10100 8 b00000000000000000000000000101100 # -b00101 6 b01010 9 -b00001 " +b00101 6 b00001 7 +b00001 " #445 0! #450 1! -b10100 7 b10100 " -b00101 9 +b10100 7 b10110 6 +b00101 9 b00000000000000000000000000101101 # b01010 8 #455 @@ -482,18 +484,18 @@ b01010 8 1! b00101 8 b00000000000000000000000000101110 # -b01011 6 b10110 9 -b01010 " +b01011 6 b01010 7 +b01010 " #465 0! #470 1! -b00101 7 b00101 " -b01011 9 +b00101 7 b10001 6 +b01011 9 b00000000000000000000000000101111 # b10110 8 #475 @@ -502,18 +504,18 @@ b10110 8 1! b01011 8 b00000000000000000000000000110000 # -b11100 6 b10001 9 -b10110 " +b11100 6 b10110 7 +b10110 " #485 0! #490 1! -b01011 7 b01011 " -b11100 9 +b01011 7 b01110 6 +b11100 9 b00000000000000000000000000110001 # b10001 8 #495 @@ -522,18 +524,18 @@ b10001 8 1! b11100 8 b00000000000000000000000000110010 # -b00111 6 b01110 9 -b10001 " +b00111 6 b10001 7 +b10001 " #505 0! #510 1! -b11100 7 b11100 " -b00111 9 +b11100 7 b10111 6 +b00111 9 b00000000000000000000000000110011 # b01110 8 #515 @@ -542,18 +544,18 @@ b01110 8 1! b00111 8 b00000000000000000000000000110100 # -b11111 6 b10111 9 -b01110 " +b11111 6 b01110 7 +b01110 " #525 0! #530 1! -b00111 7 b00111 " -b11111 9 +b00111 7 b11011 6 +b11111 9 b00000000000000000000000000110101 # b10111 8 #535 @@ -562,18 +564,18 @@ b10111 8 1! b11111 8 b00000000000000000000000000110110 # -b11001 6 b11011 9 -b10111 " +b11001 6 b10111 7 +b10111 " #545 0! #550 1! -b11111 7 b11111 " -b11001 9 +b11111 7 b11000 6 +b11001 9 b00000000000000000000000000110111 # b11011 8 #555 @@ -582,18 +584,18 @@ b11011 8 1! b11001 8 b00000000000000000000000000111000 # -b01100 6 b11000 9 -b11011 " +b01100 6 b11011 7 +b11011 " #565 0! #570 1! -b11001 7 b11001 " -b01100 9 +b11001 7 b00110 6 +b01100 9 b00000000000000000000000000111001 # b11000 8 #575 @@ -602,18 +604,18 @@ b11000 8 1! b01100 8 b00000000000000000000000000111010 # -b00011 6 b00110 9 -b11000 " +b00011 6 b11000 7 +b11000 " #585 0! #590 1! -b01100 7 b01100 " -b00011 9 +b01100 7 b10101 6 +b00011 9 b00000000000000000000000000111011 # b00110 8 #595 @@ -622,18 +624,18 @@ b00110 8 1! b00011 8 b00000000000000000000000000111100 # -b11110 6 b10101 9 -b00110 " +b11110 6 b00110 7 +b00110 " #605 0! #610 1! -b00011 7 b00011 " -b11110 9 +b00011 7 b01111 6 +b11110 9 b00000000000000000000000000111101 # b10101 8 #615 @@ -642,18 +644,18 @@ b10101 8 1! b11110 8 b00000000000000000000000000111110 # -b10011 6 b01111 9 -b10101 " +b10011 6 b10101 7 +b10101 " #625 0! #630 1! -b11110 7 b11110 " -b10011 9 +b11110 7 b11101 6 +b10011 9 b00000000000000000000000000111111 # b01111 8 #635 @@ -662,18 +664,18 @@ b01111 8 1! b10011 8 b00000000000000000000000001000000 # -b11010 6 b11101 9 -b01111 " +b11010 6 b01111 7 +b01111 " #645 0! #650 1! -b10011 7 b10011 " -b11010 9 +b10011 7 b01101 6 +b11010 9 b00000000000000000000000001000001 # b11101 8 #655 @@ -682,18 +684,18 @@ b11101 8 1! b11010 8 b00000000000000000000000001000010 # -b10010 6 b01101 9 -b11101 " +b10010 6 b11101 7 +b11101 " #665 0! #670 1! -b11010 7 b11010 " -b10010 9 +b11010 7 b01001 6 +b10010 9 b00000000000000000000000001000011 # b01101 8 #675 @@ -702,18 +704,18 @@ b01101 8 1! b10010 8 b00000000000000000000000001000100 # -b10000 6 b01001 9 -b01101 " +b10000 6 b01101 7 +b01101 " #685 0! #690 1! -b10010 7 b10010 " -b10000 9 +b10010 7 b01000 6 +b10000 9 b00000000000000000000000001000101 # b01001 8 #695 @@ -722,18 +724,18 @@ b01001 8 1! b10000 8 b00000000000000000000000001000110 # -b00100 6 b01000 9 -b01001 " +b00100 6 b01001 7 +b01001 " #705 0! #710 1! -b10000 7 b10000 " -b00100 9 +b10000 7 b00010 6 +b00100 9 b00000000000000000000000001000111 # b01000 8 #715 @@ -742,18 +744,18 @@ b01000 8 1! b00100 8 b00000000000000000000000001001000 # -b00001 6 b00010 9 -b01000 " +b00001 6 b01000 7 +b01000 " #725 0! #730 1! -b00100 7 b00100 " -b00001 9 +b00100 7 b10100 6 +b00001 9 b00000000000000000000000001001001 # b00010 8 #735 @@ -762,18 +764,18 @@ b00010 8 1! b00001 8 b00000000000000000000000001001010 # -b01010 6 b10100 9 -b00010 " +b01010 6 b00010 7 +b00010 " #745 0! #750 1! -b00001 7 b00001 " -b01010 9 +b00001 7 b00101 6 +b01010 9 b00000000000000000000000001001011 # b10100 8 #755 @@ -782,18 +784,18 @@ b10100 8 1! b01010 8 b00000000000000000000000001001100 # -b10110 6 b00101 9 -b10100 " +b10110 6 b10100 7 +b10100 " #765 0! #770 1! -b01010 7 b01010 " -b10110 9 +b01010 7 b01011 6 +b10110 9 b00000000000000000000000001001101 # b00101 8 #775 @@ -802,18 +804,18 @@ b00101 8 1! b10110 8 b00000000000000000000000001001110 # -b10001 6 b01011 9 -b00101 " +b10001 6 b00101 7 +b00101 " #785 0! #790 1! -b10110 7 b10110 " -b10001 9 +b10110 7 b11100 6 +b10001 9 b00000000000000000000000001001111 # b01011 8 #795 @@ -822,18 +824,18 @@ b01011 8 1! b10001 8 b00000000000000000000000001010000 # -b01110 6 b11100 9 -b01011 " +b01110 6 b01011 7 +b01011 " #805 0! #810 1! -b10001 7 b10001 " -b01110 9 +b10001 7 b00111 6 +b01110 9 b00000000000000000000000001010001 # b11100 8 #815 @@ -842,18 +844,18 @@ b11100 8 1! b01110 8 b00000000000000000000000001010010 # -b10111 6 b00111 9 -b11100 " +b10111 6 b11100 7 +b11100 " #825 0! #830 1! -b01110 7 b01110 " -b10111 9 +b01110 7 b11111 6 +b10111 9 b00000000000000000000000001010011 # b00111 8 #835 @@ -862,18 +864,18 @@ b00111 8 1! b10111 8 b00000000000000000000000001010100 # -b11011 6 b11111 9 -b00111 " +b11011 6 b00111 7 +b00111 " #845 0! #850 1! -b10111 7 b10111 " -b11011 9 +b10111 7 b11001 6 +b11011 9 b00000000000000000000000001010101 # b11111 8 #855 @@ -882,18 +884,18 @@ b11111 8 1! b11011 8 b00000000000000000000000001010110 # -b11000 6 b11001 9 -b11111 " +b11000 6 b11111 7 +b11111 " #865 0! #870 1! -b11011 7 b11011 " -b11000 9 +b11011 7 b01100 6 +b11000 9 b00000000000000000000000001010111 # b11001 8 #875 @@ -902,18 +904,18 @@ b11001 8 1! b11000 8 b00000000000000000000000001011000 # -b00110 6 b01100 9 -b11001 " +b00110 6 b11001 7 +b11001 " #885 0! #890 1! -b11000 7 b11000 " -b00110 9 +b11000 7 b00011 6 +b00110 9 b00000000000000000000000001011001 # b01100 8 #895 @@ -922,18 +924,18 @@ b01100 8 1! b00110 8 b00000000000000000000000001011010 # -b10101 6 b00011 9 -b01100 " +b10101 6 b01100 7 +b01100 " #905 0! #910 1! -b00110 7 b00110 " -b10101 9 +b00110 7 b11110 6 +b10101 9 b00000000000000000000000001011011 # b00011 8 #915 @@ -942,18 +944,18 @@ b00011 8 1! b10101 8 b00000000000000000000000001011100 # -b01111 6 b11110 9 -b00011 " +b01111 6 b00011 7 +b00011 " #925 0! #930 1! -b10101 7 b10101 " -b01111 9 +b10101 7 b10011 6 +b01111 9 b00000000000000000000000001011101 # b11110 8 #935 @@ -962,18 +964,18 @@ b11110 8 1! b01111 8 b00000000000000000000000001011110 # -b11101 6 b10011 9 -b11110 " +b11101 6 b11110 7 +b11110 " #945 0! #950 1! -b01111 7 b01111 " -b11101 9 +b01111 7 b11010 6 +b11101 9 b00000000000000000000000001011111 # b10011 8 #955 @@ -982,18 +984,18 @@ b10011 8 1! b11101 8 b00000000000000000000000001100000 # -b01101 6 b11010 9 -b10011 " +b01101 6 b10011 7 +b10011 " #965 0! #970 1! -b11101 7 b11101 " -b01101 9 +b11101 7 b10010 6 +b01101 9 b00000000000000000000000001100001 # b11010 8 #975 @@ -1002,18 +1004,18 @@ b11010 8 1! b01101 8 b00000000000000000000000001100010 # -b01001 6 b10010 9 -b11010 " +b01001 6 b11010 7 +b11010 " #985 0! #990 1! -b01101 7 b01101 " -b01001 9 +b01101 7 b10000 6 +b01001 9 b00000000000000000000000001100011 # b10010 8 #995 @@ -1022,7 +1024,7 @@ b10010 8 1! b01001 8 b00000000000000000000000001100100 # -b01000 6 b10000 9 -b10010 " +b01000 6 b10010 7 +b10010 " diff --git a/test_regress/t/t_trace_fst_cmake.out b/test_regress/t/t_trace_fst_cmake.out index dde4f07e0..d3e911409 100644 --- a/test_regress/t/t_trace_fst_cmake.out +++ b/test_regress/t/t_trace_fst_cmake.out @@ -1,5 +1,5 @@ $date - Sun Apr 19 04:15:36 2020 + Tue Aug 10 15:48:40 2021 $end $version @@ -39,9 +39,9 @@ $var wire 1 ! clk $end $var wire 1 $ rstn $end $var wire 5 " state $end $var logic 5 6 state_w $end -$var logic 5 7 state_array(0) $end -$var logic 5 8 state_array(1) $end -$var logic 5 9 state_array(2) $end +$var logic 5 7 state_array[0] $end +$var logic 5 8 state_array[1] $end +$var logic 5 9 state_array[2] $end $scope module unnamedblk2 $end $var int 32 : i $end $upscope $end @@ -52,43 +52,45 @@ $upscope $end $upscope $end $upscope $end $enddefinitions $end +#0 $dumpvars -0! -b00000 " -b00000000000000000000000000000000 # -0$ -r1.23 % -r4.56 & -b00000000000000000000000000000000 ' -0( -0) -b00000000000000000000000000000000 * -b0000000000000000 + -b0000000000000000000000000000000000000000000000000000000000000000 , -b00000000 - -b00000000000000000000000001111011 . -b00000000000000000000000111001000 / -00 -11 -02 -13 -04 -05 -b00000 6 -b00000 7 -b00000 8 -b00000 9 -b00000000000000000000000000000000 : b00000000000000000000000000000000 ; +b00000000000000000000000000000000 : +b00000 9 +b00000 8 +b00000 7 +b00000 6 +05 +04 +13 +02 +11 +00 +b00000000000000000000000111001000 / +b00000000000000000000000001111011 . +b00000000 - +b0000000000000000000000000000000000000000000000000000000000000000 , +b0000000000000000 + +b00000000000000000000000000000000 * +0) +0( +b00000000000000000000000000000000 ' +r4.56 & +r1.23 % +0$ +b00000000000000000000000000000000 # +b00000 " +0! +$end #10 -b00000000000000000000000000000011 ; -b00001 9 -b00001 8 -b00001 7 -b10100 6 -b00000000000000000000000000000001 # -b00001 " 1! +b00001 " +b00000000000000000000000000000001 # +b10100 6 +b00001 7 +b00001 8 +b00001 9 +b00000000000000000000000000000011 ; #15 0! #20 @@ -145,15 +147,15 @@ b00000000000000000000000000001011 # #120 1! b00000000000000000000000000001100 # -b01010 6 b10100 9 +b01010 6 b00000000000000000000000000000010 : #125 0! #130 1! -b01010 9 b00101 6 +b01010 9 b00000000000000000000000000001101 # b10100 8 #135 @@ -162,18 +164,18 @@ b10100 8 1! b01010 8 b00000000000000000000000000001110 # -b10110 6 b00101 9 -b10100 " +b10110 6 b10100 7 +b10100 " #145 0! #150 1! -b01010 7 b01010 " -b10110 9 +b01010 7 b01011 6 +b10110 9 b00000000000000000000000000001111 # b00101 8 #155 @@ -182,18 +184,18 @@ b00101 8 1! b10110 8 b00000000000000000000000000010000 # -b10001 6 b01011 9 -b00101 " +b10001 6 b00101 7 +b00101 " #165 0! #170 1! -b10110 7 b10110 " -b10001 9 +b10110 7 b11100 6 +b10001 9 b00000000000000000000000000010001 # b01011 8 #175 @@ -202,18 +204,18 @@ b01011 8 1! b10001 8 b00000000000000000000000000010010 # -b01110 6 b11100 9 -b01011 " +b01110 6 b01011 7 +b01011 " #185 0! #190 1! -b10001 7 b10001 " -b01110 9 +b10001 7 b00111 6 +b01110 9 b00000000000000000000000000010011 # b11100 8 #195 @@ -222,18 +224,18 @@ b11100 8 1! b01110 8 b00000000000000000000000000010100 # -b10111 6 b00111 9 -b11100 " +b10111 6 b11100 7 +b11100 " #205 0! #210 1! -b01110 7 b01110 " -b10111 9 +b01110 7 b11111 6 +b10111 9 b00000000000000000000000000010101 # b00111 8 #215 @@ -242,18 +244,18 @@ b00111 8 1! b10111 8 b00000000000000000000000000010110 # -b11011 6 b11111 9 -b00111 " +b11011 6 b00111 7 +b00111 " #225 0! #230 1! -b10111 7 b10111 " -b11011 9 +b10111 7 b11001 6 +b11011 9 b00000000000000000000000000010111 # b11111 8 #235 @@ -262,18 +264,18 @@ b11111 8 1! b11011 8 b00000000000000000000000000011000 # -b11000 6 b11001 9 -b11111 " +b11000 6 b11111 7 +b11111 " #245 0! #250 1! -b11011 7 b11011 " -b11000 9 +b11011 7 b01100 6 +b11000 9 b00000000000000000000000000011001 # b11001 8 #255 @@ -282,18 +284,18 @@ b11001 8 1! b11000 8 b00000000000000000000000000011010 # -b00110 6 b01100 9 -b11001 " +b00110 6 b11001 7 +b11001 " #265 0! #270 1! -b11000 7 b11000 " -b00110 9 +b11000 7 b00011 6 +b00110 9 b00000000000000000000000000011011 # b01100 8 #275 @@ -302,18 +304,18 @@ b01100 8 1! b00110 8 b00000000000000000000000000011100 # -b10101 6 b00011 9 -b01100 " +b10101 6 b01100 7 +b01100 " #285 0! #290 1! -b00110 7 b00110 " -b10101 9 +b00110 7 b11110 6 +b10101 9 b00000000000000000000000000011101 # b00011 8 #295 @@ -322,18 +324,18 @@ b00011 8 1! b10101 8 b00000000000000000000000000011110 # -b01111 6 b11110 9 -b00011 " +b01111 6 b00011 7 +b00011 " #305 0! #310 1! -b10101 7 b10101 " -b01111 9 +b10101 7 b10011 6 +b01111 9 b00000000000000000000000000011111 # b11110 8 #315 @@ -342,18 +344,18 @@ b11110 8 1! b01111 8 b00000000000000000000000000100000 # -b11101 6 b10011 9 -b11110 " +b11101 6 b11110 7 +b11110 " #325 0! #330 1! -b01111 7 b01111 " -b11101 9 +b01111 7 b11010 6 +b11101 9 b00000000000000000000000000100001 # b10011 8 #335 @@ -362,18 +364,18 @@ b10011 8 1! b11101 8 b00000000000000000000000000100010 # -b01101 6 b11010 9 -b10011 " +b01101 6 b10011 7 +b10011 " #345 0! #350 1! -b11101 7 b11101 " -b01101 9 +b11101 7 b10010 6 +b01101 9 b00000000000000000000000000100011 # b11010 8 #355 @@ -382,18 +384,18 @@ b11010 8 1! b01101 8 b00000000000000000000000000100100 # -b01001 6 b10010 9 -b11010 " +b01001 6 b11010 7 +b11010 " #365 0! #370 1! -b01101 7 b01101 " -b01001 9 +b01101 7 b10000 6 +b01001 9 b00000000000000000000000000100101 # b10010 8 #375 @@ -402,18 +404,18 @@ b10010 8 1! b01001 8 b00000000000000000000000000100110 # -b01000 6 b10000 9 -b10010 " +b01000 6 b10010 7 +b10010 " #385 0! #390 1! -b01001 7 b01001 " -b01000 9 +b01001 7 b00100 6 +b01000 9 b00000000000000000000000000100111 # b10000 8 #395 @@ -422,18 +424,18 @@ b10000 8 1! b01000 8 b00000000000000000000000000101000 # -b00010 6 b00100 9 -b10000 " +b00010 6 b10000 7 +b10000 " #405 0! #410 1! -b01000 7 b01000 " -b00010 9 +b01000 7 b00001 6 +b00010 9 b00000000000000000000000000101001 # b00100 8 #415 @@ -442,18 +444,18 @@ b00100 8 1! b00010 8 b00000000000000000000000000101010 # -b10100 6 b00001 9 -b00100 " +b10100 6 b00100 7 +b00100 " #425 0! #430 1! -b00010 7 b00010 " -b10100 9 +b00010 7 b01010 6 +b10100 9 b00000000000000000000000000101011 # b00001 8 #435 @@ -462,18 +464,18 @@ b00001 8 1! b10100 8 b00000000000000000000000000101100 # -b00101 6 b01010 9 -b00001 " +b00101 6 b00001 7 +b00001 " #445 0! #450 1! -b10100 7 b10100 " -b00101 9 +b10100 7 b10110 6 +b00101 9 b00000000000000000000000000101101 # b01010 8 #455 @@ -482,18 +484,18 @@ b01010 8 1! b00101 8 b00000000000000000000000000101110 # -b01011 6 b10110 9 -b01010 " +b01011 6 b01010 7 +b01010 " #465 0! #470 1! -b00101 7 b00101 " -b01011 9 +b00101 7 b10001 6 +b01011 9 b00000000000000000000000000101111 # b10110 8 #475 @@ -502,18 +504,18 @@ b10110 8 1! b01011 8 b00000000000000000000000000110000 # -b11100 6 b10001 9 -b10110 " +b11100 6 b10110 7 +b10110 " #485 0! #490 1! -b01011 7 b01011 " -b11100 9 +b01011 7 b01110 6 +b11100 9 b00000000000000000000000000110001 # b10001 8 #495 @@ -522,18 +524,18 @@ b10001 8 1! b11100 8 b00000000000000000000000000110010 # -b00111 6 b01110 9 -b10001 " +b00111 6 b10001 7 +b10001 " #505 0! #510 1! -b11100 7 b11100 " -b00111 9 +b11100 7 b10111 6 +b00111 9 b00000000000000000000000000110011 # b01110 8 #515 @@ -542,18 +544,18 @@ b01110 8 1! b00111 8 b00000000000000000000000000110100 # -b11111 6 b10111 9 -b01110 " +b11111 6 b01110 7 +b01110 " #525 0! #530 1! -b00111 7 b00111 " -b11111 9 +b00111 7 b11011 6 +b11111 9 b00000000000000000000000000110101 # b10111 8 #535 @@ -562,18 +564,18 @@ b10111 8 1! b11111 8 b00000000000000000000000000110110 # -b11001 6 b11011 9 -b10111 " +b11001 6 b10111 7 +b10111 " #545 0! #550 1! -b11111 7 b11111 " -b11001 9 +b11111 7 b11000 6 +b11001 9 b00000000000000000000000000110111 # b11011 8 #555 @@ -582,18 +584,18 @@ b11011 8 1! b11001 8 b00000000000000000000000000111000 # -b01100 6 b11000 9 -b11011 " +b01100 6 b11011 7 +b11011 " #565 0! #570 1! -b11001 7 b11001 " -b01100 9 +b11001 7 b00110 6 +b01100 9 b00000000000000000000000000111001 # b11000 8 #575 @@ -602,18 +604,18 @@ b11000 8 1! b01100 8 b00000000000000000000000000111010 # -b00011 6 b00110 9 -b11000 " +b00011 6 b11000 7 +b11000 " #585 0! #590 1! -b01100 7 b01100 " -b00011 9 +b01100 7 b10101 6 +b00011 9 b00000000000000000000000000111011 # b00110 8 #595 @@ -622,18 +624,18 @@ b00110 8 1! b00011 8 b00000000000000000000000000111100 # -b11110 6 b10101 9 -b00110 " +b11110 6 b00110 7 +b00110 " #605 0! #610 1! -b00011 7 b00011 " -b11110 9 +b00011 7 b01111 6 +b11110 9 b00000000000000000000000000111101 # b10101 8 #615 @@ -642,18 +644,18 @@ b10101 8 1! b11110 8 b00000000000000000000000000111110 # -b10011 6 b01111 9 -b10101 " +b10011 6 b10101 7 +b10101 " #625 0! #630 1! -b11110 7 b11110 " -b10011 9 +b11110 7 b11101 6 +b10011 9 b00000000000000000000000000111111 # b01111 8 #635 @@ -662,18 +664,18 @@ b01111 8 1! b10011 8 b00000000000000000000000001000000 # -b11010 6 b11101 9 -b01111 " +b11010 6 b01111 7 +b01111 " #645 0! #650 1! -b10011 7 b10011 " -b11010 9 +b10011 7 b01101 6 +b11010 9 b00000000000000000000000001000001 # b11101 8 #655 @@ -682,18 +684,18 @@ b11101 8 1! b11010 8 b00000000000000000000000001000010 # -b10010 6 b01101 9 -b11101 " +b10010 6 b11101 7 +b11101 " #665 0! #670 1! -b11010 7 b11010 " -b10010 9 +b11010 7 b01001 6 +b10010 9 b00000000000000000000000001000011 # b01101 8 #675 @@ -702,18 +704,18 @@ b01101 8 1! b10010 8 b00000000000000000000000001000100 # -b10000 6 b01001 9 -b01101 " +b10000 6 b01101 7 +b01101 " #685 0! #690 1! -b10010 7 b10010 " -b10000 9 +b10010 7 b01000 6 +b10000 9 b00000000000000000000000001000101 # b01001 8 #695 @@ -722,18 +724,18 @@ b01001 8 1! b10000 8 b00000000000000000000000001000110 # -b00100 6 b01000 9 -b01001 " +b00100 6 b01001 7 +b01001 " #705 0! #710 1! -b10000 7 b10000 " -b00100 9 +b10000 7 b00010 6 +b00100 9 b00000000000000000000000001000111 # b01000 8 #715 @@ -742,18 +744,18 @@ b01000 8 1! b00100 8 b00000000000000000000000001001000 # -b00001 6 b00010 9 -b01000 " +b00001 6 b01000 7 +b01000 " #725 0! #730 1! -b00100 7 b00100 " -b00001 9 +b00100 7 b10100 6 +b00001 9 b00000000000000000000000001001001 # b00010 8 #735 @@ -762,18 +764,18 @@ b00010 8 1! b00001 8 b00000000000000000000000001001010 # -b01010 6 b10100 9 -b00010 " +b01010 6 b00010 7 +b00010 " #745 0! #750 1! -b00001 7 b00001 " -b01010 9 +b00001 7 b00101 6 +b01010 9 b00000000000000000000000001001011 # b10100 8 #755 @@ -782,18 +784,18 @@ b10100 8 1! b01010 8 b00000000000000000000000001001100 # -b10110 6 b00101 9 -b10100 " +b10110 6 b10100 7 +b10100 " #765 0! #770 1! -b01010 7 b01010 " -b10110 9 +b01010 7 b01011 6 +b10110 9 b00000000000000000000000001001101 # b00101 8 #775 @@ -802,18 +804,18 @@ b00101 8 1! b10110 8 b00000000000000000000000001001110 # -b10001 6 b01011 9 -b00101 " +b10001 6 b00101 7 +b00101 " #785 0! #790 1! -b10110 7 b10110 " -b10001 9 +b10110 7 b11100 6 +b10001 9 b00000000000000000000000001001111 # b01011 8 #795 @@ -822,18 +824,18 @@ b01011 8 1! b10001 8 b00000000000000000000000001010000 # -b01110 6 b11100 9 -b01011 " +b01110 6 b01011 7 +b01011 " #805 0! #810 1! -b10001 7 b10001 " -b01110 9 +b10001 7 b00111 6 +b01110 9 b00000000000000000000000001010001 # b11100 8 #815 @@ -842,18 +844,18 @@ b11100 8 1! b01110 8 b00000000000000000000000001010010 # -b10111 6 b00111 9 -b11100 " +b10111 6 b11100 7 +b11100 " #825 0! #830 1! -b01110 7 b01110 " -b10111 9 +b01110 7 b11111 6 +b10111 9 b00000000000000000000000001010011 # b00111 8 #835 @@ -862,18 +864,18 @@ b00111 8 1! b10111 8 b00000000000000000000000001010100 # -b11011 6 b11111 9 -b00111 " +b11011 6 b00111 7 +b00111 " #845 0! #850 1! -b10111 7 b10111 " -b11011 9 +b10111 7 b11001 6 +b11011 9 b00000000000000000000000001010101 # b11111 8 #855 @@ -882,18 +884,18 @@ b11111 8 1! b11011 8 b00000000000000000000000001010110 # -b11000 6 b11001 9 -b11111 " +b11000 6 b11111 7 +b11111 " #865 0! #870 1! -b11011 7 b11011 " -b11000 9 +b11011 7 b01100 6 +b11000 9 b00000000000000000000000001010111 # b11001 8 #875 @@ -902,18 +904,18 @@ b11001 8 1! b11000 8 b00000000000000000000000001011000 # -b00110 6 b01100 9 -b11001 " +b00110 6 b11001 7 +b11001 " #885 0! #890 1! -b11000 7 b11000 " -b00110 9 +b11000 7 b00011 6 +b00110 9 b00000000000000000000000001011001 # b01100 8 #895 @@ -922,18 +924,18 @@ b01100 8 1! b00110 8 b00000000000000000000000001011010 # -b10101 6 b00011 9 -b01100 " +b10101 6 b01100 7 +b01100 " #905 0! #910 1! -b00110 7 b00110 " -b10101 9 +b00110 7 b11110 6 +b10101 9 b00000000000000000000000001011011 # b00011 8 #915 @@ -942,18 +944,18 @@ b00011 8 1! b10101 8 b00000000000000000000000001011100 # -b01111 6 b11110 9 -b00011 " +b01111 6 b00011 7 +b00011 " #925 0! #930 1! -b10101 7 b10101 " -b01111 9 +b10101 7 b10011 6 +b01111 9 b00000000000000000000000001011101 # b11110 8 #935 @@ -962,18 +964,18 @@ b11110 8 1! b01111 8 b00000000000000000000000001011110 # -b11101 6 b10011 9 -b11110 " +b11101 6 b11110 7 +b11110 " #945 0! #950 1! -b01111 7 b01111 " -b11101 9 +b01111 7 b11010 6 +b11101 9 b00000000000000000000000001011111 # b10011 8 #955 @@ -982,18 +984,18 @@ b10011 8 1! b11101 8 b00000000000000000000000001100000 # -b01101 6 b11010 9 -b10011 " +b01101 6 b10011 7 +b10011 " #965 0! #970 1! -b11101 7 b11101 " -b01101 9 +b11101 7 b10010 6 +b01101 9 b00000000000000000000000001100001 # b11010 8 #975 @@ -1002,18 +1004,18 @@ b11010 8 1! b01101 8 b00000000000000000000000001100010 # -b01001 6 b10010 9 -b11010 " +b01001 6 b11010 7 +b11010 " #985 0! #990 1! -b01101 7 b01101 " -b01001 9 +b01101 7 b10000 6 +b01001 9 b00000000000000000000000001100011 # b10010 8 #995 @@ -1022,7 +1024,7 @@ b10010 8 1! b01001 8 b00000000000000000000000001100100 # -b01000 6 b10000 9 -b10010 " +b01000 6 b10010 7 +b10010 " diff --git a/test_regress/t/t_trace_fst_sc.out b/test_regress/t/t_trace_fst_sc.out index 3fe026e70..28581fdda 100644 --- a/test_regress/t/t_trace_fst_sc.out +++ b/test_regress/t/t_trace_fst_sc.out @@ -1,5 +1,5 @@ $date - Thu Apr 1 14:28:55 2021 + Wed Aug 11 00:05:19 2021 $end $version @@ -37,9 +37,9 @@ $var wire 1 ! clk $end $var wire 1 # rstn $end $var wire 5 5 state $end $var logic 5 6 state_w $end -$var logic 5 7 state_array(0) $end -$var logic 5 8 state_array(1) $end -$var logic 5 9 state_array(2) $end +$var logic 5 7 state_array[0] $end +$var logic 5 8 state_array[1] $end +$var logic 5 9 state_array[2] $end $scope module unnamedblk2 $end $var int 32 : i $end $upscope $end diff --git a/test_regress/t/t_trace_fst_sc_cmake.out b/test_regress/t/t_trace_fst_sc_cmake.out index 3fe026e70..79ed28e84 100644 --- a/test_regress/t/t_trace_fst_sc_cmake.out +++ b/test_regress/t/t_trace_fst_sc_cmake.out @@ -1,5 +1,5 @@ $date - Thu Apr 1 14:28:55 2021 + Wed Aug 11 02:14:06 2021 $end $version @@ -37,9 +37,9 @@ $var wire 1 ! clk $end $var wire 1 # rstn $end $var wire 5 5 state $end $var logic 5 6 state_w $end -$var logic 5 7 state_array(0) $end -$var logic 5 8 state_array(1) $end -$var logic 5 9 state_array(2) $end +$var logic 5 7 state_array[0] $end +$var logic 5 8 state_array[1] $end +$var logic 5 9 state_array[2] $end $scope module unnamedblk2 $end $var int 32 : i $end $upscope $end diff --git a/test_regress/t/t_trace_packed_struct_fst.out b/test_regress/t/t_trace_packed_struct_fst.out index ac5cfac00..05ed7013f 100644 --- a/test_regress/t/t_trace_packed_struct_fst.out +++ b/test_regress/t/t_trace_packed_struct_fst.out @@ -1,5 +1,5 @@ $date - Sun Apr 19 04:15:38 2020 + Wed Aug 11 12:42:37 2021 $end $version @@ -13,21 +13,23 @@ $var wire 1 ! clk $end $scope module t $end $var wire 1 ! clk $end $var int 32 " cnt $end -$var parameter 96 # v(0) $end -$var parameter 96 $ v(1) $end -$var parameter 96 % v(2) $end +$var parameter 96 # v[0] $end +$var parameter 96 $ v[1] $end +$var parameter 96 % v[2] $end $upscope $end $upscope $end $enddefinitions $end +#0 $dumpvars -0! -b00000000000000000000000000000000 " -b001100000000000000000000000000100011000000000000000000000000000100110000000000000000000000000000 # -b001000000000000000000000000000100010000000000000000000000000000100100000000000000000000000000000 $ b000100000000000000000000000000100001000000000000000000000000000100010000000000000000000000000000 % +b001000000000000000000000000000100010000000000000000000000000000100100000000000000000000000000000 $ +b001100000000000000000000000000100011000000000000000000000000000100110000000000000000000000000000 # +b00000000000000000000000000000000 " +0! +$end #10 -b00000000000000000000000000000001 " 1! +b00000000000000000000000000000001 " #15 0! #20 diff --git a/test_regress/t/t_trace_packed_struct_fst_sc.out b/test_regress/t/t_trace_packed_struct_fst_sc.out index 7c9ef71f8..c42a1574d 100644 --- a/test_regress/t/t_trace_packed_struct_fst_sc.out +++ b/test_regress/t/t_trace_packed_struct_fst_sc.out @@ -1,5 +1,5 @@ $date - Thu Apr 1 15:33:45 2021 + Wed Aug 11 12:42:40 2021 $end $version @@ -12,9 +12,9 @@ $scope module top $end $scope module t $end $var wire 1 ! clk $end $var int 32 " cnt $end -$var parameter 96 # v(0) $end -$var parameter 96 $ v(1) $end -$var parameter 96 % v(2) $end +$var parameter 96 # v[0] $end +$var parameter 96 $ v[1] $end +$var parameter 96 % v[2] $end $upscope $end $upscope $end $enddefinitions $end diff --git a/test_regress/t/t_trace_public.out b/test_regress/t/t_trace_public.out index 01536b3b7..e9ef906b0 100644 --- a/test_regress/t/t_trace_public.out +++ b/test_regress/t/t_trace_public.out @@ -1,31 +1,30 @@ -$version Generated by SpTraceVcd $end -$date Tue Nov 3 09:34:23 2009 - $end +$version Generated by VerilatedVcd $end +$date Tue Aug 10 15:49:51 2021 $end $timescale 1ps $end $scope module top $end - $var wire 1 6 CLK $end - $var wire 1 7 RESET $end + $var wire 1 5 CLK $end + $var wire 1 6 RESET $end $scope module t $end - $var wire 1 6 CLK $end + $var wire 1 5 CLK $end $var wire 1 # RESET $end - $var wire 32 $ val [31:0] $end - $var wire 2 3 vec(3) [2:1] $end - $var wire 2 4 vec(4) [2:1] $end + $var wire 32 & val [31:0] $end + $var wire 2 $ vec[3] [2:1] $end + $var wire 2 % vec[4] [2:1] $end $scope module glbl $end - $var wire 1 5 GSR $end + $var wire 1 7 GSR $end $upscope $end $scope module little $end - $var wire 1 6 clk $end - $var wire 128 / i128 [63:190] $end - $var wire 49 - i48 [1:49] $end - $var wire 8 , i8 [0:7] $end + $var wire 1 5 clk $end + $var wire 128 1 i128 [63:190] $end + $var wire 49 / i48 [1:49] $end + $var wire 8 . i8 [0:7] $end $upscope $end $scope module neg $end - $var wire 1 6 clk $end - $var wire 128 ( i128 [63:-64] $end - $var wire 48 & i48 [-1:-48] $end - $var wire 8 % i8 [0:-7] $end + $var wire 1 5 clk $end + $var wire 128 * i128 [63:-64] $end + $var wire 48 ( i48 [-1:-48] $end + $var wire 8 ' i8 [0:-7] $end $upscope $end $upscope $end $upscope $end @@ -34,63 +33,63 @@ $enddefinitions $end #0 1# -b00000000000000000000000000000000 $ -b00000000 % -b000000000000000000000000000000000000000000000000 & -b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ( -b00000000 , -b0000000000000000000000000000000000000000000000000 - -b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 / -b00 3 -b00 4 -15 +b00 $ +b00 % +b00000000000000000000000000000000 & +b00000000 ' +b000000000000000000000000000000000000000000000000 ( +b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 * +b00000000 . +b0000000000000000000000000000000000000000000000000 / +b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1 +05 +16 17 -06 #1 #2 #3 -b11111111 % -b111111111111111111111111111111111111111111111111 & -b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 ( -b11111111 , -b1111111111111111111111111111111111111111111111111 - -b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 / -16 +b11111111 ' +b111111111111111111111111111111111111111111111111 ( +b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 * +b11111111 . +b1111111111111111111111111111111111111111111111111 / +b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 1 +15 #4 #5 #6 -06 -#7 05 +#7 +07 #8 #9 0# -b00000000 % -b000000000000000000000000000000000000000000000000 & -b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ( -b00000000 , -b0000000000000000000000000000000000000000000000000 - -b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 / -07 -16 +b00000000 ' +b000000000000000000000000000000000000000000000000 ( +b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 * +b00000000 . +b0000000000000000000000000000000000000000000000000 / +b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1 +15 +06 #10 #11 #12 -06 +05 #13 #14 #15 -b00000000000000000000000000000001 $ -b11111111 % -b111111111111111111111111111111111111111111111111 & -b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 ( -b11111111 , -b1111111111111111111111111111111111111111111111111 - -b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 / -16 +b00000000000000000000000000000001 & +b11111111 ' +b111111111111111111111111111111111111111111111111 ( +b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 * +b11111111 . +b1111111111111111111111111111111111111111111111111 / +b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 1 +15 #16 #17 #18 -06 +05 #19 #20 diff --git a/test_regress/t/t_trace_two_cc.cpp b/test_regress/t/t_trace_two_cc.cpp index 02c639418..238877e5f 100644 --- a/test_regress/t/t_trace_two_cc.cpp +++ b/test_regress/t/t_trace_two_cc.cpp @@ -21,12 +21,7 @@ // clang-format on // Compile in place -#include "Vt_trace_two_b.cpp" -#include "Vt_trace_two_b__Syms.cpp" -#include "Vt_trace_two_b___024root.cpp" -#include "Vt_trace_two_b___024root__Slow.cpp" -#include "Vt_trace_two_b__Trace.cpp" -#include "Vt_trace_two_b__Trace__Slow.cpp" +#include "Vt_trace_two_b__ALL.cpp" VM_PREFIX* ap; Vt_trace_two_b* bp; diff --git a/test_regress/t/t_trace_two_dump_cc.pl b/test_regress/t/t_trace_two_dump_cc.pl index 0b20803a9..d4643e039 100755 --- a/test_regress/t/t_trace_two_dump_cc.pl +++ b/test_regress/t/t_trace_two_dump_cc.pl @@ -21,6 +21,11 @@ compile( verilator_flags2 => ['-trace'], ); +run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_dump_sc.pl b/test_regress/t/t_trace_two_dump_sc.pl index 311848fe7..dd789a58b 100755 --- a/test_regress/t/t_trace_two_dump_sc.pl +++ b/test_regress/t/t_trace_two_dump_sc.pl @@ -24,6 +24,11 @@ else { verilator_flags2 => ['-sc -trace'], ); + run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_dumpfst_cc.pl b/test_regress/t/t_trace_two_dumpfst_cc.pl index 37f00db1c..4c1b2c968 100755 --- a/test_regress/t/t_trace_two_dumpfst_cc.pl +++ b/test_regress/t/t_trace_two_dumpfst_cc.pl @@ -21,6 +21,11 @@ compile( verilator_flags2 => ['--trace-fst --trace-threads 1 -DTEST_FST'], ); +run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_hdr_cc.pl b/test_regress/t/t_trace_two_hdr_cc.pl index 352e12450..f0c386bbb 100755 --- a/test_regress/t/t_trace_two_hdr_cc.pl +++ b/test_regress/t/t_trace_two_hdr_cc.pl @@ -21,6 +21,11 @@ compile( verilator_flags2 => ['-trace'], ); +run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_hdr_sc.pl b/test_regress/t/t_trace_two_hdr_sc.pl index b62a58aef..8374fcfee 100755 --- a/test_regress/t/t_trace_two_hdr_sc.pl +++ b/test_regress/t/t_trace_two_hdr_sc.pl @@ -24,6 +24,11 @@ else { verilator_flags2 => ['-sc -trace'], ); + run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_hdrfst_cc.pl b/test_regress/t/t_trace_two_hdrfst_cc.pl index cbdae9a87..af0d8307f 100755 --- a/test_regress/t/t_trace_two_hdrfst_cc.pl +++ b/test_regress/t/t_trace_two_hdrfst_cc.pl @@ -21,6 +21,11 @@ compile( verilator_flags2 => ['--trace-fst --trace-threads 1'], ); +run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_port_cc.pl b/test_regress/t/t_trace_two_port_cc.pl index c35b91acb..7e348f19b 100755 --- a/test_regress/t/t_trace_two_port_cc.pl +++ b/test_regress/t/t_trace_two_port_cc.pl @@ -21,6 +21,11 @@ compile( verilator_flags2 => ['-trace'], ); +run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_port_sc.pl b/test_regress/t/t_trace_two_port_sc.pl index 84676c3a8..e62e64a3b 100755 --- a/test_regress/t/t_trace_two_port_sc.pl +++ b/test_regress/t/t_trace_two_port_sc.pl @@ -24,6 +24,11 @@ else { verilator_flags2 => ['-sc -trace'], ); + run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_portfst_cc.pl b/test_regress/t/t_trace_two_portfst_cc.pl index 689296b42..676bfb508 100755 --- a/test_regress/t/t_trace_two_portfst_cc.pl +++ b/test_regress/t/t_trace_two_portfst_cc.pl @@ -21,6 +21,11 @@ compile( verilator_flags2 => ['--trace-fst --trace-threads 1'], ); +run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_sc.cpp b/test_regress/t/t_trace_two_sc.cpp index 7547853d5..bd4f0ad77 100644 --- a/test_regress/t/t_trace_two_sc.cpp +++ b/test_regress/t/t_trace_two_sc.cpp @@ -17,12 +17,7 @@ // clang-format on // Compile in place -#include "Vt_trace_two_b.cpp" -#include "Vt_trace_two_b___024root.cpp" -#include "Vt_trace_two_b___024root__Slow.cpp" -#include "Vt_trace_two_b__Syms.cpp" -#include "Vt_trace_two_b__Trace.cpp" -#include "Vt_trace_two_b__Trace__Slow.cpp" +#include "Vt_trace_two_b__ALL.cpp" // General headers #include "verilated.h" diff --git a/test_regress/t/t_unopt_combo_isolate.pl b/test_regress/t/t_unopt_combo_isolate.pl index 1fd8571d3..cd9c893bb 100755 --- a/test_regress/t/t_unopt_combo_isolate.pl +++ b/test_regress/t/t_unopt_combo_isolate.pl @@ -19,11 +19,11 @@ compile( if ($Self->{vlt_all}) { file_grep($Self->{stats}, qr/Optimizations, isolate_assignments blocks\s+5/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); } execute( diff --git a/test_regress/t/t_unopt_combo_isolate_vlt.pl b/test_regress/t/t_unopt_combo_isolate_vlt.pl index 8010fe402..a71d1a004 100755 --- a/test_regress/t/t_unopt_combo_isolate_vlt.pl +++ b/test_regress/t/t_unopt_combo_isolate_vlt.pl @@ -19,11 +19,11 @@ compile( if ($Self->{vlt_all}) { file_grep($Self->{stats}, qr/Optimizations, isolate_assignments blocks\s+5/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); } execute( diff --git a/test_regress/t/t_unpacked_str_init.pl b/test_regress/t/t_unpacked_str_init.pl index 235ca326a..baf0745f9 100755 --- a/test_regress/t/t_unpacked_str_init.pl +++ b/test_regress/t/t_unpacked_str_init.pl @@ -10,8 +10,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -# TODO change to compile() -lint( +compile( ); # No execute, not self-checking diff --git a/test_regress/t/t_unpacked_str_init2.out b/test_regress/t/t_unpacked_str_init2.out new file mode 100644 index 000000000..349ba5028 --- /dev/null +++ b/test_regress/t/t_unpacked_str_init2.out @@ -0,0 +1,34 @@ +REGX: zero +REGX: ra +REGX: sp +REGX: gp +REGX: tp +REGX: t0 +REGX: t1 +REGX: t2 +REGX: s0/fp +REGX: s1 +REGX: a0 +REGX: a1 +REGX: a2 +REGX: a3 +REGX: a4 +REGX: a5 +REGX: a6 +REGX: a7 +REGX: s2 +REGX: s3 +REGX: s4 +REGX: s5 +REGX: s6 +REGX: s7 +REGX: s8 +REGX: s9 +REGX: s10 +REGX: s11 +REGX: t3 +REGX: t4 +REGX: t5 +REGX: t6 +OP: ILLEGAL +*-* All Finished *-* diff --git a/test_regress/t/t_unpacked_str_init2.pl b/test_regress/t/t_unpacked_str_init2.pl index 235ca326a..bd862f553 100755 --- a/test_regress/t/t_unpacked_str_init2.pl +++ b/test_regress/t/t_unpacked_str_init2.pl @@ -10,11 +10,13 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -# TODO change to compile() -lint( +compile( ); -# No execute, not self-checking +execute( + check_finished => 1, + expect_filename => $Self->{golden_filename} + ); ok(1); 1; diff --git a/test_regress/t/t_vams_basic.v b/test_regress/t/t_vams_basic.v index c658d64e2..8a71bfe42 100644 --- a/test_regress/t/t_vams_basic.v +++ b/test_regress/t/t_vams_basic.v @@ -15,9 +15,9 @@ module t (/*AUTOARG*/ task check (integer line, real got, real expec); real delta; delta = got-expec; - if (delta > 0.001) begin - $display("Line%d: Got %g Exp %g\n", line, got, expec); - $stop; + if (delta > 0.001 || delta < -0.001) begin + $write("Line%0d: Got %g Exp %g\n", line, got, expec); + $stop; end endtask @@ -27,21 +27,30 @@ module t (/*AUTOARG*/ sub sub (.*); initial begin - check(`__LINE__, sqrt(2.0) , 1.414); - check(`__LINE__, pow(2.0,2.0) , 4.0); - check(`__LINE__, ln(2.0) , 0.693147); - check(`__LINE__, log(2.0) , 0.30103); - check(`__LINE__, floor(2.5) , 2.0); - check(`__LINE__, exp(2.0) , 7.38906); - check(`__LINE__, ceil(2.5) , 3.0); + check(`__LINE__, atan(0.5) , 0.463648); + check(`__LINE__, atan2(0.5, 0.3) , 1.03038); + check(`__LINE__, atanh(0.5) , 0.549306); + check(`__LINE__, ceil(2.5) , 3.0); + check(`__LINE__, cos(0.5) , 0.877583); + check(`__LINE__, cosh(0.5) , 1.12763); + check(`__LINE__, exp(2.0) , 7.38906); + check(`__LINE__, floor(2.5) , 2.0); + check(`__LINE__, ln(2.0) , 0.693147); + check(`__LINE__, log(2.0) , 0.30103); + check(`__LINE__, pow(2.0,2.0) , 4.0); + check(`__LINE__, sin(0.5) , 0.479426); + check(`__LINE__, sinh(0.5) , 0.521095); + check(`__LINE__, sqrt(2.0) , 1.414); + check(`__LINE__, tan(0.5) , 0.546302); + check(`__LINE__, tanh(0.5) , 0.462117); $write("*-* All Finished *-*\n"); $finish; end endmodule module sub ( - input wreal wr - ); + input wreal wr + ); initial begin if (wr != 1.1) $stop; end diff --git a/test_regress/t/t_var_port_xml.out b/test_regress/t/t_var_port_xml.out index bf16c637b..5ca861df1 100644 --- a/test_regress/t/t_var_port_xml.out +++ b/test_regress/t/t_var_port_xml.out @@ -121,9 +121,9 @@ - - - + + + diff --git a/test_regress/t/t_var_xref_gen.pl b/test_regress/t/t_var_xref_gen.pl index 873cde7f6..d862fcf2e 100755 --- a/test_regress/t/t_var_xref_gen.pl +++ b/test_regress/t/t_var_xref_gen.pl @@ -8,11 +8,12 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # Version 2.0. # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -scenarios(simulator => 1); +scenarios(vlt => 1); compile( verilator_flags2 => ["--debug-check"], ); ok(1); + 1; diff --git a/test_regress/t/t_verilated_all.pl b/test_regress/t/t_verilated_all.pl index 1d8370346..44b2d3a72 100755 --- a/test_regress/t/t_verilated_all.pl +++ b/test_regress/t/t_verilated_all.pl @@ -53,6 +53,7 @@ foreach my $file (sort keys %hit) { if (!$hit{$file} && $file !~ /_sc/ && $file !~ /_fst/ + && $file !~ /_heavy/ && ($file !~ /_thread/ || $Self->cfg_with_threaded)) { error("Include file not covered by t_verilated_all test: ",$file); } diff --git a/test_regress/t/t_verilated_debug.out b/test_regress/t/t_verilated_debug.out index 5ac844a6b..932ee8483 100644 --- a/test_regress/t/t_verilated_debug.out +++ b/test_regress/t/t_verilated_debug.out @@ -13,18 +13,12 @@ internalsDump: -V{t#,#}+ Initial loop -V{t#,#}+ Vt_verilated_debug___024root___eval_settle -V{t#,#}+ Vt_verilated_debug___024root___eval --V{t#,#}+ Vt_verilated_debug___024root___change_request --V{t#,#}+ Vt_verilated_debug___024root___change_request_1 -V{t#,#}+ Clock loop -V{t#,#}+ Vt_verilated_debug___024root___eval --V{t#,#}+ Vt_verilated_debug___024root___change_request --V{t#,#}+ Vt_verilated_debug___024root___change_request_1 -V{t#,#}+++++TOP Evaluate Vt_verilated_debug::eval_step -V{t#,#}+ Vt_verilated_debug___024root___eval_debug_assertions -V{t#,#}+ Clock loop -V{t#,#}+ Vt_verilated_debug___024root___eval -V{t#,#}+ Vt_verilated_debug___024root___sequent__TOP__2 *-* All Finished *-* --V{t#,#}+ Vt_verilated_debug___024root___change_request --V{t#,#}+ Vt_verilated_debug___024root___change_request_1 -V{t#,#}+ Vt_verilated_debug___024root___final diff --git a/test_regress/t/t_xml_debugcheck.out b/test_regress/t/t_xml_debugcheck.out new file mode 100644 index 000000000..3b62af357 --- /dev/null +++ b/test_regress/t/t_xml_debugcheck.out @@ -0,0 +1,1560 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_regress/t/t_xml_debugcheck.pl b/test_regress/t/t_xml_debugcheck.pl new file mode 100755 index 000000000..392d9a60c --- /dev/null +++ b/test_regress/t/t_xml_debugcheck.pl @@ -0,0 +1,37 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2012 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt => 1); + +my $out_filename = "$Self->{obj_dir}/V$Self->{name}.xml"; + +top_filename("t/t_enum_type_methods.v"); + +compile( + verilator_flags2 => ['--debug-check', '--flatten'], + verilator_make_gmake => 0, + make_top_shell => 0, + make_main => 0, + ); + +files_identical("$out_filename", $Self->{golden_filename}, 'logfile'); + +# make sure that certain tags are present in --debug-check +# that would not be present in --xml-only +file_grep("$out_filename", qr//x); # for and +file_grep("$out_filename", qr/ signed=/x); # for +file_grep("$out_filename", qr/ func=/x); # for + +ok(1); +1; diff --git a/test_regress/t/t_xml_first.out b/test_regress/t/t_xml_first.out index b00ac18b9..929ff89c1 100644 --- a/test_regress/t/t_xml_first.out +++ b/test_regress/t/t_xml_first.out @@ -79,7 +79,7 @@ - + diff --git a/test_regress/t/t_xml_flat.out b/test_regress/t/t_xml_flat.out index 964c5b791..ea35502fc 100644 --- a/test_regress/t/t_xml_flat.out +++ b/test_regress/t/t_xml_flat.out @@ -108,7 +108,7 @@ - + diff --git a/test_regress/t/t_xml_flat_vlvbound.out b/test_regress/t/t_xml_flat_vlvbound.out index 28a1c9e97..aafd3bcf6 100644 --- a/test_regress/t/t_xml_flat_vlvbound.out +++ b/test_regress/t/t_xml_flat_vlvbound.out @@ -68,43 +68,51 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + - - - + + @@ -126,43 +134,51 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + - - - + + @@ -189,11 +205,11 @@ - - + + - - + + diff --git a/test_regress/t/t_xml_tag.out b/test_regress/t/t_xml_tag.out index 873eaa1a0..9c4c044e7 100644 --- a/test_regress/t/t_xml_tag.out +++ b/test_regress/t/t_xml_tag.out @@ -55,7 +55,7 @@ - +