diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 00623317b..4e6f4567f 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -23,6 +23,8 @@ jobs: runs-on: ubuntu-20.04 env: CI_BUILD_STAGE_NAME: build + CI_RUNS_ON: ${{ matrix.os }} + CACHE_KEY: ${{ matrix.os }}-${{ matrix.compiler.cc }}-coverage steps: - uses: actions/checkout@v2 @@ -32,7 +34,7 @@ jobs: cache-name: ccache with: path: ${{ github.workspace }}/.ccache - key: coverage-${{ env.cache-name }}-${{ github.sha }} + key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }} restore-keys: coverage-${{ env.cache-name }} - name: Install dependencies for build @@ -44,14 +46,11 @@ jobs: run: | ./ci/ci-script.bash - mkdir tarball/ - cp -vr bin tarball/ - cp -vr src/obj*/*.gcno tarball/ - tar cvzf verilator-${CI_COMMIT}.tgz -C ./tarball . + tar cvzf verilator-${CI_COMMIT}-coverage.tgz bin src/obj*/*.o src/obj*/*.gcno - uses: actions/upload-artifact@v2 with: - path: verilator-${{ env.CI_COMMIT }}.tgz + path: verilator-${{ env.CI_COMMIT }}-coverage.tgz Test: @@ -73,6 +72,8 @@ jobs: runs-on: ubuntu-20.04 env: CI_BUILD_STAGE_NAME: test + CI_RUNS_ON: ${{ matrix.os }} + CACHE_KEY: ${{ matrix.os }}-${{ matrix.compiler.cc }}-coverage steps: - uses: actions/checkout@v2 @@ -82,14 +83,15 @@ jobs: cache-name: ccache with: path: ${{ github.workspace }}/.ccache - key: coverage-${{ env.cache-name }}-${{ github.sha }} + key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }} restore-keys: coverage-${{ env.cache-name }} - uses: actions/download-artifact@v2 - name: Install Verilator and test dependencies run: | - tar xvzf artifact/verilator-${CI_COMMIT}.tgz + tar xvzf artifact/verilator-${CI_COMMIT}-coverage.tgz + touch src/obj*/*.o src/obj*/*.gcno ./ci/ci-install.bash - name: Test diff --git a/Changes b/Changes index 1547bfedb..cf1962931 100644 --- a/Changes +++ b/Changes @@ -3,6 +3,37 @@ Revision history for Verilator The contributors that suggested a given feature are shown in []. Thanks! +* Verilator 4.110 2021-02-25 + +*** Optimize bit operations and others (#2186) (#2632) (#2633) (#2751) (#2800) [Yutetsu TAKATSUKASA] + +**** Support concat selection (#2721). + +**** Support struct scopes when dumping structs to VCD (#2776) [Alex Torregrosa] + +**** Generate SELRANGE for potentially unreachable code (#2625) (#2754) [Pierre-Henri Horrein] + +**** For --flatten, override inlining of public and no_inline modules (#2761) [James Hanlon] + +**** Fix little endian interface pin swizzling (#2475). [Don Owen] + +**** Fix range inheritance on port without data type (#2753). [Embedded Go] + +**** Fix TIMESCALE warnings on primitives (#2763). [Xuanqi] + +**** Fix to exclude strings from toggle coverage (#2766) (#2767) [Paul Wright] + +**** Fix $fread extra semicolon inside statements. [Leendert van Doorn] + +**** Fix class extends with VM_PARALLEL_BUILDS (#2775). [Iru Cai] + +**** Fix shifts by > 32 bit values (#2785). [qrq992] + +**** Fix examples not flushing vcd (#2787). [Richard E George] + +**** Fix little endian packed array pattern assignment (#2795). [Alex Torregrosa] + + * Verilator 4.108 2021-01-10 ** Many VPI changes for IEEE compatibility, which may alter behavior from previous releases. diff --git a/Makefile.in b/Makefile.in index acb3e43bd..fdf0ef90e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -463,7 +463,7 @@ analyzer-include: -rm -rf examples/*/obj* scan-build $(MAKE) -k examples -format: clang-format yapf +format: clang-format yapf format-pl-exec CLANGFORMAT = clang-format CLANGFORMAT_FLAGS = -i @@ -479,6 +479,8 @@ YAPF_FLAGS = -i YAPF_FILES = \ examples/xml_py/vl_file_copy \ examples/xml_py/vl_hier_graph \ + src/astgen \ + src/bisonpre \ src/config_rev \ src/cppcheck_filtered \ src/flexfix \ @@ -493,6 +495,10 @@ YAPF_FILES = \ yapf: $(YAPF) $(YAPF_FLAGS) $(YAPF_FILES) +format-pl-exec: + -chmod a+x test_regress/t/*.pl + + ftp: info install-msg: diff --git a/bin/verilator b/bin/verilator index e581f0867..e6c16fe2c 100755 --- a/bin/verilator +++ b/bin/verilator @@ -455,11 +455,11 @@ ARGUMENTS"> for the detailed description of these arguments. +verilator+debug Enable debugging +verilator+debugi+ Enable debugging at a level +verilator+help Display help - +verilator+prof+threads+file+I Set profile filename - +verilator+prof+threads+start+I Set profile starting point - +verilator+prof+threads+window+I Set profile duration - +verilator+rand+reset+I Set random reset technique - +verilator+seed+I Set random seed + +verilator+prof+threads+file+ Set profile filename + +verilator+prof+threads+start+ Set profile starting point + +verilator+prof+threads+window+ Set profile duration + +verilator+rand+reset+ Set random reset technique + +verilator+seed+ Set random seed +verilator+noassert Disable assert checking +verilator+V Verbose version and config +verilator+version Show version and exit @@ -756,7 +756,7 @@ is fairly standard across Verilog tools while -D is similar to GCC. Select the debug executable of Verilator (if available), and enable more internal assertions (equivalent to C<--debug-check>), debugging messages -(equivalent to C<--debugi 4>), and intermediate form dump files (equivalent +(equivalent to C<--debugi 3>), and intermediate form dump files (equivalent to C<--dump-treei 3>). =item --debug-check @@ -2546,6 +2546,27 @@ called from Verilog by querying the scope of that function. See the sections on DPI Context Functions and DPI Header Isolation below and the comments within the svdpi.h header for more information. +=head2 DPI Imports that access signals + +If a DPI import accesses a signal through the VPI Verilator will not be +able to know what variables are accessed and may schedule the code +inappropriately. Ideally pass the values as inputs/outputs so the VPI is +not required. Alternatively a workaround is to use a non-inlined task as a +wrapper: + + logic din; + + // This DPI function will read "din" + import "DPI-C" context function void dpi_that_accesses_din(); + + always @ (...) + dpi_din_args(din); + + task dpi_din_args(input din); + /* verilator no_inline_task */ + dpi_that_accesses_din(); + endtask + =head2 DPI Display Functions Verilator allows writing $display like functions using this syntax: @@ -5229,7 +5250,7 @@ want all data to land in the same output file. topp->trace(tfp, 99); // Trace 99 levels of hierarchy tfp->open("obj_dir/t_trace_ena_cc/simx.vcd"); ... - while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { main_time += #; tfp->dump(main_time); } diff --git a/ci/ci-script.bash b/ci/ci-script.bash index bcfc24285..eb3b828cf 100755 --- a/ci/ci-script.bash +++ b/ci/ci-script.bash @@ -51,7 +51,7 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then stat bin/verilator_bin_dbg fi else - nodist/code_coverage --stages 1-2 + nodist/code_coverage --stages 0-2 fi elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then ############################################################################## @@ -97,34 +97,34 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then "$MAKE" -C test_regress SCENARIOS=--vltmt DRIVER_HASHSET=--hashset=1/2 ;; coverage-all) - nodist/code_coverage --stages 3- + nodist/code_coverage --stages 1- ;; coverage-dist) - nodist/code_coverage --stages 3- --scenarios=--dist + nodist/code_coverage --stages 1- --scenarios=--dist ;; coverage-vlt-0) - nodist/code_coverage --stages 3- --scenarios=--vlt --hashset=0/4 + nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=0/4 ;; coverage-vlt-1) - nodist/code_coverage --stages 3- --scenarios=--vlt --hashset=1/4 + nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=1/4 ;; coverage-vlt-2) - nodist/code_coverage --stages 3- --scenarios=--vlt --hashset=2/4 + nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=2/4 ;; coverage-vlt-3) - nodist/code_coverage --stages 3- --scenarios=--vlt --hashset=3/4 + nodist/code_coverage --stages 1- --scenarios=--vlt --hashset=3/4 ;; coverage-vltmt-0) - nodist/code_coverage --stages 3- --scenarios=--vltmt --hashset=0/4 + nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=0/4 ;; coverage-vltmt-1) - nodist/code_coverage --stages 3- --scenarios=--vltmt --hashset=1/4 + nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=1/4 ;; coverage-vltmt-2) - nodist/code_coverage --stages 3- --scenarios=--vltmt --hashset=2/4 + nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=2/4 ;; coverage-vltmt-3) - nodist/code_coverage --stages 3- --scenarios=--vltmt --hashset=3/4 + nodist/code_coverage --stages 1- --scenarios=--vltmt --hashset=3/4 ;; *) fatal "Unknown test: $TESTS" diff --git a/configure.ac b/configure.ac index f3c39b4bd..5cbb6ffe0 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.108 2021-01-10], +AC_INIT([Verilator],[4.110 2021-02-25], [https://verilator.org], [verilator],[https://verilator.org]) # When releasing, also update header of Changes file diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 67b634c82..f3de9d893 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -5,6 +5,8 @@ Please see the Verilator manual for 200+ additional contributors. Thanks to all. Ahmed El-Mahmoudy Alex Chadwick +Àlex Torregrosa +Andreas Kuster Chris Randall Conor McCullough Dan Petrisko @@ -49,6 +51,7 @@ Marshal Qiao Matthew Ballance Michael Killough Mike Popoloski +Morten Borup Petersen Nandu Raj Nathan Kohagen Nathan Myers @@ -57,12 +60,14 @@ Paul Wright Peter Horvath Peter Monsson Philipp Wagner +Pierre-Henri Horrein Pieter Kapsenberg Piotr Binkowski Qingyao Sun Rafal Kapuscik Richard Myers Rupert Swarbrick +Samuel Riedel Sean Cross Sebastien Van Cauwenberghe Sergi Granell diff --git a/docs/TODO b/docs/TODO index ce1b15083..341d51d42 100644 --- a/docs/TODO +++ b/docs/TODO @@ -96,8 +96,6 @@ Don't merge if any combining would form circ logic (out goes back to in) ** Multiple assignments each bit can become single assign with concat Make sure a SEL of a CONCAT can get the single bit back. -** Usually blocks/values - Enable only after certain time, so VL_TIME_I(32) > 0x1e gets eliminated out ** Better ordering of a<=b, b<=c, put all refs to 'b' next to each other to optimize caching ** I-cache packing improvements (what/how?) ** Data cache organization (order of vars in class) diff --git a/examples/cmake_hello_c/Makefile b/examples/cmake_hello_c/Makefile index 212d264bd..c1a2d7c64 100644 --- a/examples/cmake_hello_c/Makefile +++ b/examples/cmake_hello_c/Makefile @@ -54,7 +54,7 @@ run: @echo @echo "-- BUILD -------------------" - cmake --build build + cmake --build build -j @echo @echo "-- RUN ---------------------" diff --git a/examples/cmake_hello_sc/Makefile b/examples/cmake_hello_sc/Makefile index d368370d5..633e4addd 100644 --- a/examples/cmake_hello_sc/Makefile +++ b/examples/cmake_hello_sc/Makefile @@ -90,7 +90,7 @@ run: @echo @echo "-- BUILD -------------------" - cmake --build build + cmake --build build -j @echo @echo "-- RUN ---------------------" diff --git a/examples/cmake_protect_lib/Makefile b/examples/cmake_protect_lib/Makefile index 1d8ac603c..0dba12ccf 100644 --- a/examples/cmake_protect_lib/Makefile +++ b/examples/cmake_protect_lib/Makefile @@ -54,7 +54,7 @@ run: @echo @echo "-- BUILD -------------------" - cmake --build build + cmake --build build -j @echo @echo "-- RUN ---------------------" diff --git a/examples/cmake_tracing_c/Makefile b/examples/cmake_tracing_c/Makefile index a3f0be678..22f59d125 100644 --- a/examples/cmake_tracing_c/Makefile +++ b/examples/cmake_tracing_c/Makefile @@ -54,7 +54,7 @@ run: @echo @echo "-- BUILD -------------------" - cmake --build build + cmake --build build -j @echo @echo "-- RUN ---------------------" diff --git a/examples/cmake_tracing_sc/Makefile b/examples/cmake_tracing_sc/Makefile index 81dcf4b33..65868d5bf 100644 --- a/examples/cmake_tracing_sc/Makefile +++ b/examples/cmake_tracing_sc/Makefile @@ -90,7 +90,7 @@ run: @echo @echo "-- BUILD -------------------" - cmake --build build + cmake --build build -j @echo @echo "-- RUN ---------------------" diff --git a/examples/make_hello_c/sim_main.cpp b/examples/make_hello_c/sim_main.cpp index e95be4fa6..b2c76c84a 100644 --- a/examples/make_hello_c/sim_main.cpp +++ b/examples/make_hello_c/sim_main.cpp @@ -38,5 +38,5 @@ int main(int argc, char** argv, char** env) { delete top; // Return good completion status - exit(0); + return 0; } diff --git a/examples/make_protect_lib/sim_main.cpp b/examples/make_protect_lib/sim_main.cpp index c7762ba54..4d15ea360 100644 --- a/examples/make_protect_lib/sim_main.cpp +++ b/examples/make_protect_lib/sim_main.cpp @@ -71,5 +71,6 @@ int main(int argc, char** argv, char** env) { top = nullptr; // Return good completion status - exit(0); + // Don't use exit() or destructor won't get called + return 0; } diff --git a/examples/make_tracing_c/sim_main.cpp b/examples/make_tracing_c/sim_main.cpp index bcd853162..08fa60098 100644 --- a/examples/make_tracing_c/sim_main.cpp +++ b/examples/make_tracing_c/sim_main.cpp @@ -102,5 +102,6 @@ int main(int argc, char** argv, char** env) { #endif // Return good completion status - exit(0); + // Don't use exit() or destructor won't get called + return 0; } diff --git a/include/verilated.cpp b/include/verilated.cpp index 0a1bf95cd..17949a17c 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -32,6 +32,7 @@ #include #include // mkdir #include +#include #include // clang-format off @@ -44,18 +45,12 @@ constexpr unsigned VL_VALUE_STRING_MAX_WIDTH = 8192; //=========================================================================== -// Static sanity checks (when get C++11 can use static_assert) +// Static sanity checks -typedef union { - // cppcheck-suppress unusedStructMember // Unused as is assertion - char vluint8_incorrect[(sizeof(vluint8_t) == 1) ? 1 : -1]; - // cppcheck-suppress unusedStructMember // Unused as is assertion - char vluint16_incorrect[(sizeof(vluint16_t) == 2) ? 1 : -1]; - // cppcheck-suppress unusedStructMember // Unused as is assertion - char vluint32_incorrect[(sizeof(vluint32_t) == 4) ? 1 : -1]; - // cppcheck-suppress unusedStructMember // Unused as is assertion - char vluint64_incorrect[(sizeof(vluint64_t) == 8) ? 1 : -1]; -} vl_static_checks_t; +static_assert(sizeof(vluint8_t) == 1, "vluint8_t is missized"); +static_assert(sizeof(vluint16_t) == 2, "vluint8_t is missized"); +static_assert(sizeof(vluint32_t) == 4, "vluint8_t is missized"); +static_assert(sizeof(vluint64_t) == 8, "vluint8_t is missized"); //=========================================================================== // Global variables @@ -264,7 +259,6 @@ void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE { // Overall class init Verilated::Serialized::Serialized() { - s_debug = 0; s_calcUnusedSigs = false; s_gotFinish = false; s_assertOn = true; @@ -327,19 +321,16 @@ vluint64_t vl_rand64() VL_MT_SAFE { return result; } +#ifndef VL_NO_LEGACY // VL_RANDOM_W currently unused as $random always 32 bits, left for backwards compatibility // LCOV_EXCL_START WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE { - for (int i = 0; i < VL_WORDS_I(obits); ++i) { - if (i < (VL_WORDS_I(obits) - 1)) { - outwp[i] = vl_rand64(); - } else { - outwp[i] = vl_rand64() & VL_MASK_E(obits); - } - } + for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = vl_rand64(); + outwp[VL_WORDS_I(obits) - 1] = vl_rand64() & VL_MASK_E(obits); return outwp; } // LCOV_EXCL_STOP +#endif IData VL_RANDOM_SEEDED_II(int obits, IData seed) VL_MT_SAFE { Verilated::randSeed(static_cast(seed)); @@ -365,13 +356,8 @@ QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE { return data; } WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE { - for (int i = 0; i < VL_WORDS_I(obits); ++i) { - if (i < (VL_WORDS_I(obits) - 1)) { - outwp[i] = VL_RAND_RESET_I(32); - } else { - outwp[i] = VL_RAND_RESET_I(32) & VL_MASK_E(obits); - } - } + for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = VL_RAND_RESET_I(32); + outwp[VL_WORDS_I(obits) - 1] = VL_RAND_RESET_I(32) & VL_MASK_E(obits); return outwp; } @@ -1342,8 +1328,6 @@ void VL_FCLOSE_I(IData fdi) VL_MT_SAFE { VerilatedImp::fdClose(fdi); } -void VL_FFLUSH_ALL() VL_MT_SAFE { fflush(stdout); } - void VL_SFORMAT_X(int obits, CData& destr, const char* formatp, ...) VL_MT_SAFE { static VL_THREAD_LOCAL std::string t_output; // static only for speed t_output = ""; @@ -1511,19 +1495,19 @@ IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi IData entry = read_elements + start - array_lsb; if (width <= 8) { CData* datap = &(reinterpret_cast(memp))[entry]; - if (shift == start_shift) { *datap = 0; } + if (shift == start_shift) *datap = 0; *datap |= (c << shift) & VL_MASK_I(width); } else if (width <= 16) { SData* datap = &(reinterpret_cast(memp))[entry]; - if (shift == start_shift) { *datap = 0; } + if (shift == start_shift) *datap = 0; *datap |= (c << shift) & VL_MASK_I(width); } else if (width <= VL_IDATASIZE) { IData* datap = &(reinterpret_cast(memp))[entry]; - if (shift == start_shift) { *datap = 0; } + if (shift == start_shift) *datap = 0; *datap |= (c << shift) & VL_MASK_I(width); } else if (width <= VL_QUADSIZE) { QData* datap = &(reinterpret_cast(memp))[entry]; - if (shift == start_shift) { *datap = 0; } + if (shift == start_shift) *datap = 0; *datap |= ((static_cast(c) << static_cast(shift)) & VL_MASK_Q(width)); } else { WDataOutP datap = &(reinterpret_cast(memp))[entry * VL_WORDS_I(width)]; @@ -1910,19 +1894,19 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) { 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); - if (!innum) { *datap = 0; } + if (!innum) *datap = 0; *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits); } else if (m_bits <= 16) { SData* datap = reinterpret_cast(valuep); - if (!innum) { *datap = 0; } + if (!innum) *datap = 0; *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits); } else if (m_bits <= VL_IDATASIZE) { IData* datap = reinterpret_cast(valuep); - if (!innum) { *datap = 0; } + if (!innum) *datap = 0; *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits); } else if (m_bits <= VL_QUADSIZE) { QData* datap = reinterpret_cast(valuep); - if (!innum) { *datap = 0; } + if (!innum) *datap = 0; *datap = ((*datap << static_cast(shift)) + static_cast(value)) & VL_MASK_Q(m_bits); } else { @@ -2122,7 +2106,7 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary // Helper function for conversion of timescale strings // Converts (1|10|100)(s|ms|us|ns|ps|fs) to power of then -int VL_TIME_STR_CONVERT(const char* strp) { +int VL_TIME_STR_CONVERT(const char* strp) VL_PURE { int scale = 0; if (!strp) return 0; if (*strp++ != '1') return 0; @@ -2143,14 +2127,14 @@ int VL_TIME_STR_CONVERT(const char* strp) { if (*strp) return 0; return scale; } -static const char* vl_time_str(int scale) { +static const char* vl_time_str(int scale) VL_PURE { static const char* const names[] = {"100s", "10s", "1s", "100ms", "10ms", "1ms", "100us", "10us", "1us", "100ns", "10ns", "1ns", "100ps", "10ps", "1ps", "100fs", "10fs", "1fs"}; if (VL_UNLIKELY(scale > 2 || scale < -15)) scale = 0; return names[2 - scale]; } -double vl_time_multiplier(int scale) { +double vl_time_multiplier(int scale) VL_PURE { // Return timescale multipler -18 to +18 // For speed, this does not check for illegal values if (scale < 0) { @@ -2217,7 +2201,7 @@ void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, void Verilated::debug(int level) VL_MT_SAFE { const VerilatedLockGuard lock(s_mutex); - s_s.s_debug = level; + s_ns.s_debug = level; if (level) { #ifdef VL_DEBUG VL_DEBUG_IF(VL_DBG_MSGF("- Verilated::debug is on." @@ -2473,7 +2457,11 @@ void Verilated::endOfThreadMTaskGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAF VerilatedThreadMsgQueue::flush(evalMsgQp); } -void Verilated::endOfEvalGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE { +void Verilated::endOfEval(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE { + // It doesn't work to set endOfEvalReqd on the threadpool thread + // and then check it on the eval thread since it's thread local. + // It should be ok to call into endOfEvalGuts, it returns immediately + // if there are no transactions. VL_DEBUG_IF(VL_DBG_MSGF("End-of-eval cleanup\n");); evalMsgQp->process(); } @@ -2483,12 +2471,12 @@ void Verilated::endOfEvalGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE { // VerilatedImp:: Constructors // verilated.o may exist both in protect-lib and main module. -// Both the main module and the protec-lib refer the same instance of +// Both the main module and the protect-lib refer the same instance of // static variables such as Verilated or VerilatedImplData. // This is important to share the state such as Verilated::gotFinish. // But the sharing may cause double-free error when shutting down because destructors // are called twice. -// 1st time:From protec-lib shared object on the way of unloading after exitting main() +// 1st time:From protect-lib shared object on the way of unloading after exiting main() // 2nd time:From main executable. // // To avoid the trouble, all member variables are enclosed in VerilatedImpU union. @@ -2695,7 +2683,7 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_ if (!finalize) { // Need two passes so we know array size to create // Alternative is to dynamically stretch the array, which is more code, and slower. - if (funcnum >= m_funcnumMax) { m_funcnumMax = funcnum + 1; } + if (funcnum >= m_funcnumMax) m_funcnumMax = funcnum + 1; } else { if (VL_UNCOVERABLE(funcnum >= m_funcnumMax)) { VL_FATAL_MT(__FILE__, __LINE__, "", // LCOV_EXCL_LINE diff --git a/include/verilated.h b/include/verilated.h index 6abb68521..b78c3ad21 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -265,24 +265,12 @@ public: #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 -#define VL_PIN_NOP(instname, pin, port) ///< Connect a pin, ala SP_PIN #define VL_CELL(instname, type) ///< Declare a cell, ala SP_CELL /// 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) -/// Constructor, ala SC_CTOR -#define VL_CTOR(modname) modname(const char* __VCname = "") - -/// Constructor declaration for C++, ala SP_CTOR_IMPL -#define VL_CTOR_IMP(modname) \ - modname::modname(const char* __VCname) \ - : VerilatedModule(__VCname) - -/// Constructor declaration for SystemC, ala SP_CTOR_IMPL -#define VL_SC_CTOR_IMP(modname) modname::modname(sc_module_name) - //========================================================================= // Functions overridable by user defines // (Internals however must use VL_PRINTF_MT, which calls these.) @@ -376,7 +364,6 @@ class Verilated final { static struct Serialized { // All these members serialized/deserialized // Fast path - int s_debug; ///< See accessors... only when VL_DEBUG set bool s_calcUnusedSigs; ///< Waves file on, need all signals calculated bool s_gotFinish; ///< A $finish statement executed bool s_assertOn; ///< Assertions are enabled @@ -396,6 +383,7 @@ class Verilated final { static struct NonSerialized { // Non-serialized information // These are reloaded from on command-line settings, so do not need to persist // Fast path + int s_debug = 0; ///< See accessors... only when VL_DEBUG set vluint64_t s_profThreadsStart = 1; ///< +prof+threads starting time vluint32_t s_profThreadsWindow = 2; ///< +prof+threads window size // Slow path @@ -438,6 +426,18 @@ private: public: // METHODS - User called + /// Enable debug of internal verilated code + static void debug(int level) VL_MT_SAFE; +#ifdef VL_DEBUG + /// Return debug level + /// When multithreaded this may not immediately react to another thread + /// changing the level (no mutex) + static inline int debug() VL_MT_SAFE { return s_ns.s_debug; } +#else + /// Return constant 0 debug level, so C++'s optimizer rips up + static constexpr int debug() VL_PURE { return 0; } +#endif + /// Select initial value of otherwise uninitialized signals. //// /// 0 = Set to zeros @@ -451,17 +451,6 @@ public: /// Random seed extended to 64 bits, and defaulted if user seed==0 static vluint64_t randSeedDefault64() VL_MT_SAFE; - /// Enable debug of internal verilated code - static void debug(int level) VL_MT_SAFE; -#ifdef VL_DEBUG - /// Return debug level - /// When multithreaded this may not immediately react to another thread - /// changing the level (no mutex) - static inline int debug() VL_MT_SAFE { return s_s.s_debug; } -#else - /// Return constant 0 debug level, so C++'s optimizer rips up - static constexpr int debug() VL_PURE { return 0; } -#endif /// Enable calculation of unused signals static void calcUnusedSigs(bool flag) VL_MT_SAFE; static bool calcUnusedSigs() VL_MT_SAFE { ///< Return calcUnusedSigs value @@ -479,7 +468,7 @@ public: static bool gotFinish() VL_MT_SAFE { return s_s.s_gotFinish; } ///< Return if got a $finish /// Allow traces to at some point be enabled (disables some optimizations) static void traceEverOn(bool flag) VL_MT_SAFE { - if (flag) { calcUnusedSigs(flag); } + if (flag) calcUnusedSigs(flag); } /// Enable/disable assertions static void assertOn(bool flag) VL_MT_SAFE; @@ -507,7 +496,9 @@ public: static void addFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE; static void removeFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE; static void runFlushCallbacks() VL_MT_SAFE; +#ifndef VL_NO_LEGACY static void flushCall() VL_MT_SAFE { runFlushCallbacks(); } // Deprecated +#endif /// Callbacks to run prior to termination static void addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE; static void removeExitCb(VoidPCb cb, void* datap) VL_MT_SAFE; @@ -575,35 +566,29 @@ public: static int dpiLineno() VL_MT_SAFE { return t_s.t_dpiLineno; } static int exportFuncNum(const char* namep) VL_MT_SAFE; + // Internal: Serialization setup static constexpr size_t serialized1Size() VL_PURE { return sizeof(s_s); } static constexpr void* serialized1Ptr() VL_MT_UNSAFE { return &s_s; } // For Serialize only static size_t serialized2Size() VL_PURE; static void* serialized2Ptr() VL_MT_UNSAFE; #ifdef VL_THREADED - /// Set the mtaskId, called when an mtask starts + /// Internal: Set the mtaskId, called when an mtask starts static void mtaskId(vluint32_t id) VL_MT_SAFE { t_s.t_mtaskId = id; } static vluint32_t mtaskId() VL_MT_SAFE { return t_s.t_mtaskId; } static void endOfEvalReqdInc() VL_MT_SAFE { ++t_s.t_endOfEvalReqd; } static void endOfEvalReqdDec() VL_MT_SAFE { --t_s.t_endOfEvalReqd; } - /// Called at end of each thread mtask, before finishing eval + /// Internal: Called at end of each thread mtask, before finishing eval static void endOfThreadMTask(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE { - if (VL_UNLIKELY(t_s.t_endOfEvalReqd)) { endOfThreadMTaskGuts(evalMsgQp); } - } - /// Called at end of eval loop - static void endOfEval(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE { - // It doesn't work to set endOfEvalReqd on the threadpool thread - // and then check it on the eval thread since it's thread local. - // It should be ok to call into endOfEvalGuts, it returns immediately - // if there are no transactions. - endOfEvalGuts(evalMsgQp); + if (VL_UNLIKELY(t_s.t_endOfEvalReqd)) endOfThreadMTaskGuts(evalMsgQp); } + /// Internal: Called at end of eval loop + static void endOfEval(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE; #endif private: #ifdef VL_THREADED static void endOfThreadMTaskGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE; - static void endOfEvalGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE; #endif }; @@ -655,7 +640,9 @@ 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); ///< Randomize a signal +#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(); @@ -867,7 +854,6 @@ inline vluint64_t vl_time_stamp64() { return static_cast(sc_time_sta # endif #endif -#define VL_TIME_I() (static_cast(vl_time_stamp64())) #define VL_TIME_Q() (static_cast(vl_time_stamp64())) #define VL_TIME_D() (static_cast(vl_time_stamp64())) @@ -876,8 +862,9 @@ inline vluint64_t vl_time_stamp64() { return static_cast(sc_time_sta // 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)) + /// Time imported from units to time precision -double vl_time_multiplier(int scale); +double vl_time_multiplier(int scale) VL_PURE; /// Evaluate expression if debug enabled #ifdef VL_DEBUG @@ -890,10 +877,6 @@ double vl_time_multiplier(int scale); # define VL_DEBUG_IF(text) do {} while (false) #endif -/// Collect coverage analysis for this line -#ifndef SP_AUTO_COVER3 -# define SP_AUTO_COVER3(what,file,line) -#endif // clang-format on //========================================================================= @@ -1268,7 +1251,7 @@ static inline IData VL_COUNTBITS_W(int lbits, int words, WDataInP lwp, IData ctr EData r = 0; IData wordLbits = 32; for (int i = 0; i < words; ++i) { - if (i == words - 1) { wordLbits = lbits % 32; } + if (i == words - 1) wordLbits = lbits % 32; r += VL_COUNTBITS_E(wordLbits, lwp[i], ctrl0, ctrl1, ctrl2); } return r; @@ -1341,7 +1324,7 @@ static inline IData VL_MOSTSETBITP1_W(int words, WDataInP lwp) VL_MT_SAFE { 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; } + if (VL_UNLIKELY(VL_BITISSET_E(lwp[i], bit))) return i * VL_EDATASIZE + bit + 1; } // Can't get here - one bit must be set } @@ -1638,8 +1621,8 @@ static inline WDataOutP VL_DIVS_WWW(int lbits, WDataOutP owp, WDataInP lwp, WData rwstore[VL_MULS_MAX_WORDS]; WDataInP ltup = lwp; WDataInP rtup = rwp; - if (lsign) { ltup = _VL_CLEAN_INPLACE_W(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), lwstore, lwp)); } - if (rsign) { rtup = _VL_CLEAN_INPLACE_W(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), rwstore, rwp)); } + if (lsign) ltup = _VL_CLEAN_INPLACE_W(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), lwstore, lwp)); + if (rsign) rtup = _VL_CLEAN_INPLACE_W(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), rwstore, rwp)); if ((lsign && !rsign) || (!lsign && rsign)) { WData qNoSign[VL_MULS_MAX_WORDS]; VL_DIV_WWW(lbits, qNoSign, ltup, rtup); @@ -1660,8 +1643,8 @@ static inline WDataOutP VL_MODDIVS_WWW(int lbits, WDataOutP owp, WDataInP lwp, WData rwstore[VL_MULS_MAX_WORDS]; WDataInP ltup = lwp; WDataInP rtup = rwp; - if (lsign) { ltup = _VL_CLEAN_INPLACE_W(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), lwstore, lwp)); } - if (rsign) { rtup = _VL_CLEAN_INPLACE_W(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), rwstore, rwp)); } + if (lsign) ltup = _VL_CLEAN_INPLACE_W(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), lwstore, lwp)); + if (rsign) rtup = _VL_CLEAN_INPLACE_W(lbits, VL_NEGATE_W(VL_WORDS_I(lbits), rwstore, rwp)); if (lsign) { // Only dividend sign matters for modulus WData qNoSign[VL_MULS_MAX_WORDS]; VL_MODDIV_WWW(lbits, qNoSign, ltup, rtup); @@ -2145,6 +2128,12 @@ static inline WDataOutP VL_SHIFTL_WWW(int obits, int lbits, int rbits, WDataOutP } 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 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 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 @@ -2153,6 +2142,11 @@ static inline IData VL_SHIFTL_IIW(int obits, int, int rbits, IData lhs, WDataInP } return VL_CLEAN_II(obits, obits, lhs << rwp[0]); } +static inline IData VL_SHIFTL_IIQ(int obits, int lbits, int rbits, 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 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 @@ -2162,6 +2156,11 @@ static inline QData VL_SHIFTL_QQW(int obits, int, int rbits, QData lhs, WDataInP // 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 lbits, int rbits, 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 @@ -2223,6 +2222,14 @@ static inline QData VL_SHIFTR_QQW(int obits, int, int rbits, QData lhs, WDataInP // 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 rbits, 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 rbits, 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 { @@ -2277,7 +2284,7 @@ static inline WDataOutP VL_SHIFTRS_WWW(int obits, int lbits, int rbits, WDataOut WDataInP lwp, WDataInP 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)) { + if (VL_UNLIKELY(overshift || rwp[0] >= obits)) { int lmsw = VL_WORDS_I(obits) - 1; EData sign = VL_SIGNONES_E(lbits, lwp[lmsw]); for (int j = 0; j <= lmsw; ++j) owp[j] = sign; @@ -2296,7 +2303,7 @@ static inline IData VL_SHIFTRS_IIW(int obits, int lbits, int rbits, IData lhs, WDataInP 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)) { + if (VL_UNLIKELY(overshift || rwp[0] >= obits)) { IData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative return VL_CLEAN_II(obits, obits, sign); } @@ -2306,13 +2313,14 @@ static inline QData VL_SHIFTRS_QQW(int obits, int lbits, int rbits, QData lhs, WDataInP 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)) { + if (VL_UNLIKELY(overshift || rwp[0] >= obits)) { 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_PURE { +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); diff --git a/include/verilated_cov.cpp b/include/verilated_cov.cpp index f351c3140..6d8e5a458 100644 --- a/include/verilated_cov.cpp +++ b/include/verilated_cov.cpp @@ -298,7 +298,7 @@ public: // Keys -> strings std::string keys[MAX_KEYS]; for (int i = 0; i < MAX_KEYS; ++i) { - if (ckeyps[i] && ckeyps[i][0]) { keys[i] = ckeyps[i]; } + if (ckeyps[i] && ckeyps[i][0]) keys[i] = ckeyps[i]; } // Ignore empty keys for (int i = 0; i < MAX_KEYS; ++i) { diff --git a/include/verilated_fst_c.cpp b/include/verilated_fst_c.cpp index 8f2ba2cfd..c205ceda8 100644 --- a/include/verilated_fst_c.cpp +++ b/include/verilated_fst_c.cpp @@ -94,7 +94,7 @@ void VerilatedFst::open(const char* filename) VL_MT_UNSAFE { m_code2symbol.clear(); // Allocate string buffer for arrays - if (!m_strbuf) { m_strbuf = new char[maxBits() + 32]; } + if (!m_strbuf) m_strbuf = new char[maxBits() + 32]; } void VerilatedFst::close() { diff --git a/include/verilated_imp.h b/include/verilated_imp.h index e81f5e908..e9f343a16 100644 --- a/include/verilated_imp.h +++ b/include/verilated_imp.h @@ -235,7 +235,7 @@ protected: VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex); VerilatedMutex m_hierMapMutex; ///< Protect m_hierMap - /// Map the represents scope hierarchy + /// Map that represents scope hierarchy VerilatedHierarchyMap m_hierMap VL_GUARDED_BY(m_hierMapMutex); // Slow - somewhat static: @@ -417,7 +417,7 @@ public: // But only for verilated*.cpp const VerilatedLockGuard lock(s_s.v.m_hierMapMutex); VerilatedHierarchyMap& map = s_s.v.m_hierMap; if (map.find(fromp) == map.end()) return; - VerilatedScopeVector& scopes = map[fromp]; + auto& scopes = map[fromp]; const auto it = find(scopes.begin(), scopes.end(), top); if (it != scopes.end()) scopes.erase(it); } @@ -595,7 +595,7 @@ private: } } else { // MCD Case - for (int i = 0; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) { + for (size_t i = 0; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) { if (fdi & VL_MASK_I(1)) fp.push_back(s_s.v.m_fdps[i]); } } diff --git a/include/verilated_syms.h b/include/verilated_syms.h index 295bcc1b8..191ad5014 100644 --- a/include/verilated_syms.h +++ b/include/verilated_syms.h @@ -43,6 +43,7 @@ struct VerilatedCStrCmp { }; /// Map of sorted scope names to find associated scope class +// This is a class instead of typedef/using to allow forward declaration in verilated.h class VerilatedScopeNameMap final : public std::map { public: @@ -51,16 +52,17 @@ public: }; /// Map of sorted variable names to find associated variable class +// This is a class instead of typedef/using to allow forward declaration in verilated.h class VerilatedVarNameMap final : public std::map { public: VerilatedVarNameMap() = default; ~VerilatedVarNameMap() = default; }; -typedef std::vector VerilatedScopeVector; - +/// Map of parent scope to vector of children scopes +// This is a class instead of typedef/using to allow forward declaration in verilated.h class VerilatedHierarchyMap final - : public std::unordered_map { + : public std::unordered_map> { public: VerilatedHierarchyMap() = default; ~VerilatedHierarchyMap() = default; diff --git a/include/verilated_trace.h b/include/verilated_trace.h index a16617146..feb261606 100644 --- a/include/verilated_trace.h +++ b/include/verilated_trace.h @@ -74,7 +74,7 @@ public: // Non blocking get bool tryGet(T& result) { const VerilatedLockGuard lockGuard(m_mutex); - if (m_queue.empty()) { return false; } + if (m_queue.empty()) return false; result = m_queue.front(); m_queue.pop_front(); return true; @@ -222,7 +222,7 @@ protected: void declCode(vluint32_t code, vluint32_t bits, bool tri); /// Is this an escape? - bool isScopeEscape(char c) { return isspace(c) || c == m_scopeEscape; } + bool isScopeEscape(char c) { return c != '\f' && (isspace(c) || c == m_scopeEscape); } /// Character that splits scopes. Note whitespace are ALWAYS escapes. char scopeEscape() { return m_scopeEscape; } diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index 0460dd8eb..c59468167 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -138,7 +138,9 @@ void VerilatedVcd::openNext(bool incFilename) { name[pos - 2] = '0'; if ((++(name[pos - 3])) > '9') { name[pos - 3] = '0'; - if ((++(name[pos - 4])) > '9') { name[pos - 4] = '0'; } + if ((++(name[pos - 4])) > '9') { // + name[pos - 4] = '0'; + } } } } @@ -164,7 +166,7 @@ void VerilatedVcd::openNext(bool incFilename) { } bool VerilatedVcd::preChangeDump() { - if (VL_UNLIKELY(m_rolloverMB && m_wroteBytes > m_rolloverMB)) { openNext(true); } + if (VL_UNLIKELY(m_rolloverMB && m_wroteBytes > m_rolloverMB)) openNext(true); return isOpen(); } @@ -401,13 +403,22 @@ void VerilatedVcd::dumpHeader() { if (*np == ' ') np++; if (*np == '\t') break; // tab means signal name starts printIndent(1); - printStr("$scope module "); + // Find character after name end + const char* sp = np; + while (*sp && *sp != ' ' && *sp != '\t' && *sp != '\f') sp++; + + if (*sp == '\f') { + printStr("$scope struct "); + } else { + printStr("$scope module "); + } + for (; *np && *np != ' ' && *np != '\t'; np++) { if (*np == '[') { printStr("("); } else if (*np == ']') { printStr(")"); - } else { + } else if (*np != '\f') { *m_writep++ = *np; } } diff --git a/include/verilated_vcd_c.h b/include/verilated_vcd_c.h index 6c7408691..ec42fae9b 100644 --- a/include/verilated_vcd_c.h +++ b/include/verilated_vcd_c.h @@ -84,7 +84,7 @@ private: inline void bufferCheck() { // Flush the write buffer if there's not enough space left for new information // We only call this once per vector, so we need enough slop for a very wide "b###" line - if (VL_UNLIKELY(m_writep > m_wrFlushp)) { bufferFlush(); } + if (VL_UNLIKELY(m_writep > m_wrFlushp)) bufferFlush(); } void closePrev(); void closeErr(); diff --git a/include/verilated_vcd_sc.h b/include/verilated_vcd_sc.h index 513344499..c18840541 100644 --- a/include/verilated_vcd_sc.h +++ b/include/verilated_vcd_sc.h @@ -53,7 +53,7 @@ public: // METHODS /// Called by SystemC simulate() virtual void cycle(bool delta_cycle) { - if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); } + if (!delta_cycle) this->dump(sc_time_stamp().to_double()); } private: diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index cb2a0c032..e48809e54 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -742,7 +742,7 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() { VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE { VerilatedVpiImp::assertOneCheck(); - if (VL_UNLIKELY(!s_s.m_errorInfop)) { s_s.m_errorInfop = new VerilatedVpiError(); } + if (VL_UNLIKELY(!s_s.m_errorInfop)) s_s.m_errorInfop = new VerilatedVpiError(); return s_s.m_errorInfop; } @@ -1235,7 +1235,7 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { if (scopename.find('.') == std::string::npos) { // This is a toplevel, hence search in our TOP ports first. scopep = Verilated::scopeFind("TOP"); - if (scopep) { varp = scopep->varFind(baseNamep); } + if (scopep) varp = scopep->varFind(baseNamep); } if (!varp) { scopep = Verilated::scopeFind(scopename.c_str()); @@ -2112,10 +2112,12 @@ PLI_INT32 vpi_chk_error(p_vpi_error_info error_info_p) { return _error_info_p->level; // return error severity level } +#ifndef VL_NO_LEGACY PLI_INT32 vpi_free_object(vpiHandle object) { // vpi_free_object is IEEE deprecated, use vpi_release_handle return vpi_release_handle(object); } +#endif PLI_INT32 vpi_release_handle(vpiHandle object) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_release_handle %p\n", object);); diff --git a/include/verilatedos.h b/include/verilatedos.h index d4625e643..b83d5f1bc 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -39,6 +39,7 @@ # define VL_ATTR_PRINTF(fmtArgNum) __attribute__((format(printf, (fmtArgNum), (fmtArgNum) + 1))) # define VL_ATTR_PURE __attribute__((pure)) # define VL_ATTR_UNUSED __attribute__((unused)) +# define VL_ATTR_WEAK __attribute__((weak)) # define VL_FUNC __func__ # if defined(__clang__) && defined(VL_THREADED) # define VL_ACQUIRE(...) __attribute__((acquire_capability(__VA_ARGS__))) @@ -87,6 +88,9 @@ #ifndef VL_ATTR_UNUSED # define VL_ATTR_UNUSED ///< Function that may be never used #endif +#ifndef VL_ATTR_WEAK +# define VL_ATTR_WEAK ///< Function external that is optionally defined +#endif #ifndef VL_FUNC # define VL_FUNC "__func__" ///< Name of current function for error macros #endif @@ -133,8 +137,10 @@ # define VL_THREAD_LOCAL ///< Use new C++ static local thread #endif -#define VL_THREAD ///< Deprecated -#define VL_STATIC_OR_THREAD static ///< Deprecated +#ifndef VL_NO_LEGACY +# define VL_THREAD ///< Deprecated +# define VL_STATIC_OR_THREAD static ///< Deprecated +#endif #define VL_PURE ///< Comment tag that Function is pure (and thus also VL_MT_SAFE) #define VL_MT_SAFE ///< Comment tag that function is threadsafe when VL_THREADED @@ -144,7 +150,9 @@ #define VL_MT_UNSAFE_ONE ///< Comment tag that function is not threadsafe when VL_THREADED, ///< protected to make sure single-caller -#define VL_ULL(c) (c##ULL) ///< Add appropriate suffix to 64-bit constant (deprecated) +#ifndef VL_NO_LEGACY +# define VL_ULL(c) (c##ULL) ///< Add appropriate suffix to 64-bit constant (deprecated) +#endif // This is not necessarily the same as #UL, depending on what the IData typedef is. #define VL_UL(c) (static_cast(c##UL)) ///< Add appropriate suffix to 32-bit constant @@ -184,16 +192,18 @@ // C++-2011 #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(VL_CPPCHECK) +# ifndef VL_NO_LEGACY // These are deprecated historical defines. We leave them in case users referenced them. -# define VL_EQ_DELETE = delete -# define vl_unique_ptr std::unique_ptr -# define vl_unordered_map std::unordered_map -# define vl_unordered_set std::unordered_set -# define VL_INCLUDE_UNORDERED_MAP -# define VL_INCLUDE_UNORDERED_SET -# define VL_FINAL final -# define VL_MUTABLE mutable -# define VL_OVERRIDE override +# define VL_EQ_DELETE = delete +# define vl_unique_ptr std::unique_ptr +# define vl_unordered_map std::unordered_map +# define vl_unordered_set std::unordered_set +# define VL_INCLUDE_UNORDERED_MAP +# define VL_INCLUDE_UNORDERED_SET +# define VL_FINAL final +# define VL_MUTABLE mutable +# define VL_OVERRIDE override +# endif #else # error "Verilator requires a C++11 or newer compiler" #endif @@ -358,12 +368,15 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type #define VL_BYTESIZE 8 ///< Bits in a CData / byte #define VL_SHORTSIZE 16 ///< Bits in a SData / short #define VL_IDATASIZE 32 ///< Bits in a IData / word -#define VL_WORDSIZE VL_IDATASIZE ///< Legacy define #define VL_QUADSIZE 64 ///< Bits in a QData / quadword #define VL_EDATASIZE 32 ///< Bits in a EData (WData entry) #define VL_EDATASIZE_LOG2 5 ///< log2(VL_EDATASIZE) #define VL_CACHE_LINE_BYTES 64 ///< Bytes in a cache line (for alignment) +#ifndef VL_NO_LEGACY +# define VL_WORDSIZE VL_IDATASIZE ///< Legacy define +#endif + /// Bytes this number of bits needs (1 bit=1 byte) #define VL_BYTES_I(nbits) (((nbits) + (VL_BYTESIZE - 1)) / VL_BYTESIZE) /// Words/EDatas this number of bits needs (1 bit=1 word) diff --git a/nodist/code_coverage b/nodist/code_coverage index ce797e942..eebc34d03 100755 --- a/nodist/code_coverage +++ b/nodist/code_coverage @@ -26,10 +26,15 @@ def test(): sys.exit("%Error: Run code_coverage from the top of the verilator kit") exec(open("./nodist/code_coverage.dat").read()) + if Args.stage_enabled[0]: + ci_fold_start("distclean") + print("Stage 0: distclean") + run("make distclean || true") + ci_fold_end() + if Args.stage_enabled[1]: ci_fold_start("configure") print("Stage 1: configure (coverage on)") - run("make distclean || true") run("autoconf") # Exceptions can pollute the branch coverage data run("./configure --enable-longtests CXX='g++ --coverage -fno-exceptions -DVL_GCOV'" @@ -87,8 +92,8 @@ def test(): if re.search(regexp, dat): # Remove .gcda/.gcno for files we don't care about before we slowly # read them - os.unlink(dat) - os.unlink(gcno) + unlink_ok(dat) + unlink_ok(gcno) del dats[dat] break @@ -305,18 +310,24 @@ def source_globs(*dirs): def run(command): # run a system command, check errors print("\t%s" % command) - os.system(command) status = subprocess.call(command, shell=True) if status < 0: raise Exception("%Error: Command failed " + command + ", stopped") +def unlink_ok(filename): + try: + os.unlink(filename) + except OSError: + pass + + def ci_fold_start(action): - print("::group::" + action) + print("::group::" + action, flush=True) def ci_fold_end(): - print("::endgroup::\n") + print("::endgroup::\n", flush=True) ####################################################################### @@ -387,7 +398,7 @@ if True: start = int(match_from.group(1)) else: os.exit("%Error: --stages not understood: " + Args.stages) - for n in range(1, 100): + for n in range(0, 100): Args.stage_enabled[n] = False for n in range(start, end + 1): Args.stage_enabled[n] = True diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index a5db6d5e9..4562bcd7d 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -288,7 +288,7 @@ V3Number_test: V3Number_test.o #### Modules %__gen.cpp: %.cpp $(ASTGEN) V3Ast.h V3AstNodes.h - $(PERL) $(ASTGEN) -I$(srcdir) $*.cpp + $(PYTHON3) $(ASTGEN) -I $(srcdir) $*.cpp %.o: %.cpp $(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} -c $< -o $@ @@ -315,18 +315,18 @@ serial:: V3Ast__gen_classes.h V3ParseBison.c serial_vlcov:: vlcovgen.d vlcovgen.d: $(VLCOVGEN) $(srcdir)/../include/verilated_cov_key.h - $(PERL) $(VLCOVGEN) --srcdir $(srcdir) + $(PYTHON3) $(VLCOVGEN) --srcdir $(srcdir) touch $@ V3Ast__gen_classes.h : $(ASTGEN) V3Ast.h V3AstNodes.h - $(PERL) $(ASTGEN) -I$(srcdir) --classes + $(PYTHON3) $(ASTGEN) -I $(srcdir) --classes V3ParseBison.h: V3ParseBison.c # Have only one output file in this rule to prevent parallel make issues V3ParseBison.c: verilog.y $(BISONPRE) @echo "If you get errors from verilog.y below, try upgrading bison to version 1.875 or newer." - $(PERL) $(BISONPRE) --yacc ${YACC} -d -v -o V3ParseBison.c $< + $(PYTHON3) $(BISONPRE) --yacc ${YACC} -d -v -o V3ParseBison.c $< V3Lexer_pregen.yy.cpp: verilog.l V3ParseBison.h $(HEADERS) ${LEX} --version @@ -340,7 +340,7 @@ V3PreLex_pregen.yy.cpp: V3PreLex.l $(HEADERS) ${LEX} ${LFLAGS} -o$@ $< V3PreLex.yy.cpp: V3PreLex_pregen.yy.cpp $(FLEXFIX) - $(PERL) $(FLEXFIX) V3PreLex <$< >$@ + $(PYTHON3) $(FLEXFIX) V3PreLex <$< >$@ .SUFFIXES: diff --git a/src/V3Active.cpp b/src/V3Active.cpp index a0d3de8fd..b918b7249 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -176,7 +176,7 @@ public: for (const auto& vrp : m_outputs) { LatchDetectGraphVertex* vertp = castVertexp(vrp->varp()->user1p()); vertp->user(true); // Identify the output vertex we are checking paths _to_ - if (!latchCheckInternal(castVertexp(verticesBeginp()))) { latch_detected = true; } + if (!latchCheckInternal(castVertexp(verticesBeginp()))) latch_detected = true; if (latch_detected && !latch_expected) { nodep->v3warn( LATCH, @@ -185,7 +185,7 @@ public: << " (not all control paths of combinational always assign a value)\n" << nodep->warnMore() << "... Suggest use of always_latch for intentional latches"); - if (debug() >= 9) { dumpDotFilePrefixed("latch_" + vrp->name()); } + if (debug() >= 9) dumpDotFilePrefixed("latch_" + vrp->name()); } vertp->user(false); // Clear again (see above) vrp->varp()->isLatched(latch_detected); diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index 307081add..9049d6146 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -148,7 +148,7 @@ private: AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); - if (m_disablep) { lhsp = new AstAnd(fl, new AstNot(fl, m_disablep), lhsp); } + if (m_disablep) lhsp = new AstAnd(fl, new AstNot(fl, m_disablep), lhsp); AstNode* past = new AstPast(fl, lhsp, nullptr); past->dtypeFrom(lhsp); diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 3c72a8d80..31f0b4d42 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1104,7 +1104,7 @@ void AstNode::dumpTree(std::ostream& os, const string& indent, int maxDepth) con os << indent << " "; dumpPtrs(os); } - if (s_debugFileline >= 9) { os << fileline()->warnContextSecondary(); } + if (s_debugFileline >= 9) os << fileline()->warnContextSecondary(); if (maxDepth == 1) { if (op1p() || op2p() || op3p() || op4p()) os << indent << "1: ...(maxDepth)\n"; } else { diff --git a/src/V3Ast.h b/src/V3Ast.h index 2295ff743..258fb40c5 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1706,7 +1706,7 @@ public: } } void dtypeFrom(AstNode* fromp) { - if (fromp) { dtypep(fromp->dtypep()); } + if (fromp) dtypep(fromp->dtypep()); } void dtypeChgSigned(bool flag = true); void dtypeChgWidth(int width, int widthMin); @@ -1922,7 +1922,7 @@ public: virtual string emitVerilog() = 0; /// Format string for verilog writing; see V3EmitV // For documentation on emitC format see EmitCStmts::emitOpName virtual string emitC() = 0; - virtual string emitSimpleOperator() { return ""; } + virtual string emitSimpleOperator() { return ""; } // "" means not ok to use virtual bool emitCheckMaxWords() { return false; } // Check VL_MULS_MAX_WORDS virtual bool cleanOut() const = 0; // True if output has extra upper bits zero // Someday we will generically support data types on every math node @@ -2547,7 +2547,7 @@ public: return nullptr; } virtual void cloneRelink() override { - if (m_refDTypep && m_refDTypep->clonep()) { m_refDTypep = m_refDTypep->clonep(); } + if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep(); } virtual bool same(const AstNode* samep) const override { const AstNodeArrayDType* asamep = static_cast(samep); @@ -2614,7 +2614,7 @@ class AstNodeStream VL_NOT_FINAL : public AstNodeBiop { public: AstNodeStream(AstType t, FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop{t, fl, lhsp, rhsp} { - if (lhsp->dtypep()) { dtypeSetLogicSized(lhsp->dtypep()->width(), VSigning::UNSIGNED); } + if (lhsp->dtypep()) dtypeSetLogicSized(lhsp->dtypep()->width(), VSigning::UNSIGNED); } ASTNODE_BASE_FUNCS(NodeStream) }; @@ -2811,7 +2811,7 @@ public: ASTNODE_BASE_FUNCS(NodeFTaskRef) virtual const char* broken() const override; virtual void cloneRelink() override { - if (m_taskp && m_taskp->clonep()) { m_taskp = m_taskp->clonep(); } + if (m_taskp && m_taskp->clonep()) m_taskp = m_taskp->clonep(); } virtual void dump(std::ostream& str = std::cout) const override; virtual string name() const override { return m_name; } // * = Var name diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 262b0421b..db091b09b 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -50,7 +50,7 @@ const char* AstNodeVarRef::broken() const { } void AstNodeVarRef::cloneRelink() { - if (m_varp && m_varp->clonep()) { m_varp = m_varp->clonep(); } + if (m_varp && m_varp->clonep()) m_varp = m_varp->clonep(); } string AstNodeVarRef::hiernameProtect() const { @@ -99,7 +99,7 @@ void AstNodeCCall::dump(std::ostream& str) const { } } void AstNodeCCall::cloneRelink() { - if (m_funcp && m_funcp->clonep()) { m_funcp = m_funcp->clonep(); } + if (m_funcp && m_funcp->clonep()) m_funcp = m_funcp->clonep(); } const char* AstNodeCCall::broken() const { BROKEN_RTN(m_funcp && !m_funcp->brokeExists()); @@ -1098,7 +1098,7 @@ void AstNode::dump(std::ostream& str) const { } else { str << " @dt=" << nodeAddr(dtypep()) << "@"; } - if (AstNodeDType* dtp = dtypep()) { dtp->dumpSmall(str); } + if (AstNodeDType* dtp = dtypep()) dtp->dumpSmall(str); } else { // V3Broken will throw an error if (dtypep()) str << " %Error-dtype-exp=null,got=" << nodeAddr(dtypep()); } @@ -1230,9 +1230,9 @@ void AstEnumItemRef::dump(std::ostream& str) const { } void AstIfaceRefDType::dump(std::ostream& str) const { this->AstNode::dump(str); - if (cellName() != "") { str << " cell=" << cellName(); } - if (ifaceName() != "") { str << " if=" << ifaceName(); } - if (modportName() != "") { str << " mp=" << modportName(); } + if (cellName() != "") str << " cell=" << cellName(); + if (ifaceName() != "") str << " if=" << ifaceName(); + if (modportName() != "") str << " mp=" << modportName(); if (cellp()) { str << " -> "; cellp()->dump(str); @@ -1388,7 +1388,7 @@ void AstNodeDType::dump(std::ostream& str) const { void AstNodeDType::dumpSmall(std::ostream& str) const { str << "(" << (generic() ? "G/" : "") << ((isSigned() && !isDouble()) ? "s" : "") << (isNosign() ? "n" : "") << (isDouble() ? "d" : "") << (isString() ? "str" : ""); - if (!isDouble() && !isString()) { str << "w" << (widthSized() ? "" : "u") << width(); } + if (!isDouble() && !isString()) str << "w" << (widthSized() ? "" : "u") << width(); if (!widthSized()) str << "/" << widthMin(); str << ")"; } @@ -1640,7 +1640,7 @@ void AstNodeFTaskRef::dump(std::ostream& str) const { this->AstNodeStmt::dump(str); if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep()); str << " -> "; - if (dotted() != "") { str << ".=" << dotted() << " "; } + if (dotted() != "") str << ".=" << dotted() << " "; if (taskp()) { taskp()->dump(str); } else { @@ -1676,7 +1676,7 @@ void AstCoverDecl::dump(std::ostream& str) const { str << " -> "; this->dataDeclNullp()->dump(str); } else { - if (binNum()) { str << " bin" << std::dec << binNum(); } + if (binNum()) str << " bin" << std::dec << binNum(); } } void AstCoverInc::dump(std::ostream& str) const { diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index b6bb6653a..efaca0cec 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -539,8 +539,8 @@ public: return nullptr; } virtual void cloneRelink() override { - if (m_refDTypep && m_refDTypep->clonep()) { m_refDTypep = m_refDTypep->clonep(); } - if (m_keyDTypep && m_keyDTypep->clonep()) { m_keyDTypep = m_keyDTypep->clonep(); } + if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep(); + if (m_keyDTypep && m_keyDTypep->clonep()) m_keyDTypep = m_keyDTypep->clonep(); } virtual bool same(const AstNode* samep) const override { const AstAssocArrayDType* asamep = static_cast(samep); @@ -641,7 +641,7 @@ public: return nullptr; } virtual void cloneRelink() override { - if (m_refDTypep && m_refDTypep->clonep()) { m_refDTypep = m_refDTypep->clonep(); } + if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep(); } virtual bool same(const AstNode* samep) const override { const AstAssocArrayDType* asamep = static_cast(samep); @@ -760,7 +760,7 @@ public: return nullptr; } virtual void cloneRelink() override { - if (m_refDTypep && m_refDTypep->clonep()) { m_refDTypep = m_refDTypep->clonep(); } + if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep(); } virtual bool same(const AstNode* samep) const override { const AstNodeArrayDType* asamep = static_cast(samep); @@ -971,7 +971,7 @@ public: return nullptr; } virtual void cloneRelink() override { - if (m_refDTypep && m_refDTypep->clonep()) { m_refDTypep = m_refDTypep->clonep(); } + if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep(); } virtual bool same(const AstNode* samep) const override { const AstConstDType* sp = static_cast(samep); @@ -1140,7 +1140,7 @@ public: return nullptr; } virtual void cloneRelink() override { - if (m_refDTypep && m_refDTypep->clonep()) { m_refDTypep = m_refDTypep->clonep(); } + if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep(); } virtual bool same(const AstNode* samep) const override { const AstQueueDType* asamep = static_cast(samep); @@ -1217,8 +1217,8 @@ public: return nullptr; } virtual void cloneRelink() override { - if (m_typedefp && m_typedefp->clonep()) { m_typedefp = m_typedefp->clonep(); } - if (m_refDTypep && m_refDTypep->clonep()) { m_refDTypep = m_refDTypep->clonep(); } + if (m_typedefp && m_typedefp->clonep()) m_typedefp = m_typedefp->clonep(); + if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep(); } virtual bool same(const AstNode* samep) const override { const AstRefDType* asamep = static_cast(samep); @@ -1486,7 +1486,7 @@ public: return nullptr; } virtual void cloneRelink() override { - if (m_refDTypep && m_refDTypep->clonep()) { m_refDTypep = m_refDTypep->clonep(); } + if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep(); } virtual bool same(const AstNode* samep) const override { const AstEnumDType* sp = static_cast(samep); @@ -2047,7 +2047,7 @@ public: , m_origName{name} { init(); combineType(type); - if (examplep->childDTypep()) { childDTypep(examplep->childDTypep()->cloneTree(true)); } + if (examplep->childDTypep()) childDTypep(examplep->childDTypep()->cloneTree(true)); dtypeFrom(examplep); m_declKwd = examplep->declKwd(); } @@ -2170,7 +2170,7 @@ public: return ((isIO() || isSignal()) && (isIO() || isBitLogic()) // Wrapper would otherwise duplicate wrapped module's coverage - && !isSc() && !isPrimaryIO() && !isConst() && !isDouble()); + && !isSc() && !isPrimaryIO() && !isConst() && !isDouble() && !isString()); } bool isClassMember() const { return varType() == AstVarType::MEMBER; } bool isStatementTemp() const { return (varType() == AstVarType::STMTTEMP); } @@ -2677,7 +2677,7 @@ public: } ASTNODE_NODE_FUNCS(MemberSel) virtual void cloneRelink() override { - if (m_varp && m_varp->clonep()) { m_varp = m_varp->clonep(); } + if (m_varp && m_varp->clonep()) m_varp = m_varp->clonep(); } virtual const char* broken() const override { BROKEN_RTN(m_varp && !m_varp->brokeExists()); @@ -7294,7 +7294,7 @@ class AstShiftL final : public AstNodeBiop { public: AstShiftL(FileLine* fl, AstNode* lhsp, AstNode* rhsp, int setwidth = 0) : ASTGEN_SUPER(fl, lhsp, rhsp) { - if (setwidth) { dtypeSetLogicSized(setwidth, VSigning::UNSIGNED); } + if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED); } ASTNODE_NODE_FUNCS(ShiftL) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override { @@ -7305,7 +7305,9 @@ public: } virtual string emitVerilog() override { return "%k(%l %f<< %r)"; } virtual string emitC() override { return "VL_SHIFTL_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } - virtual string emitSimpleOperator() override { return "<<"; } + virtual string emitSimpleOperator() override { + return (rhsp()->isWide() || rhsp()->isQuad()) ? "" : "<<"; + } virtual bool cleanOut() const override { return false; } virtual bool cleanLhs() const override { return false; } virtual bool cleanRhs() const override { return true; } @@ -7316,7 +7318,7 @@ class AstShiftR final : public AstNodeBiop { public: AstShiftR(FileLine* fl, AstNode* lhsp, AstNode* rhsp, int setwidth = 0) : ASTGEN_SUPER(fl, lhsp, rhsp) { - if (setwidth) { dtypeSetLogicSized(setwidth, VSigning::UNSIGNED); } + if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED); } ASTNODE_NODE_FUNCS(ShiftR) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override { @@ -7327,7 +7329,9 @@ public: } virtual string emitVerilog() override { return "%k(%l %f>> %r)"; } virtual string emitC() override { return "VL_SHIFTR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } - virtual string emitSimpleOperator() override { return ">>"; } + virtual string emitSimpleOperator() override { + return (rhsp()->isWide() || rhsp()->isQuad()) ? "" : ">>"; + } virtual bool cleanOut() const override { return false; } virtual bool cleanLhs() const override { return true; } virtual bool cleanRhs() const override { return true; } @@ -7342,7 +7346,7 @@ public: AstShiftRS(FileLine* fl, AstNode* lhsp, AstNode* rhsp, int setwidth = 0) : ASTGEN_SUPER(fl, lhsp, rhsp) { // Important that widthMin be correct, as opExtend requires it after V3Expand - if (setwidth) { dtypeSetLogicSized(setwidth, VSigning::SIGNED); } + if (setwidth) dtypeSetLogicSized(setwidth, VSigning::SIGNED); } ASTNODE_NODE_FUNCS(ShiftRS) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override { @@ -9044,7 +9048,7 @@ public: , m_cleanOut{cleanOut} , m_pure{true} { addNOp1p(new AstText(fl, textStmt, true)); - if (setwidth) { dtypeSetLogicSized(setwidth, VSigning::UNSIGNED); } + if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED); } ASTNODE_NODE_FUNCS(CMath) virtual bool isGateOptimizable() const override { return m_pure; } diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index 49556d404..2aa30b2f7 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -81,7 +81,7 @@ public: m_tlFuncp->isStatic(false); m_tlFuncp->slow(!VN_IS(m_modp, Class)); // Only classes construct on fast path m_tlFuncp->argTypes(m_argsp); - if (stmt != "") { m_tlFuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); } + if (stmt != "") m_tlFuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); m_funcp = m_tlFuncp; m_modp->addStmtp(m_tlFuncp); } diff --git a/src/V3Case.cpp b/src/V3Case.cpp index d40c6eb0e..1b3122d7d 100644 --- a/src/V3Case.cpp +++ b/src/V3Case.cpp @@ -484,7 +484,7 @@ private: } //-------------------- virtual void visit(AstNode* nodep) override { - if (VN_IS(nodep, Always)) { m_alwaysp = nodep; } + if (VN_IS(nodep, Always)) m_alwaysp = nodep; iterateChildren(nodep); } diff --git a/src/V3Cdc.cpp b/src/V3Cdc.cpp index 95b44887f..9139c5eb5 100644 --- a/src/V3Cdc.cpp +++ b/src/V3Cdc.cpp @@ -599,7 +599,7 @@ private: } } // If multiple domains need to do complicated optimizations - if (senedited) { senoutp = VN_CAST(V3Const::constifyExpensiveEdit(senoutp), SenTree); } + if (senedited) senoutp = VN_CAST(V3Const::constifyExpensiveEdit(senoutp), SenTree); if (traceDests) { vertexp->dstDomainSet(true); // Note it's set - domainp may be null, so can't use that vertexp->dstDomainp(senoutp); diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index 065f55197..ba0b91d6c 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -162,10 +162,10 @@ private: void operandQuadop(AstNodeQuadop* nodep) { iterateChildren(nodep); computeCppWidth(nodep); - if (nodep->cleanLhs()) { ensureClean(nodep->lhsp()); } - if (nodep->cleanRhs()) { ensureClean(nodep->rhsp()); } - if (nodep->cleanThs()) { ensureClean(nodep->thsp()); } - if (nodep->cleanFhs()) { ensureClean(nodep->fhsp()); } + if (nodep->cleanLhs()) ensureClean(nodep->lhsp()); + if (nodep->cleanRhs()) ensureClean(nodep->rhsp()); + if (nodep->cleanThs()) ensureClean(nodep->thsp()); + if (nodep->cleanFhs()) ensureClean(nodep->fhsp()); // no setClean.. must do it in each user routine. } diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index 04c174fa8..eb5cd526c 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -101,6 +101,7 @@ private: // BOTHEDGE: var ^ var_last // HIGHEDGE: var // LOWEDGE: ~var + // ANYEDGE: var ^ var_last AstNode* newp = nullptr; if (nodep->edgeType() == VEdgeType::ET_ILLEGAL) { nodep->v3warn(E_UNSUPPORTED, diff --git a/src/V3Config.cpp b/src/V3Config.cpp index b0f035cd2..40b7985d7 100644 --- a/src/V3Config.cpp +++ b/src/V3Config.cpp @@ -61,7 +61,7 @@ public: T* resolve(const string& name) { // Lookup if it was resolved before, typically not auto it = m_mapResolved.find(name); - if (VL_UNLIKELY(it != m_mapResolved.end())) { return &it->second; } + if (VL_UNLIKELY(it != m_mapResolved.end())) return &it->second; T* newp = nullptr; // Cannot be resolved, create if matched diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 8ef02f707..a8f7b5722 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -29,9 +29,9 @@ #include "V3Ast.h" #include "V3Width.h" #include "V3Simulate.h" +#include "V3Stats.h" #include -#include //###################################################################### // Utilities @@ -76,6 +76,421 @@ public: bool found() const { return m_found; } }; +// 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 { + // TYPES + + struct LeafInfo { // 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; + }; + // 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 + V3Number m_bitPolarity; // Coefficient of each bit + static int widthOfRef(AstVarRef* refp) { + if (AstWordSel* selp = VN_CAST(refp->backp(), WordSel)) return selp->width(); + return refp->width(); + } + + public: + // METHODS + bool hasConstantResult() const { return m_constResult >= 0; } + void setPolarity(bool compBit, int bit) { + UASSERT_OBJ(!hasConstantResult(), m_refp, "Already has result of " << m_constResult); + if (m_bitPolarity.bitIsX(bit)) { // The bit is not yet set + m_bitPolarity.setBit(bit, compBit); + } else { // Priviously set the bit + 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"); + m_bitPolarity.setBit(bit, 'x'); + } else { // And, Or + // Can ignore this nodep as the bit is already registered + if (sameFlag) return; // a & a == a, b | b == b + // Otherwise result is constant + m_constResult = m_parentp->isAndTree() ? 0 : 1; + m_bitPolarity.setAllBitsX(); // The variable is not referred anymore + } + } + } + AstNode* getResult() const { + FileLine* fl = m_refp->fileline(); + AstNode* srcp = VN_CAST(m_refp->backp(), WordSel); + if (!srcp) srcp = m_refp; + const int width = widthOfRef(m_refp); + + if (hasConstantResult()) + return new AstConst{fl, + V3Number{srcp, width, static_cast(m_constResult)}}; + + 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); + } 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}; + } else { // Or + compValuep->num().opXor(V3Number{compValuep->num()}, maskValuep->num()); + resultp = new AstNeq{fl, compValuep, maskedp}; + } + } + return resultp; + } + + // CONSTRUCTORS + VarInfo(ConstBitOpTreeVisitor* parent, AstVarRef* refp) + : m_parentp(parent) + , m_refp(refp) + , m_bitPolarity(refp, widthOfRef(refp)) { + m_bitPolarity.setAllBitsX(); + } + }; + + // 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... + 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 + + AstUser4InUse m_inuser4; + std::vector m_frozenNodes; // Nodes that cannot be optimized + 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() + + bool isAndTree() const { return VN_IS(m_rootp, And); } + bool isOrTree() const { return VN_IS(m_rootp, Or); } + bool isXorTree() const { return VN_IS(m_rootp, Xor) || VN_IS(m_rootp, RedXor); } + +#define CONST_BITOP_RETURN_IF(cond, nodep) \ + if (setFailed(cond, #cond, nodep, __LINE__)) return + +#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) { + 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 |= fail; + return m_failed; + } + void incrOps(const AstNode* nodep, int line) { + ++m_ops; + UINFO(9, "Increment to " << m_ops << " " << nodep << " called from line " << line << "\n"); + } + VarInfo& getVarInfo(const LeafInfo& ref) { + UASSERT_OBJ(ref.m_refp, m_rootp, "null varref in And/Or/Xor optimization"); + AstNode* nodep = ref.m_refp->varScopep(); + if (!nodep) nodep = ref.m_refp->varp(); // Not scoped + int baseIdx = nodep->user4(); + if (baseIdx == 0) { // Not set yet + baseIdx = m_varInfos.size(); + const int numWords + = ref.m_refp->dtypep()->isWide() ? ref.m_refp->dtypep()->widthWords() : 1; + m_varInfos.resize(m_varInfos.size() + numWords, nullptr); + nodep->user4(baseIdx); + } + const size_t idx = baseIdx + std::max(0, ref.m_wordIdx); + VarInfo* varInfop = m_varInfos[idx]; + if (!varInfop) { + varInfop = new VarInfo{this, ref.m_refp}; + m_varInfos[idx] = varInfop; + } + return *varInfop; + } + + // Traverse down to see AstConst or AstVarRef + LeafInfo findLeaf(AstNode* nodep, bool expectConst) { + LeafInfo info; + { + VL_RESTORER(m_leafp); + m_leafp = &info; + iterate(nodep); + } + + bool ok = !m_failed; + if (expectConst) + ok &= !info.m_refp && info.m_constp; + 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); + } + } + + // VISITORS + virtual void visit(AstNode* nodep) override { + CONST_BITOP_SET_FAILED("Hit unexpected op", nodep); + } + virtual void visit(AstCCast* nodep) override { iterateChildren(nodep); } + virtual void visit(AstShiftR* nodep) override { + CONST_BITOP_RETURN_IF(!m_leafp, nodep); + AstConst* constp = VN_CAST(nodep->rhsp(), Const); + CONST_BITOP_RETURN_IF(!constp, nodep->rhsp()); + m_lsb += constp->toUInt(); + incrOps(nodep, __LINE__); + iterate(nodep->lhsp()); + m_lsb -= constp->toUInt(); + } + 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); + incrOps(nodep, __LINE__); + m_polarity = !m_polarity; + iterateChildren(nodep); + // Don't restore m_polarity for Xor as it counts parity of the entire tree + if (!isXorTree()) m_polarity = !m_polarity; + } + virtual void visit(AstWordSel* nodep) override { + CONST_BITOP_RETURN_IF(!m_leafp, nodep); + AstConst* constp = VN_CAST(nodep->bitp(), Const); + CONST_BITOP_RETURN_IF(!constp, nodep->rhsp()); + UASSERT_OBJ(m_leafp->m_wordIdx == -1, nodep, "Unexpected nested WordSel"); + m_leafp->m_wordIdx = constp->toSInt(); + iterate(nodep->fromp()); + } + virtual void visit(AstVarRef* nodep) override { + CONST_BITOP_RETURN_IF(!m_leafp, nodep); + UASSERT_OBJ(!m_leafp->m_refp, nodep, m_leafp->m_refp << " is already set"); + m_leafp->m_refp = nodep; + m_leafp->m_polarity = m_polarity; + m_leafp->m_lsb = m_lsb; + } + virtual void visit(AstConst* nodep) override { + CONST_BITOP_RETURN_IF(!m_leafp, nodep); + UASSERT_OBJ(!m_leafp->m_constp, nodep, m_leafp->m_constp << " is already set"); + m_leafp->m_constp = nodep; + m_leafp->m_lsb = m_lsb; + } + + virtual void visit(AstRedXor* nodep) override { // Expect '^(mask & v)' + CONST_BITOP_RETURN_IF(!VN_IS(m_rootp, Xor), nodep); + AstAnd* andp = VN_CAST(nodep->lhsp(), And); + CONST_BITOP_RETURN_IF(!andp, nodep->lhsp()); + + auto mask = findLeaf(andp->lhsp(), true); + CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp()); + + LeafInfo leaf = findLeaf(andp->rhsp(), false); + CONST_BITOP_RETURN_IF(!leaf.m_refp, andp->rhsp()); + + incrOps(nodep, __LINE__); + incrOps(andp, __LINE__); + const V3Number& maskNum = mask.m_constp->num(); + VarInfo& varInfo = getVarInfo(leaf); + for (int i = 0; i < maskNum.width(); ++i) { + // Set true, m_treePolarity takes care of the entire parity + if (maskNum.bitIs1(i)) varInfo.setPolarity(true, i + leaf.m_lsb); + } + } + + virtual void visit(AstNodeBiop* nodep) override { + 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) { + LeafInfo leafInfo; + m_leafp = &leafInfo; + m_curOpp = i == 0 ? nodep->lhsp() : nodep->rhsp(); + const size_t origFrozens = m_frozenNodes.size(); + const int origOps = m_ops; + const bool origFailed = m_failed; + iterate(m_curOpp); + if (leafInfo.m_constp || m_failed) { + // Rvert changes in leaf + if (m_frozenNodes.size() > origFrozens) m_frozenNodes.resize(origFrozens); + m_frozenNodes.push_back(m_curOpp); + m_ops = origOps; + m_failed = origFailed; + } else if (leafInfo.m_refp) { + VarInfo& varInfo = getVarInfo(leafInfo); + if (!varInfo.hasConstantResult()) { + varInfo.setPolarity(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) + 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); + 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]) + incrOps(nodep, __LINE__); + iterate(nodep->rhsp()); + return; + } else if ((isAndTree() && VN_IS(nodep, Eq)) || (isOrTree() && VN_IS(nodep, Neq))) { + CONST_BITOP_RETURN_IF(!m_polarity, nodep); + const bool maskFlip = isOrTree(); + LeafInfo comp = findLeaf(nodep->lhsp(), true); + CONST_BITOP_RETURN_IF(!comp.m_constp || comp.m_lsb != 0, nodep->lhsp()); + + AstAnd* andp = VN_CAST(nodep->rhsp(), And); // comp == (mask & v) + CONST_BITOP_RETURN_IF(!andp, nodep->rhsp()); + + LeafInfo mask = findLeaf(andp->lhsp(), true); + CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp()); + + LeafInfo ref = findLeaf(andp->rhsp(), false); + CONST_BITOP_RETURN_IF(!ref.m_refp, andp->rhsp()); + + VarInfo& varInfo = getVarInfo(ref); + + const V3Number maskNum = mask.m_constp->num(); + const V3Number compNum = comp.m_constp->num(); + for (int i = 0; i < maskNum.width() && !varInfo.hasConstantResult(); ++i) { + const int bit = i + ref.m_lsb; + if (maskNum.bitIs0(i)) continue; + varInfo.setPolarity(compNum.bitIs1(i) ^ maskFlip, bit); + } + incrOps(nodep, __LINE__); + incrOps(andp, __LINE__); + return; + } + CONST_BITOP_SET_FAILED("Mixture of different ops cannot be optimized", nodep); + } + + // CONSTRUCTORS + ConstBitOpTreeVisitor(AstNode* nodep, int ops) + : m_ops(ops) + , 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)) { + iterate(biopp); + } else { + incrOps(nodep, __LINE__); + iterateChildren(nodep); + } + UASSERT_OBJ(isXorTree() || m_polarity, nodep, "must be the original polarity"); + } + virtual ~ConstBitOpTreeVisitor() { + for (size_t i = 0; i < m_varInfos.size(); ++i) { + VL_DO_DANGLING(delete m_varInfos[i], m_varInfos[i]); + } + } +#undef CONST_BITOP_RETURN_IF +#undef CONST_BITOP_SET_FAILED + +public: + // Transform as below. + // v[0] & v[1] => 2'b11 == (2'b11 & v) + // v[0] | v[1] => 2'b00 != (2'b11 & v) + // v[0] ^ v[1] => ^{2'b11 & v} + // (3'b011 == (3'b011 & v)) & v[2] => 3'b111 == (3'b111 & v) + // (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}; + 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; + for (const VarInfo* 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 (const VarInfo* 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 (resultp->width() != nodep->width()) { + resultp = new AstCCast{resultp->fileline(), resultp, nodep}; + } + return resultp; + } +}; + //###################################################################### // Const state, as a visitor of each AstNode @@ -104,6 +519,7 @@ private: AstArraySel* m_selp = nullptr; // Current select AstNode* m_scopep = nullptr; // Current scope AstAttrOf* m_attrp = nullptr; // Current attribute + VDouble0 m_statBitOpReduction; // Ops reduced in ConstBitOpTreeVisitor // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -168,6 +584,34 @@ private: return (lp && VN_IS(lp->lhsp(), Const) && VN_IS(np->rhsp(), Const) && lp->width() == np->width()); } + bool matchRedundantClean(AstAnd* andp) { + // Remove And with constant one inserted by V3Clean + // 1 & (a == b) -> (IData)(a == b) + // When bool is casted to int, the value is either 0 or 1 + AstConst* constp = VN_CAST(andp->lhsp(), Const); + UASSERT_OBJ(constp && constp->isOne(), andp->lhsp(), "TRREEOPC must meet this condition"); + AstNode* rhsp = andp->rhsp(); + AstCCast* ccastp = nullptr; + auto isEqOrNeq + = [](AstNode* nodep) -> bool { return VN_IS(nodep, Eq) || VN_IS(nodep, Neq); }; + if (isEqOrNeq(rhsp)) { + ccastp = new AstCCast{andp->fileline(), rhsp->unlinkFrBack(), andp}; + } else if (AstCCast* tmpp = VN_CAST(rhsp, CCast)) { + if (isEqOrNeq(tmpp->lhsp())) { + if (tmpp->width() == andp->width()) { + tmpp->unlinkFrBack(); + ccastp = tmpp; + } else { + ccastp = new AstCCast{andp->fileline(), tmpp->lhsp()->unlinkFrBack(), andp}; + } + } + } + if (ccastp) { + andp->replaceWith(ccastp); + VL_DO_DANGLING(andp->deleteTree(), andp); + } + return ccastp; + } static bool operandAndOrSame(const AstNode* nodep) { // OR( AND(VAL,x), AND(VAL,y)) -> AND(VAL,OR(x,y)) @@ -242,6 +686,36 @@ private: VL_DO_DANGLING(nodep->deleteTree(), nodep); return true; } + bool matchBitOpTree(AstNode* nodep) { + 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); + } + if (newp) { + UINFO(4, "Transformed leaf of bit tree to " << newp << std::endl); + if (debug() >= 9) { + static int c = 0; + std::cout << "Call matchBitOpTree[" << c << "]\n"; + nodep->dumpTree(std::cout); + std::cout << "\nResult:\n"; + newp->dumpTree(std::cout); + ++c; + } + nodep->replaceWith(newp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + } + return newp; + } static bool operandShiftSame(const AstNode* nodep) { const AstNodeBiop* np = VN_CAST_CONST(nodep, NodeBiop); { @@ -1741,8 +2215,8 @@ private: if (lhsp->varrefp()->name() < rhsp->varrefp()->name()) return true; if (lhsp->varrefp()->name() > rhsp->varrefp()->name()) return false; // But might be same name with different scopes - if (lhsp->varrefp()->varScopep() < rhsp->varrefp()->varScopep()) { return true; } - if (lhsp->varrefp()->varScopep() > rhsp->varrefp()->varScopep()) { return false; } + if (lhsp->varrefp()->varScopep() < rhsp->varrefp()->varScopep()) return true; + if (lhsp->varrefp()->varScopep() > rhsp->varrefp()->varScopep()) return false; // Or rarely, different data types if (lhsp->varrefp()->dtypep() < rhsp->varrefp()->dtypep()) return true; if (lhsp->varrefp()->dtypep() > rhsp->varrefp()->dtypep()) return false; @@ -2128,7 +2602,7 @@ private: // Ignored, can eliminate early virtual void visit(AstSysIgnore* nodep) override { iterateChildren(nodep); - if (m_doNConst) { VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } + if (m_doNConst) VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } // Simplify @@ -2221,17 +2695,18 @@ private: // ("AstOr {%a, AstAnd{AstNot{%b}, %c}} if %a.width1 if %a==%b", "AstOr{%a,%c}; %b.delete"); // Lhs/rhs would be implied; for non math operations you'd need $lhsp etc. - // Lint Checks + // v--- * * This op done on Verilog or C+++ mode, in all non-m_doConst stages // v--- *1* These ops are always first, as we warn before replacing - // v--- *V* This op is a verilog op, only in m_doV mode - // v--- *C* This op works on all constant children, allowed in m_doConst mode - // v--- *S* This op specifies a type should use short-circuiting of its lhs op + // v--- *C* This op is a (C)++ op, only in m_doCpp mode + // v--- *V* This op is a (V)erilog op, only in m_doV mode + // v--- *A* This op works on (A)ll constant children, allowed in m_doConst mode + // v--- *S* This op specifies a type should use (S)hort-circuiting of its lhs op TREEOP1("AstSel{warnSelect(nodep)}", "NEVER"); // Generic constants on both side. Do this first to avoid other replacements - TREEOPC("AstNodeBiop {$lhsp.castConst, $rhsp.castConst, nodep->isPredictOptimizable()}", "replaceConst(nodep)"); - TREEOPC("AstNodeUniop{$lhsp.castConst, !nodep->isOpaque(), nodep->isPredictOptimizable()}", "replaceConst(nodep)"); - TREEOPC("AstNodeQuadop{$lhsp.castConst, $rhsp.castConst, $thsp.castConst, $fhsp.castConst}", "replaceConst(nodep)"); + TREEOPA("AstNodeBiop {$lhsp.castConst, $rhsp.castConst, nodep->isPredictOptimizable()}", "replaceConst(nodep)"); + TREEOPA("AstNodeUniop{$lhsp.castConst, !nodep->isOpaque(), nodep->isPredictOptimizable()}", "replaceConst(nodep)"); + TREEOPA("AstNodeQuadop{$lhsp.castConst, $rhsp.castConst, $thsp.castConst, $fhsp.castConst}", "replaceConst(nodep)"); // Zero on one side or the other TREEOP ("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); TREEOP ("AstAnd {$lhsp.isZero, $rhsp, isTPure($rhsp)}", "replaceZero(nodep)"); // Can't use replaceZeroChkPure as we make this pattern in ChkPure @@ -2290,12 +2765,13 @@ private: TREEOP ("AstModDiv{$lhsp, operandIsPowTwo($rhsp)}", "replaceModAnd(nodep)"); // a % 2^n -> a&(2^n-1) TREEOP ("AstPow {operandIsTwo($lhsp), $rhsp}", "replacePowShift(nodep)"); // 2**a == 1<castAdd()->lhsp(),$rhsp}, $lhsp->castAdd()->rhsp()}"); // ((a+x)-y) -> (a+(x-y)) + TREEOPC("AstAnd {$lhsp.isOne, matchRedundantClean(nodep)}", "DONE") // 1 & (a == b) -> (IData)(a == b) // Trinary ops // Note V3Case::Sel requires Cond to always be conditionally executed in C to prevent core dump! TREEOP ("AstNodeCond{$condp.isZero, $expr1p, $expr2p}", "replaceWChild(nodep,$expr2p)"); TREEOP ("AstNodeCond{$condp.isNeqZero, $expr1p, $expr2p}", "replaceWChild(nodep,$expr1p)"); - TREEOPC("AstNodeCond{$condp.isZero, $expr1p.castConst, $expr2p.castConst}", "replaceWChild(nodep,$expr2p)"); - TREEOPC("AstNodeCond{$condp.isNeqZero, $expr1p.castConst, $expr2p.castConst}", "replaceWChild(nodep,$expr1p)"); + TREEOPA("AstNodeCond{$condp.isZero, $expr1p.castConst, $expr2p.castConst}", "replaceWChild(nodep,$expr2p)"); + TREEOPA("AstNodeCond{$condp.isNeqZero, $expr1p.castConst, $expr2p.castConst}", "replaceWChild(nodep,$expr1p)"); TREEOP ("AstNodeCond{$condp, operandsSame($expr1p,,$expr2p)}","replaceWChild(nodep,$expr1p)"); // This visit function here must allow for short-circuiting. TREEOPS("AstCond {$lhsp.isZero}", "replaceWIteratedThs(nodep)"); @@ -2446,6 +2922,8 @@ private: TREEOPV("AstRedAnd{$lhsp.castExtend, $lhsp->width() > VN_CAST($lhsp,,Extend)->lhsp()->width()}", "replaceZero(nodep)"); // &{0,...} => 0 Prevents compiler limited range error 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"); TREEOPV("AstOneHot{$lhsp.width1}", "replaceWLhs(nodep)"); TREEOPV("AstOneHot0{$lhsp.width1}", "replaceNum(nodep,1)"); // Binary AND/OR is faster than logical and/or (usually) @@ -2467,6 +2945,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"); // 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())"); @@ -2486,7 +2967,7 @@ private: TREEOPV("AstSel{$fromp.castSub, operandSelBiLower(nodep)}", "DONE"); TREEOPV("AstSel{$fromp.castXor, operandSelBiLower(nodep)}", "DONE"); TREEOPV("AstSel{$fromp.castShiftR, operandSelShiftLower(nodep)}", "DONE"); - TREEOPC("AstSel{$fromp.castConst, $lsbp.castConst, $widthp.castConst, }", "replaceConst(nodep)"); + TREEOPA("AstSel{$fromp.castConst, $lsbp.castConst, $widthp.castConst, }", "replaceConst(nodep)"); TREEOPV("AstSel{$fromp.castConcat, $lsbp.castConst, $widthp.castConst, }", "replaceSelConcat(nodep)"); TREEOPV("AstSel{$fromp.castReplicate, $lsbp.castConst, $widthp.castConst, operandSelReplicate(nodep) }", "DONE"); // V3Tristate requires selects below BufIf1. @@ -2502,9 +2983,9 @@ private: TREEOPV("AstLogIf{$lhsp, $rhsp}", "AstLogOr{AstLogNot{$lhsp},$rhsp}"); TREEOPV("AstLogEq{$lhsp, $rhsp}", "replaceLogEq(nodep)"); // Strings - TREEOPC("AstPutcN{$lhsp.castConst, $rhsp.castConst, $thsp.castConst}", "replaceConst(nodep)"); - TREEOPC("AstSubstrN{$lhsp.castConst, $rhsp.castConst, $thsp.castConst}", "replaceConst(nodep)"); - TREEOPC("AstCvtPackString{$lhsp.castConst}", "replaceConstString(nodep, VN_CAST(nodep->lhsp(), Const)->num().toString())"); + TREEOPA("AstPutcN{$lhsp.castConst, $rhsp.castConst, $thsp.castConst}", "replaceConst(nodep)"); + TREEOPA("AstSubstrN{$lhsp.castConst, $rhsp.castConst, $thsp.castConst}", "replaceConst(nodep)"); + TREEOPA("AstCvtPackString{$lhsp.castConst}", "replaceConstString(nodep, VN_CAST(nodep->lhsp(), Const)->num().toString())"); // Custom // Implied by AstIsUnbounded::numberOperate: V("AstIsUnbounded{$lhsp.castConst}", "replaceNum(nodep, 0)"); TREEOPV("AstIsUnbounded{$lhsp.castUnbounded}", "replaceNum(nodep, 1)"); @@ -2564,7 +3045,12 @@ public: } // clang-format on } - virtual ~ConstVisitor() override = default; + virtual ~ConstVisitor() override { + if (m_doCpp) { + V3Stats::addStat("Optimizations, Const bit op reduction", m_statBitOpReduction); + } + } + AstNode* mainAcceptEdit(AstNode* nodep) { // Operate starting at a random place return iterateSubtreeReturnEdits(nodep); diff --git a/src/V3Descope.cpp b/src/V3Descope.cpp index 237da3a1c..c9aa2bc88 100644 --- a/src/V3Descope.cpp +++ b/src/V3Descope.cpp @@ -60,7 +60,7 @@ private: int instances = 0; for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { if (VN_IS(stmtp, Scope)) { - if (++instances > 1) { return false; } + if (++instances > 1) return false; } } return (instances == 1); diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 95cb52af0..00fe1eeeb 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -693,7 +693,7 @@ public: iterateAndNextNull(nodep->offset()); puts(","); iterateAndNextNull(nodep->operation()); - puts(")==-1?-1:0)"); + puts(") == -1 ? -1 : 0)"); } virtual void visit(AstFTell* nodep) override { puts("VL_FTELL_I("); @@ -703,7 +703,7 @@ public: virtual void visit(AstFRewind* nodep) override { puts("(VL_FSEEK_I("); iterateAndNextNull(nodep->filep()); - puts(", 0, 0)==-1?-1:0)"); + puts(", 0, 0) == -1 ? -1 : 0)"); } virtual void visit(AstFRead* nodep) override { puts("VL_FREAD_I("); @@ -748,7 +748,7 @@ public: } else { puts(cvtToStr(array_size)); } - puts(");\n"); + puts(")"); } virtual void visit(AstSysFuncAsTask* nodep) override { if (!nodep->lhsp()->isWide()) puts("(void)"); @@ -1035,8 +1035,8 @@ public: emitIQW(nodep); puts("OI("); puts(cvtToStr(nodep->widthMin())); - if (nodep->lhsp()) { puts("," + cvtToStr(nodep->lhsp()->widthMin())); } - if (nodep->rhsp()) { puts("," + cvtToStr(nodep->rhsp()->widthMin())); } + if (nodep->lhsp()) puts("," + cvtToStr(nodep->lhsp()->widthMin())); + if (nodep->rhsp()) puts("," + cvtToStr(nodep->rhsp()->widthMin())); puts(","); iterateAndNextNull(nodep->lhsp()); puts(", "); @@ -2026,7 +2026,7 @@ void EmitCStmts::emitOpName(AstNode* nodep, const string& format, AstNode* lhsp, nextComma = ","; needComma = false; } - if (pos[1] == ' ') { ++pos; } // Must do even if no nextComma + if (pos[1] == ' ') ++pos; // Must do even if no nextComma } else if (pos[0] == '%') { ++pos; bool detail = false; @@ -2427,10 +2427,12 @@ void EmitCImp::emitCtorImp(AstNodeModule* modp) { if (VN_IS(modp, Class)) { modp->v3fatalSrc("constructors should be AstCFuncs instead"); } else if (optSystemC() && modp->isTop()) { - puts("VL_SC_CTOR_IMP(" + prefixNameProtect(modp) + ")"); + puts(prefixNameProtect(modp) + "::" + prefixNameProtect(modp) + "(sc_module_name)"); } else { - puts("VL_CTOR_IMP(" + prefixNameProtect(modp) + ")"); - first = false; // VL_CTOR_IMP includes the first ':' + puts(prefixNameProtect(modp) + "::" + prefixNameProtect(modp) + + "(const char* __VCname)\n"); + puts(" : VerilatedModule(__VCname)\n"); + first = false; // printed the first ':' } emitVarCtors(&first); if (modp->isTop() && v3Global.opt.mtasks()) emitMTaskVertexCtors(&first); @@ -3080,6 +3082,13 @@ void EmitCImp::emitIntTop(AstNodeModule*) { 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 @@ -3177,7 +3186,7 @@ void EmitCImp::emitInt(AstNodeModule* modp) { puts("virtual ~" + prefixNameProtect(modp) + "();\n"); } else if (optSystemC()) { ofp()->putsPrivate(false); // public: - puts("VL_CTOR(" + prefixNameProtect(modp) + ");\n"); + puts(prefixNameProtect(modp) + "(const char* __VCname = \"\");\n"); puts("~" + prefixNameProtect(modp) + "();\n"); } else { ofp()->putsPrivate(false); // public: @@ -3868,7 +3877,7 @@ public: // Put out the file newOutCFile(0); - if (m_slow) { emitTraceSlow(); } + if (m_slow) emitTraceSlow(); iterate(v3Global.rootp()); diff --git a/src/V3EmitCMain.cpp b/src/V3EmitCMain.cpp index 63e9abea6..768694416 100644 --- a/src/V3EmitCMain.cpp +++ b/src/V3EmitCMain.cpp @@ -55,8 +55,6 @@ private: puts("\n//======================\n\n"); - puts(topClassName() + "* topp;\n"); - puts("\n"); puts("// Requires -DVL_TIME_STAMP64\n"); v3Global.opt.addCFlags("-DVL_TIME_STAMP64"); puts("vluint64_t main_time = 0;\n"); @@ -67,10 +65,15 @@ private: puts("// Setup defaults and parse command line\n"); puts("Verilated::debug(0);\n"); puts("Verilated::commandArgs(argc, argv);\n"); + puts("\n"); + puts("// Construct the Verilated model, from Vtop.h generated from Verilating\n"); - puts("topp = new " + topClassName() + "(\"top\");\n"); + puts("const std::unique_ptr<" + topClassName() + "> topp{new " + topClassName() + "};\n"); + puts("\n"); + puts("// Evaluate initials\n"); puts("topp->eval(); // Evaluate\n"); + puts("\n"); puts("// Simulate until $finish\n"); puts("while (!Verilated::gotFinish()) {\n"); @@ -88,8 +91,7 @@ private: puts("// Final model cleanup\n"); puts("topp->final();\n"); - puts("VL_DO_DANGLING(delete topp, topp);\n"); - puts("exit(0);\n"); + puts("return 0;\n"); puts("}\n"); } }; diff --git a/src/V3EmitCMake.cpp b/src/V3EmitCMake.cpp index ebf8c07c9..e9097abde 100644 --- a/src/V3EmitCMake.cpp +++ b/src/V3EmitCMake.cpp @@ -58,7 +58,7 @@ class CMakeEmitter final { static void cmake_set_raw(std::ofstream& of, const string& name, const string& raw_value, const string& cache_type = "", const string& docstring = "") { of << "set(" << name << " " << raw_value; - if (!cache_type.empty()) { of << " CACHE " << cache_type << " \"" << docstring << "\""; } + if (!cache_type.empty()) of << " CACHE " << cache_type << " \"" << docstring << "\""; of << ")\n"; } diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index d6392ef64..d9c0882b0 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -332,7 +332,7 @@ class EmitCSyms final : EmitCBaseVisitor { virtual void visit(AstVar* nodep) override { nameCheck(nodep); iterateChildren(nodep); - if (nodep->isSigUserRdPublic()) { m_modVars.emplace_back(make_pair(m_modp, nodep)); } + if (nodep->isSigUserRdPublic()) m_modVars.emplace_back(make_pair(m_modp, nodep)); } virtual void visit(AstCoverDecl* nodep) override { // Assign numbers to all bins, so we know how big of an array to use @@ -620,7 +620,7 @@ void EmitCSyms::emitSymImp() { + "& os) {\n"); puts("// LOCAL STATE\n"); // __Vm_namep presumably already correct - if (v3Global.opt.trace()) { puts("os" + op + "__Vm_activity;\n"); } + if (v3Global.opt.trace()) puts("os" + op + "__Vm_activity;\n"); puts("os" + op + "__Vm_didInit;\n"); puts("// SUBCELL STATE\n"); for (std::vector::iterator it = m_scopes.begin(); it != m_scopes.end(); diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index 8464de0eb..ab5ebabc2 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -101,10 +101,10 @@ public: // have them. } else if (support == 2 && !slow) { putMakeClassEntry(of, "verilated.cpp"); - if (v3Global.dpi()) { putMakeClassEntry(of, "verilated_dpi.cpp"); } - if (v3Global.opt.vpi()) { putMakeClassEntry(of, "verilated_vpi.cpp"); } - if (v3Global.opt.savable()) { putMakeClassEntry(of, "verilated_save.cpp"); } - if (v3Global.opt.coverage()) { putMakeClassEntry(of, "verilated_cov.cpp"); } + if (v3Global.dpi()) putMakeClassEntry(of, "verilated_dpi.cpp"); + if (v3Global.opt.vpi()) putMakeClassEntry(of, "verilated_vpi.cpp"); + if (v3Global.opt.savable()) putMakeClassEntry(of, "verilated_save.cpp"); + if (v3Global.opt.coverage()) putMakeClassEntry(of, "verilated_cov.cpp"); if (v3Global.opt.trace()) { putMakeClassEntry(of, v3Global.opt.traceSourceBase() + "_c.cpp"); if (v3Global.opt.systemC()) { @@ -117,7 +117,7 @@ public: } } } - if (v3Global.opt.mtasks()) { putMakeClassEntry(of, "verilated_threads.cpp"); } + if (v3Global.opt.mtasks()) putMakeClassEntry(of, "verilated_threads.cpp"); } else if (support == 2 && slow) { } else { for (AstNodeFile* nodep = v3Global.rootp()->filesp(); nodep; @@ -298,8 +298,8 @@ class EmitMkHierVerilation final { of.puts("VM_HIER_RUN_DIR := " + cwd + "\n"); of.puts("# Common options for hierarchical blocks\n"); const string fullpath_bin = V3Os::filenameRealPath(v3Global.opt.bin()); - const string perl_wrapper = V3Os::filenameDir(fullpath_bin) + "/verilator"; - of.puts("VM_HIER_VERILATOR := " + perl_wrapper + "\n"); + const string verilator_wrapper = V3Os::filenameDir(fullpath_bin) + "/verilator"; + of.puts("VM_HIER_VERILATOR := " + verilator_wrapper + "\n"); of.puts("VM_HIER_INPUT_FILES := \\\n"); const V3StringList& vFiles = v3Global.opt.vFiles(); for (const string& i : vFiles) of.puts("\t" + V3Os::filenameRealPath(i) + " \\\n"); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 026d48d16..238341569 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -815,7 +815,7 @@ public: AstSenTree* domainp, bool user3mark) : EmitVBaseVisitor{false, domainp} , m_formatter{os, prefix, flWidth} { - if (user3mark) { AstUser3InUse::check(); } + if (user3mark) AstUser3InUse::check(); iterate(nodep); } virtual ~EmitVPrefixedVisitor() override = default; diff --git a/src/V3EmitXml.cpp b/src/V3EmitXml.cpp index 27770caa5..e02173e09 100644 --- a/src/V3EmitXml.cpp +++ b/src/V3EmitXml.cpp @@ -55,7 +55,7 @@ class EmitXmlFileVisitor final : public AstNVisitor { // XML methods void outputId(AstNode* nodep) { - if (!nodep->user1()) { nodep->user1(++m_id); } + if (!nodep->user1()) nodep->user1(++m_id); puts("\"" + cvtToStr(nodep->user1()) + "\""); } void outputTag(AstNode* nodep, const string& tagin) { @@ -336,7 +336,7 @@ private: } } virtual void visit(AstCell* nodep) override { - if (nodep->modp()->dead()) { return; } + if (nodep->modp()->dead()) return; if (!m_hasChildren) m_os << ">\n"; m_os << "fileline()->xml() << " " << nodep->fileline()->xmlDetailedLocation() << " name=\"" << nodep->name() << "\"" diff --git a/src/V3Error.h b/src/V3Error.h index 9f3b8daee..374bc4415 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -364,12 +364,12 @@ inline void v3errorEndFatal(std::ostringstream& sstr) { // Assertion without object, generally UOBJASSERT preferred #define UASSERT(condition, stmsg) \ do { \ - if (VL_UNCOVERABLE(!(condition))) { v3fatalSrc(stmsg); } \ + if (VL_UNCOVERABLE(!(condition))) v3fatalSrc(stmsg); \ } while (false) // Assertion with object #define UASSERT_OBJ(condition, obj, stmsg) \ do { \ - if (VL_UNCOVERABLE(!(condition))) { (obj)->v3fatalSrc(stmsg); } \ + if (VL_UNCOVERABLE(!(condition))) (obj)->v3fatalSrc(stmsg); \ } while (false) // For use in V3Ast static functions only #define UASSERT_STATIC(condition, stmsg) \ diff --git a/src/V3File.cpp b/src/V3File.cpp index d04a70b8e..9b39bd425 100644 --- a/src/V3File.cpp +++ b/src/V3File.cpp @@ -320,7 +320,7 @@ void V3File::createMakeDir() { if (!created) { created = true; V3Os::createDir(v3Global.opt.makeDir()); - if (v3Global.opt.hierTop()) { V3Os::createDir(v3Global.opt.hierTopDataDir()); } + if (v3Global.opt.hierTop()) V3Os::createDir(v3Global.opt.hierTopDataDir()); } } @@ -787,11 +787,11 @@ void V3OutFormatter::puts(const char* strg) { wordstart = false; break; case 'e': - if (wordstart && m_lang == LA_VERILOG && tokenEnd(cp)) { indentDec(); } + if (wordstart && m_lang == LA_VERILOG && tokenEnd(cp)) indentDec(); wordstart = false; break; case 'm': - if (wordstart && m_lang == LA_VERILOG && tokenStart(cp, "module")) { indentInc(); } + if (wordstart && m_lang == LA_VERILOG && tokenStart(cp, "module")) indentInc(); wordstart = false; break; default: wordstart = false; break; diff --git a/src/V3FileLine.cpp b/src/V3FileLine.cpp index aabcea844..272277948 100644 --- a/src/V3FileLine.cpp +++ b/src/V3FileLine.cpp @@ -338,7 +338,7 @@ void FileLine::modifyStateInherit(const FileLine* fromp) { // Any warnings that are off in "from", become off in "this". for (int codei = V3ErrorCode::EC_MIN; codei < V3ErrorCode::_ENUM_MAX; codei++) { V3ErrorCode code = V3ErrorCode(codei); - if (fromp->warnIsOff(code)) { warnOff(code, true); } + if (fromp->warnIsOff(code)) warnOff(code, true); } } @@ -358,7 +358,7 @@ void FileLine::v3errorEnd(std::ostringstream& sstr, const string& locationStr) { } else if (!V3Error::errorContexted()) { nsstr << warnContextPrimary(); } - if (!m_waive) { V3Waiver::addEntry(V3Error::errorCode(), filename(), sstr.str()); } + if (!m_waive) V3Waiver::addEntry(V3Error::errorCode(), filename(), sstr.str()); V3Error::v3errorEnd(nsstr, lstr.str()); } diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp index e03443083..3c1a92e9e 100644 --- a/src/V3Gate.cpp +++ b/src/V3Gate.cpp @@ -264,7 +264,7 @@ private: virtual void visit(AstNode* nodep) override { // *** Special iterator if (!m_isSimple) return; // Fastpath - if (++m_ops > v3Global.opt.gateStmts()) { clearSimple("--gate-stmts exceeded"); } + if (++m_ops > v3Global.opt.gateStmts()) clearSimple("--gate-stmts exceeded"); if (!(m_dedupe ? nodep->isGateDedupable() : nodep->isGateOptimizable()) // || !nodep->isPure() || nodep->isBrancher()) { UINFO(5, "Non optimizable type: " << nodep << endl); @@ -282,7 +282,7 @@ public: // Iterate iterate(nodep); // Check results - if (!m_substTreep) { clearSimple("No assignment found\n"); } + if (!m_substTreep) clearSimple("No assignment found\n"); for (GateVarRefList::const_iterator it = m_rhsVarRefs.begin(); it != m_rhsVarRefs.end(); ++it) { if (m_lhsVarRef && m_lhsVarRef->varScopep() == (*it)->varScopep()) { diff --git a/src/V3Graph.cpp b/src/V3Graph.cpp index 76ab948b1..c4e28599d 100644 --- a/src/V3Graph.cpp +++ b/src/V3Graph.cpp @@ -377,7 +377,7 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const { << (edgep->dotLabel() != "" ? edgep->dotLabel() : "") << "\"" << " weight=" << edgep->weight() << " color=" << edgep->dotColor(); if (edgep->dotStyle() != "") *logp << " style=" << edgep->dotStyle(); - // if (edgep->cutable()) { *logp<<",constraint=false"; } // to rank without + // if (edgep->cutable()) *logp << ",constraint=false"; // to rank without // following edges *logp << "];\n"; } diff --git a/src/V3GraphAlg.cpp b/src/V3GraphAlg.cpp index 7cc32dd55..f1e769f97 100644 --- a/src/V3GraphAlg.cpp +++ b/src/V3GraphAlg.cpp @@ -271,7 +271,9 @@ private: } for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) { - if (!vertexp->user()) { vertexIterate(vertexp, 1); } + if (!vertexp->user()) { // + vertexIterate(vertexp, 1); + } } } diff --git a/src/V3GraphDfa.cpp b/src/V3GraphDfa.cpp index ad77d1c60..555c19d94 100644 --- a/src/V3GraphDfa.cpp +++ b/src/V3GraphDfa.cpp @@ -445,7 +445,7 @@ private: for (V3GraphVertex *nextp, *vertexp = m_graphp->verticesBeginp(); vertexp; vertexp = nextp) { nextp = vertexp->verticesNextp(); - if (!vertexp->user()) { VL_DO_DANGLING(vertexp->unlinkDelete(m_graphp), vertexp); } + if (!vertexp->user()) VL_DO_DANGLING(vertexp->unlinkDelete(m_graphp), vertexp); } } diff --git a/src/V3GraphStream.h b/src/V3GraphStream.h index 06e887699..b58ea458a 100644 --- a/src/V3GraphStream.h +++ b/src/V3GraphStream.h @@ -175,7 +175,7 @@ public: // Wrap curIt. Expect to wrap, and make another pass, to find // newly-ready elements that could have appeared ahead of the // m_last iterator - if (curIt == m_readyVertices.end()) { curIt = m_readyVertices.begin(); } + if (curIt == m_readyVertices.end()) curIt = m_readyVertices.begin(); } if (curIt != m_readyVertices.end()) { diff --git a/src/V3HierBlock.cpp b/src/V3HierBlock.cpp index 3f12fcfe4..3dd3f730e 100644 --- a/src/V3HierBlock.cpp +++ b/src/V3HierBlock.cpp @@ -224,7 +224,7 @@ void V3HierBlock::writeCommandArgsFile(bool forCMake) const { for (const auto& hierblockp : m_children) *of << hierblockp->hierBlockArgs().front() << "\n"; // Hierarchical blocks should not use multi-threading, // but needs to be thread safe when top is multi-threaded. - if (v3Global.opt.threads() > 0) { *of << "--threads 1\n"; } + if (v3Global.opt.threads() > 0) *of << "--threads 1\n"; *of << v3Global.opt.allArgsStringForHierBlock(false) << "\n"; } diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index e81a28365..e1ba954db 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -108,7 +108,9 @@ private: // If inlining moves post-scope this can perhaps be relaxed. cantInline("modIface", true); } - if (m_modp->modPublic()) cantInline("modPublic", false); + if (m_modp->modPublic() && (m_modp->isTop() || !v3Global.opt.flatten())) { + cantInline("modPublic", false); + } iterateChildren(nodep); m_modp = nullptr; @@ -137,7 +139,7 @@ private: } else if (nodep->pragType() == AstPragmaType::NO_INLINE_MODULE) { if (!m_modp) { nodep->v3error("Inline pragma not under a module"); // LCOV_EXCL_LINE - } else { + } else if (!v3Global.opt.flatten()) { cantInline("Pragma NO_INLINE_MODULE", false); } // Remove so don't propagate to upper cell... @@ -379,8 +381,8 @@ private: string name = m_cellp->name() + "__DOT__" + nodep->name(); if (!nodep->isFuncLocal() && !nodep->isClassMember()) nodep->inlineAttrReset(name); if (!m_cellp->isTrace()) nodep->trace(false); - if (debug() >= 9) { nodep->dumpTree(cout, "varchanged:"); } - if (debug() >= 9 && nodep->valuep()) { nodep->valuep()->dumpTree(cout, "varchangei:"); } + if (debug() >= 9) nodep->dumpTree(cout, "varchanged:"); + if (debug() >= 9 && nodep->valuep()) nodep->valuep()->dumpTree(cout, "varchangei:"); iterateChildren(nodep); } virtual void visit(AstNodeFTask* nodep) override { @@ -593,7 +595,7 @@ private: // Cleanup var names, etc, to not conflict { InlineRelinkVisitor(newmodp, m_modp, nodep); } // Move statements to top module - if (debug() >= 9) { newmodp->dumpTree(cout, "fixmod:"); } + if (debug() >= 9) newmodp->dumpTree(cout, "fixmod:"); AstNode* stmtsp = newmodp->stmtsp(); if (stmtsp) stmtsp->unlinkFrBackWithNext(); if (stmtsp) m_modp->addStmtp(stmtsp); @@ -601,7 +603,7 @@ private: VL_DO_DANGLING(newmodp->deleteTree(), newmodp); // Clear any leftover ports, etc nodep->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(nodep), nodep); - if (debug() >= 9) { m_modp->dumpTree(cout, "donemod:"); } + if (debug() >= 9) m_modp->dumpTree(cout, "donemod:"); } } @@ -636,7 +638,7 @@ private: // VISITORS virtual void visit(AstNetlist* nodep) override { iterateChildren(nodep); } virtual void visit(AstModule* nodep) override { - if (nodep->isTop()) { iterateChildren(nodep); } + if (nodep->isTop()) iterateChildren(nodep); } virtual void visit(AstCell* nodep) override { VL_RESTORER(m_scope); diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index fb950ae6c..055c710ad 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -396,7 +396,8 @@ private: AstNode* prevPinp = nullptr; // Clone the var referenced by the pin, and clone each var referenced by the varref // Clone pin varp: - for (int i = pinArrp->lo(); i <= pinArrp->hi(); ++i) { + for (int in = 0; in < pinArrp->elementsConst(); ++in) { + int i = pinArrp->left() + in * pinArrp->declRange().leftToRightInc(); string varNewName = pinVarp->name() + "__BRA__" + cvtToStr(i) + "__KET__"; AstVar* varNewp = nullptr; @@ -429,16 +430,20 @@ private: newp->modVarp(varNewp); newp->name(newp->name() + "__BRA__" + cvtToStr(i) + "__KET__"); // And replace exprp with a new varxref - int offset = 0; const AstVarRef* varrefp = VN_CAST(newp->exprp(), VarRef); + int offset = 0; if (varrefp) { } else if (AstSliceSel* slicep = VN_CAST(newp->exprp(), SliceSel)) { varrefp = VN_CAST(slicep->fromp(), VarRef); UASSERT(VN_IS(slicep->rhsp(), Const), "Slices should be constant"); offset = VN_CAST(slicep->rhsp(), Const)->toSInt(); } - if (!varrefp) { newp->exprp()->v3error("Unexpected connection to arrayed port"); } - string newname = varrefp->name() + "__BRA__" + cvtToStr(i + offset) + "__KET__"; + int expr_i = i; + if (auto* exprArrp = VN_CAST(newp->exprp()->dtypep(), UnpackArrayDType)) + expr_i = exprArrp->left() + + (in + offset) * exprArrp->declRange().leftToRightInc(); + if (!varrefp) newp->exprp()->v3error("Unexpected connection to arrayed port"); + string newname = varrefp->name() + "__BRA__" + cvtToStr(expr_i) + "__KET__"; AstVarXRef* newVarXRefp = new AstVarXRef(nodep->fileline(), newname, "", VAccess::WRITE); newVarXRefp->varp(newp->modVarp()); @@ -538,7 +543,7 @@ public: // Done. Constant. } else { // Make a new temp wire - // if (1 || debug() >= 9) { pinp->dumpTree(cout, "-in_pin:"); } + // if (1 || debug() >= 9) pinp->dumpTree(cout, "-in_pin:"); V3Inst::checkOutputShort(pinp); AstNode* pinexprp = pinp->exprp()->unlinkFrBack(); string newvarname @@ -570,8 +575,8 @@ public: pinp->exprp(new AstVarRef(pinexprp->fileline(), newvarp, VAccess::READ)); } if (assignp) cellp->addNextHere(assignp); - // if (debug()) { pinp->dumpTree(cout, "- out:"); } - // if (debug()) { assignp->dumpTree(cout, "- aout:"); } + // if (debug()) pinp->dumpTree(cout, "- out:"); + // if (debug()) assignp->dumpTree(cout, "- aout:"); } return assignp; } diff --git a/src/V3LifePost.cpp b/src/V3LifePost.cpp index 1dc7865d6..c578335c7 100644 --- a/src/V3LifePost.cpp +++ b/src/V3LifePost.cpp @@ -110,8 +110,8 @@ public: bool operator<(const LifeLocation& b) const { unsigned a_id = mtaskp ? mtaskp->id() : 0; unsigned b_id = b.mtaskp ? b.mtaskp->id() : 0; - if (a_id < b_id) { return true; } - if (b_id < a_id) { return false; } + if (a_id < b_id) return true; + if (b_id < a_id) return false; return sequence < b.sequence; } }; diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index ef716e74f..0578e5bc7 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -453,7 +453,9 @@ private: nodep->hasIfaceVar(true); } } - if (nodep->modp()) { iterateChildren(nodep); } + if (nodep->modp()) { // + iterateChildren(nodep); + } UINFO(4, " Link Cell done: " << nodep << endl); } diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 0c283904a..f6dafdc1b 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -369,7 +369,7 @@ public: symp->classOrPackagep(classOrPackagep); symp->fallbackp(abovep); nodep->user1p(symp); - if (name != "") { checkDuplicate(abovep, nodep, name); } + if (name != "") checkDuplicate(abovep, nodep, name); // Duplicates are possible, as until resolve generates might have 2 same cells under an if abovep->reinsert(name, symp); return symp; @@ -409,7 +409,7 @@ public: // Mark the given variable name as being allowed to be implicitly declared if (nodep) { const auto it = m_implicitNameSet.find(make_pair(nodep, varname)); - if (it == m_implicitNameSet.end()) { m_implicitNameSet.emplace(nodep, varname); } + if (it == m_implicitNameSet.end()) m_implicitNameSet.emplace(nodep, varname); } } bool implicitOk(AstNodeModule* nodep, const string& varname) { @@ -2107,7 +2107,7 @@ private: && (VN_IS(nodep->lhsp(), CellRef) || VN_IS(nodep->lhsp(), CellArrayRef))) { m_ds.m_unlinkedScopep = nodep->lhsp(); } - if (VN_IS(nodep->lhsp(), LambdaArgRef)) { m_ds.m_unlinkedScopep = nodep->lhsp(); } + if (VN_IS(nodep->lhsp(), LambdaArgRef)) m_ds.m_unlinkedScopep = nodep->lhsp(); if (!m_ds.m_dotErr) { // Once something wrong, give up // Top 'final' dot RHS is final RHS, else it's a // DOT(DOT(x,*here*),real-rhs) which we consider a RHS @@ -2152,7 +2152,7 @@ private: // Generally resolved during Primay, but might be at param time under AstUnlinkedRef UASSERT_OBJ(m_statep->forPrimary() || m_statep->forPrearray(), nodep, "ParseRefs should no longer exist"); - if (nodep->name() == "super") { nodep->v3warn(E_UNSUPPORTED, "Unsupported: super"); } + if (nodep->name() == "super") nodep->v3warn(E_UNSUPPORTED, "Unsupported: super"); DotStates lastStates = m_ds; bool start = (m_ds.m_dotPos == DP_NONE); // Save, as m_dotp will be changed if (start) { diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index 8572a25e9..96ef55d56 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -102,11 +102,12 @@ void V3LinkLevel::timescaling(const ModVec& mods) { for (AstNodeModule* nodep : mods) { if (nodep->timeunit().isNone()) { - if (modTimedp && !VN_IS(nodep, Iface) + if (modTimedp && !VN_IS(nodep, Iface) && !VN_IS(nodep, Primitive) && !(VN_IS(nodep, Package) && VN_CAST(nodep, Package)->isDollarUnit())) { nodep->v3warn(TIMESCALEMOD, "Timescale missing on this module as other modules have " "it (IEEE 1800-2017 3.14.2.2)\n" + << nodep->warnContextPrimary() << '\n' << modTimedp->warnOther() << "... Location of module with timescale\n" << modTimedp->warnContextSecondary()); diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index f2d1dbb6a..f2f4c33a2 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -134,7 +134,7 @@ private: iterateChildren(nodep); } m_ftaskp = nullptr; - if (nodep->dpiExport()) { nodep->scopeNamep(new AstScopeName(nodep->fileline())); } + if (nodep->dpiExport()) nodep->scopeNamep(new AstScopeName(nodep->fileline())); } virtual void visit(AstNodeFTaskRef* nodep) override { iterateChildren(nodep); @@ -235,11 +235,9 @@ private: fromp->cloneTree(false))); } else if (VN_IS(basefromp, Replicate)) { // From {...}[...] syntax in IEEE 2017 - if (basefromp) { UINFO(1, " Related node: " << basefromp << endl); } - nodep->v3warn(E_UNSUPPORTED, "Unsupported: Select of concatenation"); - nodep = nullptr; + if (basefromp) UINFO(1, " Related node: " << basefromp << endl); } else { - if (basefromp) { UINFO(1, " Related node: " << basefromp << endl); } + if (basefromp) UINFO(1, " Related node: " << basefromp << endl); nodep->v3fatalSrc("Illegal bit select; no signal/member being extracted from"); } } diff --git a/src/V3MergeCond.cpp b/src/V3MergeCond.cpp index 873048ee1..0cb527c42 100644 --- a/src/V3MergeCond.cpp +++ b/src/V3MergeCond.cpp @@ -147,7 +147,7 @@ private: return condp; } else if (AstAnd* const andp = VN_CAST(rhsp, And)) { if (AstNodeCond* const condp = VN_CAST(andp->rhsp(), NodeCond)) { - if (VN_IS(andp->lhsp(), Const)) { return condp; } + if (VN_IS(andp->lhsp(), Const)) return condp; } } return nullptr; @@ -174,7 +174,9 @@ private: } else if (AstNodeCond* const condp = extractCond(rhsp)) { AstNode* const resp = condTrue ? condp->expr1p()->unlinkFrBack() : condp->expr2p()->unlinkFrBack(); - if (condp == rhsp) { return resp; } + if (condp == rhsp) { // + return resp; + } 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); diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 819e47339..91a52ece2 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -401,7 +401,7 @@ V3Number& V3Number::setLongS(vlsint32_t value) { return *this; } V3Number& V3Number::setDouble(double value) { - if (VL_UNCOVERABLE(width() != 64)) { v3fatalSrc("Real operation on wrong sized number"); } + if (VL_UNCOVERABLE(width() != 64)) v3fatalSrc("Real operation on wrong sized number"); m_double = true; union { double d; @@ -951,10 +951,10 @@ bool V3Number::isAnyZ() const { bool V3Number::isLtXZ(const V3Number& rhs) const { // Include X/Z in comparisons for sort ordering for (int bit = 0; bit < std::max(this->width(), rhs.width()); bit++) { - if (this->bitIs1(bit) && rhs.bitIs0(bit)) { return true; } - if (rhs.bitIs1(bit) && this->bitIs0(bit)) { return false; } - if (this->bitIsXZ(bit)) { return true; } - if (rhs.bitIsXZ(bit)) { return false; } + if (this->bitIs1(bit) && rhs.bitIs0(bit)) return true; + if (rhs.bitIs1(bit) && this->bitIs0(bit)) return false; + if (this->bitIsXZ(bit)) return true; + if (rhs.bitIsXZ(bit)) return false; } return false; } @@ -1347,14 +1347,14 @@ V3Number& V3Number::opLogAnd(const V3Number& lhs, const V3Number& rhs) { loutc = 1; break; } - if (lhs.bitIsXZ(bit) && loutc == 0) { loutc = 'x'; } + if (lhs.bitIsXZ(bit) && loutc == 0) loutc = 'x'; } for (int bit = 0; bit < rhs.width(); bit++) { if (rhs.bitIs1(bit)) { routc = 1; break; } - if (rhs.bitIsXZ(bit) && routc == 0) { routc = 'x'; } + if (rhs.bitIsXZ(bit) && routc == 0) routc = 'x'; } char outc = 'x'; if (routc == 1 && loutc == 1) outc = 1; @@ -1528,7 +1528,7 @@ bool V3Number::isCaseEq(const V3Number& rhs) const { if (this->width() != rhs.width()) return false; for (int bit = 0; bit < std::max(this->width(), rhs.width()); bit++) { - if (this->bitIs(bit) != rhs.bitIs(bit)) { return false; } + if (this->bitIs(bit) != rhs.bitIs(bit)) return false; } return true; } diff --git a/src/V3Number.h b/src/V3Number.h index 47ca60a11..c2fef325b 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -109,6 +109,8 @@ private: return ("01zx"[(((m_value[bit / 32] & (1UL << (bit & 31))) ? 1 : 0) | ((m_valueX[bit / 32] & (1UL << (bit & 31))) ? 2 : 0))]); } + +public: bool bitIs0(int bit) const { if (bit < 0) return false; if (bit >= m_width) return !bitIsXZ(m_width - 1); @@ -144,6 +146,8 @@ private: return ((~m_value[bit / 32] & (1UL << (bit & 31))) && (m_valueX[bit / 32] & (1UL << (bit & 31)))); } + +private: uint32_t bitsValue(int lsb, int nbits) const { uint32_t v = 0; for (int bitn = 0; bitn < nbits; bitn++) { v |= (bitIs1(lsb + bitn) << bitn); } diff --git a/src/V3Number_test.cpp b/src/V3Number_test.cpp index 74055f5c1..07ba971e8 100644 --- a/src/V3Number_test.cpp +++ b/src/V3Number_test.cpp @@ -110,7 +110,7 @@ void test(const string& lhss, const string& op, const string& rhss, const string V3Number ok(fl, 1); ok.opCaseEq(expnum, gotnum); - if (ok.toUInt() != 1) { v3fatalSrc("%Error:Test FAILED"); } + if (ok.toUInt() != 1) v3fatalSrc("%Error:Test FAILED"); free(l1); free(r1); diff --git a/src/V3Options.cpp b/src/V3Options.cpp index fdc56aa08..99f6489e2 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -18,6 +18,7 @@ #include "verilatedos.h" #include "V3Global.h" +#include "V3Ast.h" #include "V3String.h" #include "V3Os.h" #include "V3Options.h" @@ -864,10 +865,10 @@ void V3Options::parseOpts(FileLine* fl, int argc, char** argv) { } // Default prefix to the filename - if (prefix() == "" && topModule() != "") m_prefix = string("V") + topModule(); - if (prefix() == "" && vFilesList.size() >= 1) { - m_prefix = string("V") + V3Os::filenameNonExt(*(vFilesList.begin())); - } + if (prefix() == "" && topModule() != "") + m_prefix = string("V") + AstNode::encodeName(topModule()); + if (prefix() == "" && vFilesList.size() >= 1) + m_prefix = string("V") + AstNode::encodeName(V3Os::filenameNonExt(*(vFilesList.begin()))); if (modPrefix() == "") m_modPrefix = prefix(); // Find files in makedir @@ -1369,7 +1370,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char string msg = sw + strlen("-Werror-"); V3ErrorCode code(msg.c_str()); if (code == V3ErrorCode::EC_ERROR) { - if (!isFuture(msg)) { fl->v3fatal("Unknown warning specified: " << sw); } + if (!isFuture(msg)) fl->v3fatal("Unknown warning specified: " << sw); } else { V3Error::pretendError(code, true); } @@ -1402,7 +1403,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char string msg = sw + strlen("-Wwarn-"); V3ErrorCode code(msg.c_str()); if (code == V3ErrorCode::EC_ERROR) { - if (!isFuture(msg)) { fl->v3fatal("Unknown warning specified: " << sw); } + if (!isFuture(msg)) fl->v3fatal("Unknown warning specified: " << sw); } else { FileLine::globalWarnOff(code, false); V3Error::pretendError(code, false); diff --git a/src/V3Order.cpp b/src/V3Order.cpp index c8509fa72..4612cf44b 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -487,7 +487,7 @@ public: for (V3GraphVertex* itp = m_graphp->verticesBeginp(); itp; itp = itp->verticesNextp()) { if (OrderLogicVertex* lvertexp = dynamic_cast(itp)) { T_MoveVertex* moveVxp = m_logic2move[lvertexp]; - if (moveVxp) { iterate(moveVxp, lvertexp, lvertexp->domainp()); } + if (moveVxp) iterate(moveVxp, lvertexp, lvertexp->domainp()); } } } @@ -1661,7 +1661,7 @@ void OrderVisitor::processMovePrepReady() { UINFO(5, " MovePrepReady\n"); for (OrderMoveVertex* vertexp = m_pomWaiting.begin(); vertexp;) { OrderMoveVertex* nextp = vertexp->pomWaitingNextp(); - if (vertexp->isWait() && vertexp->inEmpty()) { processMoveReadyOne(vertexp); } + if (vertexp->isWait() && vertexp->inEmpty()) processMoveReadyOne(vertexp); vertexp = nextp; } } diff --git a/src/V3Os.cpp b/src/V3Os.cpp index df66d97de..c3648025b 100644 --- a/src/V3Os.cpp +++ b/src/V3Os.cpp @@ -271,7 +271,7 @@ string V3Os::trueRandom(size_t size) { #if defined(_WIN32) || defined(__MINGW32__) NTSTATUS hr = BCryptGenRandom(nullptr, reinterpret_cast(data), size, BCRYPT_USE_SYSTEM_PREFERRED_RNG); - if (!BCRYPT_SUCCESS(hr)) { v3fatal("Could not acquire random data."); } + if (!BCRYPT_SUCCESS(hr)) v3fatal("Could not acquire random data."); #else std::ifstream is("/dev/urandom", std::ios::in | std::ios::binary); // This read uses the size of the buffer. diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index e19325194..349fd6cc3 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -178,7 +178,7 @@ void V3ParseImp::lexErrorPreprocDirective(FileLine* fl, const char* textp) { string V3ParseImp::lexParseTag(const char* textp) { string tmp = textp + strlen("/*verilator tag "); string::size_type pos; - if ((pos = tmp.rfind("*/")) != string::npos) { tmp.erase(pos); } + if ((pos = tmp.rfind("*/")) != string::npos) tmp.erase(pos); return tmp; } @@ -468,7 +468,7 @@ void V3ParseImp::tokenPipeline() { V3ParseBisonYYSType curValue = yylval; // Remember value, as about to read ahead { size_t depth = tokenPipeScanParam(0); - if (tokenPeekp(depth)->token == yP_COLONCOLON) { token = yaID__CC; } + if (tokenPeekp(depth)->token == yP_COLONCOLON) token = yaID__CC; } yylval = curValue; } diff --git a/src/V3ParseLex.cpp b/src/V3ParseLex.cpp index eedae87c1..a53f3d569 100644 --- a/src/V3ParseLex.cpp +++ b/src/V3ParseLex.cpp @@ -64,7 +64,7 @@ void V3ParseImp::yylexReadTok() { void V3ParseImp::lexNew() { if (m_lexerp) delete m_lexerp; // Restart from clean slate. m_lexerp = new V3Lexer(); - if (debugFlex() >= 9) { m_lexerp->set_debug(~0); } + if (debugFlex() >= 9) m_lexerp->set_debug(~0); } void V3ParseImp::lexDestroy() { diff --git a/src/V3Partition.cpp b/src/V3Partition.cpp index bf3c0b410..d8a2c6e5b 100644 --- a/src/V3Partition.cpp +++ b/src/V3Partition.cpp @@ -749,8 +749,8 @@ public: || LogicMTask::pathExistsFrom(m_bp, m_ap, nullptr)); } bool operator<(const SiblingMC& other) const { - if (m_ap->id() < other.m_ap->id()) { return true; } - if (m_ap->id() > other.m_ap->id()) { return false; } + if (m_ap->id() < other.m_ap->id()) return true; + if (m_ap->id() > other.m_ap->id()) return false; return m_bp->id() < other.m_bp->id(); } }; @@ -1245,7 +1245,7 @@ private: for (SibpSet::iterator it = m_mtask2sibs[mtaskp].begin(); it != m_mtask2sibs[mtaskp].end(); ++it) { const SiblingMC* pairp = *it; - if (!pairp->removedFromSb()) { m_sb.removeElem(pairp); } + if (!pairp->removedFromSb()) m_sb.removeElem(pairp); LogicMTask* otherp = (pairp->bp() == mtaskp) ? pairp->ap() : pairp->bp(); size_t erased = m_mtask2sibs[otherp].erase(pairp); UASSERT_OBJ(erased > 0, otherp, "Expected existing mtask"); @@ -1409,7 +1409,7 @@ private: return 1 + edgeScore(edgep); } const SiblingMC* sibsp = dynamic_cast(pairp); - if (sibsp) { return siblingScore(sibsp); } + if (sibsp) return siblingScore(sibsp); v3fatalSrc("Failed to cast pairp to either MTaskEdge or SiblingMC in mergeCandidateScore"); return 0; } @@ -1472,10 +1472,10 @@ private: const LogicMTask* bp = *reinterpret_cast(vbp); uint32_t aCp = ap->critPathCost(*wp) + ap->stepCost(); uint32_t bCp = bp->critPathCost(*wp) + bp->stepCost(); - if (aCp < bCp) { return -1; } - if (aCp > bCp) { return 1; } - if (ap->id() < bp->id()) { return -1; } - if (ap->id() > bp->id()) { return 1; } + if (aCp < bCp) return -1; + if (aCp > bCp) return 1; + if (ap->id() < bp->id()) return -1; + if (ap->id() > bp->id()) return 1; return 0; } @@ -1534,7 +1534,7 @@ private: for (unsigned i = 0; i < chain_len; ++i) { LogicMTask* mtp = new LogicMTask(&mtasks, nullptr); mtp->setCost(1); - if (lastp) { new MTaskEdge(&mtasks, lastp, mtp, 1); } + if (lastp) new MTaskEdge(&mtasks, lastp, mtp, 1); lastp = mtp; } partInitCriticalPaths(&mtasks); @@ -1802,7 +1802,7 @@ private: ++it) { LogicMTask* mtaskp = *it; if (mergedp) { - if (mergedp->cost() < mtaskp->cost()) { mergedp = mtaskp; } + if (mergedp->cost() < mtaskp->cost()) mergedp = mtaskp; } else { mergedp = mtaskp; } @@ -1983,7 +1983,7 @@ public: for (V3GraphVertex* vxp = m_mtasksp->verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { LogicMTask* mtaskp = dynamic_cast(vxp); - if (hasDpiHazard(mtaskp)) { tasksByRank[vxp->rank()].insert(mtaskp); } + if (hasDpiHazard(mtaskp)) tasksByRank[vxp->rank()].insert(mtaskp); } mergeSameRankTasks(&tasksByRank); } @@ -2415,7 +2415,7 @@ void V3Partition::go(V3Graph* mtasksp) { mtasksp->orderPreRanked(); int targetParFactor = v3Global.opt.threads(); - if (targetParFactor < 2) { v3fatalSrc("We should not reach V3Partition when --threads <= 1"); } + if (targetParFactor < 2) v3fatalSrc("We should not reach V3Partition when --threads <= 1"); // Set cpLimit to roughly totalGraphCost / nThreads // diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp index c62eb7ec1..4fadcb2b8 100644 --- a/src/V3PreProc.cpp +++ b/src/V3PreProc.cpp @@ -953,7 +953,7 @@ int V3PreProcImp::getStateToken() { // Most states emit white space and comments between tokens. (Unless collecting a string) if (tok == VP_WHITE && state() != ps_STRIFY) return tok; - if (tok == VP_BACKQUOTE && state() != ps_STRIFY) { tok = VP_TEXT; } + if (tok == VP_BACKQUOTE && state() != ps_STRIFY) tok = VP_TEXT; if (tok == VP_COMMENT) { if (!m_off) { if (m_lexp->m_keepComments == KEEPCMT_SUB) { diff --git a/src/V3PreProc.h b/src/V3PreProc.h index a51dab1bf..43d8f3a19 100644 --- a/src/V3PreProc.h +++ b/src/V3PreProc.h @@ -45,7 +45,7 @@ public: // CONSTANTS enum MiscConsts { DEFINE_RECURSION_LEVEL_MAX = 1000, // How many `def substitutions before an error - LINE_TOKEN_MAX = 20000, // How many tokens on a line before an error + LINE_TOKEN_MAX = 40000, // How many tokens on a line before an error INCLUDE_DEPTH_MAX = 500, // How many `includes deep before an error // Streams deep (sometimes `def deep) before an error. // Set more than DEFINE_RECURSION_LEVEL_MAX or INCLUDE_DEPTH_MAX. diff --git a/src/V3ProtectLib.cpp b/src/V3ProtectLib.cpp index af6e08918..2432ffada 100644 --- a/src/V3ProtectLib.cpp +++ b/src/V3ProtectLib.cpp @@ -205,7 +205,7 @@ private: m_tmpDeclsp = new AstTextBlock(fl); txtp->addNodep(m_tmpDeclsp); txtp->addText(fl, "\ntime last_combo_seqnum__V;\n"); - if (m_hasClk) { txtp->addText(fl, "time last_seq_seqnum__V;\n\n"); } + if (m_hasClk) txtp->addText(fl, "time last_seq_seqnum__V;\n\n"); // CPP hash value addComment(txtp, fl, "Hash value to make sure this file and the corresponding"); @@ -425,7 +425,7 @@ private: m_comboPortsp->addNodep(varp->cloneTree(false)); m_comboParamsp->addText(fl, varp->name() + "\n"); m_comboIgnorePortsp->addNodep(varp->cloneTree(false)); - if (m_hasClk) { m_comboIgnoreParamsp->addText(fl, varp->name() + "\n"); } + if (m_hasClk) m_comboIgnoreParamsp->addText(fl, varp->name() + "\n"); m_cComboParamsp->addText(fl, varp->dpiArgType(true, false) + "\n"); m_cComboInsp->addText(fl, cInputConnection(varp)); m_cIgnoreParamsp->addText(fl, varp->dpiArgType(true, false) + "\n"); diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index de7a4b362..231eb5fbe 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -141,7 +141,7 @@ class SliceVisitor final : public AstNVisitor { AstNode* newp = nodep->cloneType // AstNodeAssign (cloneAndSel(nodep->lhsp(), elements, offset), cloneAndSel(nodep->rhsp(), elements, offset)); - if (debug() >= 9) { newp->dumpTree(cout, "-new "); } + if (debug() >= 9) newp->dumpTree(cout, "-new "); newlistp = AstNode::addNextNull(newlistp, newp); } if (debug() >= 9) nodep->dumpTree(cout, " Deslice-Dn: "); diff --git a/src/V3SplitVar.cpp b/src/V3SplitVar.cpp index a3f0f4ef0..a6b33376b 100644 --- a/src/V3SplitVar.cpp +++ b/src/V3SplitVar.cpp @@ -853,7 +853,7 @@ 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* refp = VN_CAST(m_nodep, VarRef)) return VN_CAST(refp->backp(), SenItem); return nullptr; } }; @@ -971,7 +971,7 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { nodep->attrSplitVar(false); } else { // Finally find a good candidate const bool inserted = m_refs.insert(std::make_pair(nodep, PackedVarRef(nodep))).second; - if (inserted) { UINFO(3, nodep->prettyNameQ() << " is added to candidate list.\n"); } + if (inserted) UINFO(3, nodep->prettyNameQ() << " is added to candidate list.\n"); } } virtual void visit(AstVarRef* nodep) override { diff --git a/src/V3TSP.cpp b/src/V3TSP.cpp index e9f2503c6..f0666448d 100644 --- a/src/V3TSP.cpp +++ b/src/V3TSP.cpp @@ -379,7 +379,7 @@ public: for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) { degree++; } - if (degree & 1) { result.push_back(tspvp->key()); } + if (degree & 1) result.push_back(tspvp->key()); } return result; } @@ -402,7 +402,7 @@ void V3TSP::tspSort(const V3TSP::StateVec& states, V3TSP::StateVec* resultp) { // Make this TSP implementation work for graphs of size 0 or 1 // which, unfortunately, is a special case as the following // code assumes >= 2 nodes. - if (states.empty()) { return; } + if (states.empty()) return; if (states.size() == 1) { resultp->push_back(*(states.begin())); return; @@ -482,7 +482,7 @@ void V3TSP::tspSort(const V3TSP::StateVec& states, V3TSP::StateVec* resultp) { while (i != max_cost_idx) { new_result.push_back((*resultp)[i]); i++; - if (i >= resultp->size()) { i = 0; } + if (i >= resultp->size()) i = 0; } new_result.push_back((*resultp)[i]); diff --git a/src/V3Table.cpp b/src/V3Table.cpp index cc68dba7c..37ef613e1 100644 --- a/src/V3Table.cpp +++ b/src/V3Table.cpp @@ -135,7 +135,9 @@ private: if (m_totalBytes > TABLE_TOTAL_BYTES) { chkvis.clearOptimizable(nodep, "Table out of memory"); } - if (!m_outWidth || !m_inWidth) { chkvis.clearOptimizable(nodep, "Table has no outputs"); } + if (!m_outWidth || !m_inWidth) { // + chkvis.clearOptimizable(nodep, "Table has no outputs"); + } UINFO(4, " Test: Opt=" << (chkvis.optimizable() ? "OK" : "NO") << ", Instrs=" << chkvis.instrCount() << " Data=" << chkvis.dataCount() << " inw=" << m_inWidth diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 1ba20e2fd..8f0663b1e 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -182,7 +182,7 @@ private: } // Likewise, all FTask->scope mappings for (AstNode* stmtp = nodep->blocksp(); stmtp; stmtp = stmtp->nextp()) { - if (AstNodeFTask* taskp = VN_CAST(stmtp, NodeFTask)) { taskp->user3p(nodep); } + if (AstNodeFTask* taskp = VN_CAST(stmtp, NodeFTask)) taskp->user3p(nodep); } iterateChildren(nodep); } @@ -786,8 +786,8 @@ private: // Static doesn't need save-restore as if below will re-fill proper value stmt += "static int __Vfuncnum = -1;\n"; // First time init (faster than what the compiler does if we did a singleton - stmt += "if (VL_UNLIKELY(__Vfuncnum==-1)) { __Vfuncnum = Verilated::exportFuncNum(\"" - + nodep->cname() + "\"); }\n"; + stmt += "if (VL_UNLIKELY(__Vfuncnum == -1)) __Vfuncnum = Verilated::exportFuncNum(\"" + + nodep->cname() + "\");\n"; // If the find fails, it will throw an error stmt += "const VerilatedScope* __Vscopep = Verilated::dpiScope();\n"; // If dpiScope is fails and is null; the exportFind function throws and error diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index 4f6a7df98..bcbd89506 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -46,6 +46,7 @@ #include "V3Stats.h" #include +#include #include //###################################################################### @@ -299,7 +300,7 @@ private: nextp = itp->verticesNextp(); if (TraceActivityVertex* const vtxp = dynamic_cast(itp)) { // Leave in the always vertex for later use. - if (vtxp != m_alwaysVtxp && !vtxp->outBeginp()) { vtxp->unlinkDelete(&m_graph); } + if (vtxp != m_alwaysVtxp && !vtxp->outBeginp()) vtxp->unlinkDelete(&m_graph); } } } @@ -645,7 +646,7 @@ private: } } ifp = new AstIf(flp, condp, nullptr, nullptr); - if (!always) { ifp->branchPred(VBranchPred::BP_UNLIKELY); } + if (!always) ifp->branchPred(VBranchPred::BP_UNLIKELY); subFuncp->addStmtsp(ifp); subStmts += EmitCBaseCounterVisitor(ifp).count(); prevActSet = &actSet; diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index 0ed6572ce..22d0b2cfe 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -295,7 +295,15 @@ private: VL_RESTORER(m_traShowname); VL_RESTORER(m_traValuep); { - m_traShowname += string(" ") + itemp->prettyName(); + // Add @ to mark as struct + // Since it is not a valid symbol for verilog variable names, no + // collision should happen + if (v3Global.opt.traceFormat().fst()) { + m_traShowname += string(" ") + itemp->prettyName(); + } else { + m_traShowname += string("\f ") + itemp->prettyName(); + } + if (VN_IS(nodep, StructDType)) { m_traValuep = new AstSel(nodep->fileline(), m_traValuep->cloneTree(true), diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index 28b96f230..57c49c552 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -365,10 +365,10 @@ private: } else { nodep->unlinkFrBack(); } - if (bodysp) { VL_DO_DANGLING(pushDeletep(bodysp), bodysp); } - if (precondsp) { VL_DO_DANGLING(pushDeletep(precondsp), precondsp); } - if (initp) { VL_DO_DANGLING(pushDeletep(initp), initp); } - if (incp && !incp->backp()) { VL_DO_DANGLING(pushDeletep(incp), incp); } + if (bodysp) VL_DO_DANGLING(pushDeletep(bodysp), bodysp); + if (precondsp) VL_DO_DANGLING(pushDeletep(precondsp), precondsp); + if (initp) VL_DO_DANGLING(pushDeletep(initp), initp); + if (incp && !incp->backp()) VL_DO_DANGLING(pushDeletep(incp), incp); if (debug() >= 9 && newbodysp) newbodysp->dumpTree(cout, "- _new: "); return true; } @@ -385,7 +385,7 @@ private: // Grab initial value AstNode* initp = nullptr; // Should be statement before the while. if (nodep->backp()->nextp() == nodep) initp = nodep->backp(); - if (initp) { VL_DO_DANGLING(V3Const::constifyEdit(initp), initp); } + if (initp) VL_DO_DANGLING(V3Const::constifyEdit(initp), initp); if (nodep->backp()->nextp() == nodep) initp = nodep->backp(); // Grab assignment AstNode* incp = nullptr; // Should be last statement diff --git a/src/V3Width.cpp b/src/V3Width.cpp index e84c0f08a..f9a8fbe6c 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -804,8 +804,8 @@ private: } // We're extracting, so just make sure the expression is at least wide enough. if (nodep->fromp()->width() < width) { - nodep->v3error("Extracting " << width << " bits from only " - << nodep->fromp()->width() << " bit number"); + nodep->v3warn(SELRANGE, "Extracting " << width << " bits from only " + << nodep->fromp()->width() << " bit number"); // Extend it. AstNodeDType* subDTypep = nodep->findLogicDType(width, width, nodep->fromp()->dtypep()->numeric()); @@ -2669,7 +2669,7 @@ private: newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "r_" + nodep->name(), withp); newp->dtypeFrom(adtypep->subDTypep()); - if (!nodep->firstAbovep()) { newp->makeStatement(); } + if (!nodep->firstAbovep()) newp->makeStatement(); } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique" || nodep->name() == "unique_index") { methodOkArguments(nodep, 0, 0); @@ -2762,7 +2762,7 @@ private: newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "r_" + nodep->name(), withp); newp->dtypeFrom(adtypep->subDTypep()); - if (!nodep->firstAbovep()) { newp->makeStatement(); } + if (!nodep->firstAbovep()) newp->makeStatement(); } else if (nodep->name() == "reverse" || nodep->name() == "shuffle" || nodep->name() == "sort" || nodep->name() == "rsort") { AstWith* withp = nullptr; @@ -2876,7 +2876,7 @@ private: newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name(), nullptr); newp->dtypeFrom(adtypep->subDTypep()); - if (!nodep->firstAbovep()) { newp->makeStatement(); } + if (!nodep->firstAbovep()) newp->makeStatement(); } else if (nodep->name() == "push_back" || nodep->name() == "push_front") { methodOkArguments(nodep, 1, 1); methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); @@ -2894,7 +2894,7 @@ private: newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "r_" + nodep->name(), withp); newp->dtypeFrom(adtypep->subDTypep()); - if (!nodep->firstAbovep()) { newp->makeStatement(); } + if (!nodep->firstAbovep()) newp->makeStatement(); } else if (nodep->name() == "reverse" || nodep->name() == "shuffle" || nodep->name() == "sort" || nodep->name() == "rsort") { AstWith* withp = nullptr; @@ -3427,9 +3427,10 @@ private: void patternArray(AstPattern* nodep, AstNodeArrayDType* arrayDtp, AstPatMember* defaultp) { VNumRange range = arrayDtp->declRange(); PatVecMap patmap = patVectorMap(nodep, range); - UINFO(9, "ent " << range.hi() << " to " << range.lo() << endl); + UINFO(9, "ent " << range.left() << " to " << range.right() << endl); AstNode* newp = nullptr; - for (int ent = range.hi(); ent >= range.lo(); --ent) { + for (int entn = 0, ent = range.left(); entn < range.elements(); + ++entn, ent += range.leftToRightInc()) { AstPatMember* newpatp = nullptr; AstPatMember* patp = nullptr; const auto it = patmap.find(ent); @@ -5592,7 +5593,7 @@ private: AstNodeBiop* replaceWithDVersion(AstNodeBiop* nodep) { // Given a signed/unsigned node type, create the opposite type // Return new node or nullptr if nothing - if (nodep->doubleFlavor()) { return nullptr; } + if (nodep->doubleFlavor()) return nullptr; FileLine* fl = nodep->fileline(); AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); diff --git a/src/V3WidthSel.cpp b/src/V3WidthSel.cpp index 3769c1923..665e5d543 100644 --- a/src/V3WidthSel.cpp +++ b/src/V3WidthSel.cpp @@ -313,7 +313,7 @@ private: nodep->replaceWith(fromp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } - if (!rhsp->backp()) { VL_DO_DANGLING(pushDeletep(rhsp), rhsp); } + if (!rhsp->backp()) VL_DO_DANGLING(pushDeletep(rhsp), rhsp); } virtual void visit(AstSelExtract* nodep) override { // Select of a range specified part of an array, i.e. "array[2:3]" @@ -369,10 +369,11 @@ private: lsb = x; } if (lsb > msb) { - nodep->v3error("[" - << msb << ":" << lsb - << "] Range extract has backward bit ordering, perhaps you wanted [" - << lsb << ":" << msb << "]"); + nodep->v3warn( + SELRANGE, + "[" << msb << ":" << lsb + << "] Range extract has backward bit ordering, perhaps you wanted [" << lsb + << ":" << msb << "]"); int x = msb; msb = lsb; lsb = x; @@ -398,10 +399,11 @@ private: lsb = x; } if (lsb > msb) { - nodep->v3error("[" - << msb << ":" << lsb - << "] Range extract has backward bit ordering, perhaps you wanted [" - << lsb << ":" << msb << "]"); + nodep->v3warn( + SELRANGE, + "[" << msb << ":" << lsb + << "] Range extract has backward bit ordering, perhaps you wanted [" << lsb + << ":" << msb << "]"); int x = msb; msb = lsb; lsb = x; @@ -419,10 +421,11 @@ private: } else if (VN_IS(ddtypep, NodeUOrStructDType)) { // Classes aren't little endian if (lsb > msb) { - nodep->v3error("[" - << msb << ":" << lsb - << "] Range extract has backward bit ordering, perhaps you wanted [" - << lsb << ":" << msb << "]"); + nodep->v3warn( + SELRANGE, + "[" << msb << ":" << lsb + << "] Range extract has backward bit ordering, perhaps you wanted [" << lsb + << ":" << msb << "]"); int x = msb; msb = lsb; lsb = x; @@ -455,9 +458,9 @@ private: VL_DO_DANGLING(pushDeletep(nodep), nodep); } // delete whatever we didn't use in reconstruction - if (!fromp->backp()) { VL_DO_DANGLING(pushDeletep(fromp), fromp); } - if (!msbp->backp()) { VL_DO_DANGLING(pushDeletep(msbp), msbp); } - if (!lsbp->backp()) { VL_DO_DANGLING(pushDeletep(lsbp), lsbp); } + if (!fromp->backp()) VL_DO_DANGLING(pushDeletep(fromp), fromp); + if (!msbp->backp()) VL_DO_DANGLING(pushDeletep(msbp), msbp); + if (!lsbp->backp()) VL_DO_DANGLING(pushDeletep(lsbp), lsbp); } void replaceSelPlusMinus(AstNodePreSel* nodep) { @@ -560,9 +563,9 @@ private: VL_DO_DANGLING(pushDeletep(nodep), nodep); } // delete whatever we didn't use in reconstruction - if (!fromp->backp()) { VL_DO_DANGLING(pushDeletep(fromp), fromp); } - if (!rhsp->backp()) { VL_DO_DANGLING(pushDeletep(rhsp), rhsp); } - if (!widthp->backp()) { VL_DO_DANGLING(pushDeletep(widthp), widthp); } + if (!fromp->backp()) VL_DO_DANGLING(pushDeletep(fromp), fromp); + if (!rhsp->backp()) VL_DO_DANGLING(pushDeletep(rhsp), rhsp); + if (!widthp->backp()) VL_DO_DANGLING(pushDeletep(widthp), widthp); } virtual void visit(AstSelPlus* nodep) override { replaceSelPlusMinus(nodep); } virtual void visit(AstSelMinus* nodep) override { replaceSelPlusMinus(nodep); } diff --git a/src/VlcTop.cpp b/src/VlcTop.cpp index b4969ed53..021e875c4 100644 --- a/src/VlcTop.cpp +++ b/src/VlcTop.cpp @@ -219,7 +219,7 @@ void VlcTop::annotateCalc() { for (int lni = start; start && lni <= end; ++lni) { source.incCount(lni, point.column(), point.count(), ok); } - if (!*covp) { break; } + if (!*covp) break; start = 0; // Prep for next end = 0; range = false; diff --git a/src/astgen b/src/astgen old mode 100644 new mode 100755 index c69a40417..98e108f03 --- a/src/astgen +++ b/src/astgen @@ -1,790 +1,720 @@ -#!/usr/bin/env perl -# See copyright, etc in below POD section. +#!/usr/bin/env python3 ###################################################################### -use warnings; -use Getopt::Long; -use IO::File; -use Pod::Usage; -use strict; -use vars qw($Debug @Types %Classes %Children %ClassRefs %Stages); +import argparse +import glob +import re +import sys +#from pprint import pprint, pformat -#====================================================================== -# main +Types = [] +Classes = {} +Children = {} +ClassRefs = {} +Stages = {} -$Debug = 0; -my $opt_classes; -my $opt_report; -my @Opt_Cpt; -my @Opt_I; -Getopt::Long::config("pass_through", "no_auto_abbrev"); -if (! GetOptions( - "help" => \&usage, - "debug" => sub { $Debug = 1; }, - "classes!" => \$opt_classes, - "report!" => \$opt_report, - "<>" => \¶meter, - )) { - usage(); -} -read_types("$Opt_I[0]/V3Ast.h"); -read_types("$Opt_I[0]/V3AstNodes.h"); -foreach my $type (sort (keys %Classes)) { - # Check all leaves are not AstNode* and non-leaves are AstNode* - my @children = children_of($type); - if ($type =~ /^Node/) { - @children || die "Error: Final AstNode subclasses must not be named AstNode*: Ast$type" - } else { - !@children || die "Error: Non-final AstNode subclasses must be named AstNode*: Ast$type" - } -} -read_stages("$Opt_I[0]/Verilator.cpp"); -read_refs(glob("$Opt_I[0]/*.y"), glob("$Opt_I[0]/*.h"), glob("$Opt_I[0]/*.cpp")); -if ($opt_report) { - write_report(undef); -} -if ($opt_classes) { - write_report("V3Ast__gen_report.txt"); - write_classes("V3Ast__gen_classes.h"); - write_visitor("V3Ast__gen_visitor.h"); - write_impl("V3Ast__gen_impl.h"); - write_types("V3Ast__gen_types.h"); - write_header("V3AstNodes__gen.h"); -} -foreach my $cpt (@Opt_Cpt) { - Cpt::process(in_filename=>"$Opt_I[0]/${cpt}.cpp", out_filename=>"${cpt}__gen.cpp"); -} +class Cpt: + def __init__(self): + self.did_out_tree = False + self.out_lines = [] + self.out_linenum = 1 + self.treeop = {} + self.tree_skip_visit = {} + self._exec_nsyms = 0 + self._exec_syms = {} -#---------------------------------------------------------------------- + def error(self, txt): + sys.exit("%%Error: %s:%d: %s" % + (self.in_filename, self.in_linenum, txt)) -sub usage { - pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT); - exit(1); # Unreachable -} + def print(self, txt): + self.out_lines.append(txt) -sub parameter { - my $param = shift; - if ($param =~ /^-+I(\S+)/) { - push @Opt_I, $1; - } elsif ($param =~ s/\.cpp$//) { - push @Opt_Cpt, $param; - } else { - die "%Error: Unknown parameter: $param,"; - } -} + def output_func(self, func): + self.out_lines.append(func) -####################################################################### + def _output_line(self): + self.print("#line " + str(self.out_linenum + 2) + " \"" + + self.out_filename + "\"\n") -sub read_types { - my $filename = shift; + def process(self, in_filename, out_filename): + self.in_filename = in_filename + self.out_filename = out_filename + ln = 0 + didln = False - my $fh = IO::File->new($filename) or die "%Error: $! $filename,"; - while (defined (my $line = $fh->getline())) { - $line =~ s/\/\/.*$//; - next if $line =~ /^\s*$/; - if ($line =~ /^\s*(class|struct)\s*(\S+)/) { - my $class = $2; - my $inh = ""; - $inh = $1 if ($line =~ /:\s*public\s+(\S+)/); - print "class $class : $inh\n" if $Debug; - $inh = "" if $class eq "AstNode"; - if ($inh =~ /Ast/ || $class eq "AstNode") { - $class =~ s/^Ast//; - $inh =~ s/^Ast//; - $Classes{$class} = $inh; - $Children{$inh}{$class} = 1; + # Read the file and parse into list of functions that generate output + with open(self.in_filename) as fhi: + for line in fhi: + ln += 1 + if not didln: + self.print("#line " + str(ln) + " \"" + self.in_filename + + "\"\n") + didln = True + match = re.match(r'^\s+(TREE.*)$', line) + if match: + func = match.group(1) + self.in_linenum = ln + self.print("//" + line) + self.output_func(lambda self: self._output_line()) + self.tree_line(func) + didln = False + elif not re.match(r'^\s*/[/\*]\s*TREE', line) and re.search( + r'\s+TREE', line): + self.error("Unknown astgen line: " + line) + else: + self.print(line) + + # Put out the resultant file, if the list has a reference to a + # function, then call that func to generate output + with open_file(self.out_filename) as fho: + togen = self.out_lines + for line in togen: + if type(line) is str: + self.out_lines = [line] + else: + self.out_lines = [] + line(self) # lambda call + for out in self.out_lines: + for _ in re.findall(r'\n', out): + self.out_linenum += 1 + fho.write(out) + + def tree_line(self, func): + func = re.sub(r'\s*//.*$', '', func) + func = re.sub(r'\s*;\s*$', '', func) + + # doflag "S" indicates an op specifying short-circuiting for a type. + match = re.search( + # 1 2 3 4 + r'TREEOP(1?)([ACSV]?)\s*\(\s*\"([^\"]*)\"\s*,\s*\"([^\"]*)\"\s*\)', + func) + match_skip = re.search(r'TREE_SKIP_VISIT\s*\(\s*\"([^\"]*)\"\s*\)', + func) + + if match: + order = match.group(1) + doflag = match.group(2) + fromn = match.group(3) + to = match.group(4) + #self.print("// $fromn $to\n") + if not self.did_out_tree: + self.did_out_tree = True + self.output_func(lambda self: self.tree_match_base()) + match = re.search(r'Ast([a-zA-Z0-9]+)\s*\{(.*)\}\s*$', fromn) + if not match: + self.error("Can't parse from function: " + func) + typen = match.group(1) + subnodes = match.group(2) + if not subclasses_of(typen): + self.error("Unknown AstNode typen: " + typen + ": in " + func) + + mif = "" + if doflag == '': + mif = "m_doNConst" + elif doflag == 'A': + mif = "" + elif doflag == 'C': + mif = "m_doCpp" + elif doflag == 'S': + mif = "m_doNConst" # Not just for m_doGenerate + elif doflag == 'V': + mif = "m_doV" + else: + self.error("Unknown flag: " + doflag) + subnodes = re.sub(r',,', '__ESCAPEDCOMMA__', subnodes) + for subnode in re.split(r'\s*,\s*', subnodes): + subnode = re.sub(r'__ESCAPEDCOMMA__', ',', subnode) + if re.match(r'^\$([a-zA-Z0-9]+)$', subnode): + continue # "$lhs" is just a comment that this op has a lhs + subnodeif = subnode + subnodeif = re.sub( + r'\$([a-zA-Z0-9]+)\.cast([A-Z][A-Za-z0-9]+)$', + r'VN_IS(nodep->\1(),\2)', subnodeif) + subnodeif = re.sub(r'\$([a-zA-Z0-9]+)\.([a-zA-Z0-9]+)$', + r'nodep->\1()->\2()', subnodeif) + subnodeif = self.add_nodep(subnodeif) + if mif != "" and subnodeif != "": + mif += " && " + mif += subnodeif + + exec_func = self.treeop_exec_func(to) + exec_func = re.sub( + r'([-()a-zA-Z0-9_>]+)->cast([A-Z][A-Za-z0-9]+)\(\)', + r'VN_CAST(\1,\2)', exec_func) + + if typen not in self.treeop: + self.treeop[typen] = [] + n = len(self.treeop[typen]) + typefunc = { + 'order': order, + 'comment': func, + 'match_func': "match_" + typen + "_" + str(n), + 'match_if': mif, + 'exec_func': exec_func, + 'uinfo': re.sub(r'[ \t\"\{\}]+', ' ', func), + 'uinfo_level': (0 if re.match(r'^!', to) else 7), + 'short_circuit': (doflag == 'S'), } - } - } -} + self.treeop[typen].append(typefunc) -sub read_stages { - my $filename = shift; + elif match_skip: + typen = match_skip.group(1) + self.tree_skip_visit[typen] = 1 + if typen not in Classes: + self.error("Unknown node type: " + typen) - my $fh = IO::File->new($filename) or die "%Error: $! $filename,"; - my $n = 0; - while (defined (my $line = $fh->getline())) { - $line =~ s/\/\/.*$//; - next if $line =~ /^\s*$/; - if ($line =~ /^\s*([A-Za-z0-9]+)::/) { - my $stage = $1.".cpp"; - if (!defined ($Stages{$stage})) { - $Stages{$stage} = $n++; - } - } - } -} + else: + self.error("Unknown astgen op: " + func) -sub read_refs { - my @filenames = @_; + @staticmethod + def add_nodep(str): + str = re.sub(r'\$([a-zA-Z0-9]+)', r'nodep->\1()', str) + return str - foreach my $filename (@filenames) { - (my $basename = $filename) =~ s!.*/!!; - my $fh = IO::File->new($filename) or die "%Error: $! $filename,"; - while (defined (my $line = $fh->getline())) { - $line =~ s/\/\/.*$//; - while ($line =~ /\bnew\s*(Ast[A-Za-z0-9_]+)/g) { - $ClassRefs{$1}{newed}{$basename} = 1; - } - while ($line =~ /\b(Ast[A-Za-z0-9_]+)/g) { - $ClassRefs{$1}{used}{$basename} = 1; - } - } - } - #use Data::Dumper;print Dumper(\%ClassRefs); -} + def _exec_syms_recurse(self, aref): + for sym in aref: + if type(sym) is list: + self._exec_syms_recurse(sym) + elif re.search(r'^\$.*', sym): + if sym not in self._exec_syms: + self._exec_nsyms += 1 + self._exec_syms[sym] = "arg" + str(self._exec_nsyms) + "p" -#---------------------------------------------------------------------- + def _exec_new_recurse(self, aref): + out = "new " + aref[0] + "(nodep->fileline()" + first = True + for sym in aref: + if first: + first = False + continue + out += ", " + if type(sym) is list: + out += self._exec_new_recurse(sym) + elif re.match(r'^\$.*', sym): + out += self._exec_syms[sym] + else: + out += sym + return out + ")" -sub open_file { - my $filename = shift; - my $fh = IO::File->new($filename,"w") or die "%Error: $! $filename,"; - if ($filename =~ /\.txt$/) { - print $fh '// Generated by astgen'."\n"; - } else { - print $fh '// Generated by astgen // -*- mode: C++; c-file-style: "cc-mode" -*-'."\n"; - } - return $fh; -} + def treeop_exec_func(self, func): + out = "" + func = re.sub(r'^!', '', func) -#---------------------------------------------------------------------- + if re.match(r'^\s*[a-zA-Z0-9]+\s*\(', func): # Function call + outl = re.sub(r'\$([a-zA-Z0-9]+)', r'nodep->\1()', func) + out += outl + ";" + elif re.match(r'^\s*Ast([a-zA-Z0-9]+)\s*\{\s*(.*)\s*\}$', func): + nargs = 0 + argnums = [] # Number for each argument name + aref = None + # Recursive array with structure to form + astack = [] + forming = "" + argtext = func + "\000" # EOF character + for tok in argtext: + if tok == "\000": + None + elif re.match(r'\s+', tok): + None + elif tok == "{": + newref = [forming] + if not aref: + aref = [] + aref.append(newref) + astack.append(aref) + aref = newref + forming = "" + elif tok == "}": + if forming: + aref.append(forming) + if len(astack) == 0: + self.error("Too many } in execution function: " + func) + aref = astack.pop() + forming = "" + elif tok == ",": + if forming: + aref.append(forming) + forming = "" + else: + forming += tok + if not (aref and len(aref) == 1): + self.error("Badly formed execution function: " + func) + aref = aref[0] -sub subclasses_of { - my $type = shift; + # Assign numbers to each $ symbol + self._exec_syms = {} + self._exec_nsyms = 0 + self._exec_syms_recurse(aref) - my @cllist; - for (my $subclass = $::Classes{$type}; $subclass; ) { - push @cllist, $subclass; - $subclass = $::Classes{$subclass}; - } + for sym in sorted(self._exec_syms.keys(), + key=lambda val: self._exec_syms[val]): + argnp = self._exec_syms[sym] + arg = self.add_nodep(sym) + out += "AstNode* " + argnp + " = " + arg + "->unlinkFrBack();\n" - return (reverse @cllist); -} + out += "AstNode* newp = " + self._exec_new_recurse(aref) + ";\n" + out += "nodep->replaceWith(newp);" + out += "VL_DO_DANGLING(nodep->deleteTree(), nodep);" + elif func == "NEVER": + out += "nodep->v3fatalSrc(\"Executing transform that was NEVERed\");" + elif func == "DONE": + None + else: + self.error("Unknown execution function format: " + func + "\n") + return out -sub children_of { - my $type = shift; + def tree_match_base(self): + self.tree_match() + self.tree_base() - my @cllist; - my @todo; - push @todo, $type; - while (my $subclass = shift @todo) { - foreach my $child (sort keys %{$::Children{$subclass}}) { - push @todo, $child; - push @cllist, $child; - } - } + def tree_match(self): + self.print( + " // TREEOP functions, each return true if they matched & transformed\n" + ) + for base in sorted(self.treeop.keys()): + for typefunc in self.treeop[base]: + self.print(" // Generated by astgen\n") + self.print(" bool " + typefunc['match_func'] + "(Ast" + + base + "* nodep) {\n") + self.print("\t// " + typefunc['comment'] + "\n") + self.print("\tif (" + typefunc['match_if'] + ") {\n") + self.print("\t UINFO(" + str(typefunc['uinfo_level']) + + ",cvtToHex(nodep)" + "<<\" " + typefunc['uinfo'] + + "\\n\");\n") + self.print("\t " + typefunc['exec_func'] + "\n") + self.print("\t return true;\n") + self.print("\t}\n") + self.print("\treturn false;\n") + self.print(" }\n", ) - return (@cllist); -} + def tree_base(self): + self.print(" // TREEOP visitors, call each base type's match\n") + self.print( + " // Bottom class up, as more simple transforms are generally better\n" + ) + for typen in sorted(Classes.keys()): + out_for_type_sc = [] + out_for_type = [] + bases = subclasses_of(typen) + bases.append(typen) + for base in bases: + if not base in self.treeop: + continue + for typefunc in self.treeop[base]: + lines = [ + " if (" + typefunc['match_func'] + + "(nodep)) return;\n" + ] + if (typefunc['short_circuit']): # short-circuit match fn + out_for_type_sc.extend(lines) + else: # Standard match fn + if typefunc[ + 'order']: # TREEOP1's go in front of others + out_for_type = lines + out_for_type + else: + out_for_type.extend(lines) -#---------------------------------------------------------------------- + # We need to deal with two cases. For short circuited functions we + # evaluate the LHS, then apply the short-circuit matches, then + # evaluate the RHS and possibly THS (ternary operators may + # short-circuit) and apply all the other matches. -sub write_report { - my $filename = shift; - my $fh = defined($filename) ? open_file($filename) : \*STDOUT; + # For types without short-circuits, we just use iterateChildren, which + # saves one comparison. + if len(out_for_type_sc) > 0: # Short-circuited types + self.print( + " // Generated by astgen with short-circuiting\n" + + " virtual void visit(Ast" + typen + + "* nodep) override {\n" + + " iterateAndNextNull(nodep->lhsp());\n" + + "".join(out_for_type_sc)) + if out_for_type[0]: + self.print( + " iterateAndNextNull(nodep->rhsp());\n" + + " AstNodeTriop *tnp = VN_CAST(nodep, NodeTriop);\n" + + + " if (tnp && tnp->thsp()) iterateAndNextNull(tnp->thsp());\n" + + "".join(out_for_type) + " }\n") + elif len(out_for_type) > 0: # Other types with something to print + skip = typen in self.tree_skip_visit + gen = "Gen" if skip else "" + override = "" if skip else " override" + self.print( + " // Generated by astgen\n" + " virtual void visit" + + gen + "(Ast" + typen + "* nodep)" + override + " {\n" + + ("" if skip else " iterateChildren(nodep);\n") + + ''.join(out_for_type) + " }\n") - $fh->print("Processing stages (approximate, based on order in Verilator.cpp):\n"); - foreach my $class (sort {$Stages{$a} <=> $Stages{$b}} keys %Stages) { - $fh->print(" $class\n"); - } - $fh->print("\nClasses:\n"); - foreach my $type (sort (keys %Classes)) { - printf $fh " class %-20s\n", "Ast${type}"; - $fh->print(" parent: "); - foreach my $subclass (subclasses_of($type)) { - next if $subclass eq 'Node'; - printf $fh "Ast%-12s ",$subclass; - } - printf $fh "\n"; - $fh->print(" childs: "); - foreach my $subclass (children_of($type)) { - next if $subclass eq 'Node'; - printf $fh "Ast%-12s ",$subclass; - } - printf $fh "\n"; - if (my $refs = $ClassRefs{"Ast${type}"}) { - $fh->print(" newed: "); - foreach my $stage (sort {($Stages{$a}||-1) <=> ($Stages{$b}||-1)} - keys %{$refs->{newed}}) { - $fh->print($stage." "); - } - $fh->print("\n"); - $fh->print(" used: "); - foreach my $stage (sort {($Stages{$a}||-1) <=> ($Stages{$b}||-1)} - keys %{$refs->{used}}) { - $fh->print($stage." "); - } - $fh->print("\n"); - } - $fh->print("\n"); - } -} +###################################################################### +###################################################################### -sub write_classes { - my $fh = open_file(@_); - printf $fh "class AstNode;\n"; - foreach my $type (sort (keys %Classes)) { - printf $fh "class %-20s // ", "Ast${type};"; - foreach my $subclass (subclasses_of($type)) { - printf $fh "Ast%-12s ",$subclass; - } - printf $fh "\n"; - } - $fh->close(); -} -sub write_visitor { - my $fh = open_file(@_); - foreach my $type (sort (keys %Classes)) { - my $base = $Classes{$type}; - if ($base) { - printf $fh " virtual void visit(Ast${type}* nodep) { visit((Ast${base}*)(nodep)); }\n"; - } else { - printf $fh " virtual void visit(Ast${type}*) = 0;\n"; - } - } - $fh->close(); -} +def read_types(filename): + with open(filename) as fh: + for line in fh: + line = re.sub(r'//.*$', '', line) + if re.match(r'^\s*$', line): + continue + match = re.search(r'^\s*(class|struct)\s*(\S+)', line) + if match: + classn = match.group(2) + inh = "" + match = re.search(r':\s*public\s+(\S+)', line) + if match: + inh = match.group(1) + #print("class "+classn+" : "+inh) + if classn == "AstNode": + inh = "" + if re.search(r'Ast', inh) or classn == "AstNode": + classn = re.sub(r'^Ast', '', classn) + inh = re.sub(r'^Ast', '', inh) + Classes[classn] = inh + if inh != '': + if inh not in Children: + Children[inh] = {} + Children[inh][classn] = 1 -sub write_impl { - my $fh = open_file(@_); - print $fh "\n"; +def read_stages(filename): + with open(filename) as fh: + n = 100 + for line in fh: + line = re.sub(r'//.*$', '', line) + if re.match(r'^\s*$', line): + continue + match = re.match(r'^\s*([A-Za-z0-9]+)::', line) + if match: + stage = match.group(1) + ".cpp" + if stage not in Stages: + Stages[stage] = n + n += 1 - print $fh " // These for use by VN_IS only\n"; - foreach my $type (sort (keys %Classes)) { - print $fh "template<> inline bool AstNode::privateIs(const AstNode* nodep) { "; - if ($type eq "Node") { - print $fh "return nodep != NULL; "; - } else { - print $fh "return nodep && "; - if ($type =~ /^Node/) { - print $fh "(static_cast(nodep->type()) >= static_cast(AstType::first",$type,")) && "; - print $fh "(static_cast(nodep->type()) <= static_cast(AstType::last",$type,")); "; - } else { - print $fh "(static_cast(nodep->type()) == static_cast(AstType::at",$type,")); "; - } - } - print $fh "}\n" - } - print $fh " // These for use by VN_CAST macro only\n"; - foreach my $type (sort (keys %Classes)) { - print $fh "template<> inline Ast",$type,"* AstNode::privateCast(AstNode* nodep) { "; - if ($type eq "Node") { - print $fh "return nodep; "; - } else { - print $fh "return AstNode::privateIs(nodep) ? "; - print $fh "reinterpret_cast(nodep) : NULL; "; - } - print $fh "}\n"; - } +def read_refs(filename): + basename = re.sub(r'.*/', '', filename) + with open(filename) as fh: + for line in fh: + line = re.sub(r'//.*$', '', line) + for match in re.finditer(r'\bnew\s*(Ast[A-Za-z0-9_]+)', line): + ref = match.group(1) + if ref not in ClassRefs: + ClassRefs[ref] = {'newed': {}, 'used': {}} + ClassRefs[ref]['newed'][basename] = 1 + for match in re.finditer(r'\b(Ast[A-Za-z0-9_]+)', line): + ref = match.group(1) + if ref not in ClassRefs: + ClassRefs[ref] = {'newed': {}, 'used': {}} + ClassRefs[ref]['used'][basename] = 1 - print $fh " // These for use by VN_CAST_CONST macro only\n"; - foreach my $type (sort (keys %Classes)) { - print $fh "template<> inline const Ast",$type,"* AstNode::privateConstCast(const AstNode* nodep) { "; - if ($type eq "Node") { - print $fh "return nodep; "; - } else { - print $fh "return AstNode::privateIs(nodep) ? "; - print $fh "reinterpret_cast(nodep) : NULL; "; - } - print $fh "}\n"; - } - $fh->close(); -} +def open_file(filename): + fh = open(filename, "w") + if re.search(r'\.txt$', filename): + fh.write("// Generated by astgen\n") + else: + fh.write( + '// Generated by astgen // -*- mode: C++; c-file-style: "cc-mode" -*-' + + "\n") + return fh -sub write_type_enum { - my $fh = shift; - my $type = shift; - my $idx = shift; - my $processed = shift; - my $kind = shift; - my $indent = shift; +def subclasses_of(typen): + cllist = [] + subclass = Classes[typen] + while True: + if not subclass in Classes: + break + cllist.append(subclass) + subclass = Classes[subclass] + + cllist.reverse() + return cllist + + +def children_of(typen): + cllist = [] + todo = [] + todo.append(typen) + while len(todo) != 0: + subclass = todo.pop(0) + if subclass in Children: + for child in sorted(Children[subclass].keys()): + todo.append(child) + cllist.append(child) + + return cllist + + +#--------------------------------------------------------------------- + + +def write_report(filename): + with open_file(filename) as fh: + + fh.write( + "Processing stages (approximate, based on order in Verilator.cpp):\n" + ) + for classn in sorted(Stages.keys(), key=lambda val: Stages[val]): + fh.write(" " + classn + "\n") + + fh.write("\nClasses:\n") + for typen in sorted(Classes.keys()): + fh.write(" class Ast%-17s\n" % typen) + fh.write(" parent: ") + for subclass in subclasses_of(typen): + if subclass != 'Node': + fh.write("Ast%-12s " % subclass) + fh.write("\n") + fh.write(" childs: ") + for subclass in children_of(typen): + if subclass != 'Node': + fh.write("Ast%-12s " % subclass) + fh.write("\n") + if ("Ast" + typen) in ClassRefs: + refs = ClassRefs["Ast" + typen] + fh.write(" newed: ") + for stage in sorted(refs['newed'].keys(), + key=lambda val: Stages[val] + if (val in Stages) else -1): + fh.write(stage + " ") + fh.write("\n") + fh.write(" used: ") + for stage in sorted(refs['used'].keys(), + key=lambda val: Stages[val] + if (val in Stages) else -1): + fh.write(stage + " ") + fh.write("\n") + fh.write("\n") + + +def write_classes(filename): + with open_file(filename) as fh: + fh.write("class AstNode;\n") + for typen in sorted(Classes.keys()): + fh.write("class Ast%-17s // " % (typen + ";")) + for subclass in subclasses_of(typen): + fh.write("Ast%-12s " % subclass) + fh.write("\n") + + +def write_visitor(filename): + with open_file(filename) as fh: + for typen in sorted(Classes.keys()): + if typen == "Node": + fh.write(" virtual void visit(Ast" + typen + "*) = 0;\n") + else: + base = Classes[typen] + fh.write(" virtual void visit(Ast" + typen + + "* nodep) { visit((Ast" + base + "*)(nodep)); }\n") + + +def write_impl(filename): + with open_file(filename) as fh: + fh.write("\n") + fh.write(" // These for use by VN_IS only\n") + for typen in sorted(Classes.keys()): + fh.write("template<> inline bool AstNode::privateIs(const AstNode* nodep) { ") + if typen == "Node": + fh.write("return nodep != NULL; ") + else: + fh.write("return nodep && ") + if re.search(r'^Node', typen): + fh.write( + "(static_cast(nodep->type()) >= static_cast(AstType::first" + + typen + ")) && ") + fh.write( + "(static_cast(nodep->type()) <= static_cast(AstType::last" + + typen + ")); ") + else: + fh.write( + "(static_cast(nodep->type()) == static_cast(AstType::at" + + typen + ")); ") + fh.write("}\n") + + fh.write(" // These for use by VN_CAST macro only\n") + for typen in sorted(Classes.keys()): + fh.write("template<> inline Ast" + typen + + "* AstNode::privateCast(AstNode* nodep) { ") + if typen == "Node": + fh.write("return nodep; ") + else: + fh.write("return AstNode::privateIs(nodep) ? ") + fh.write("reinterpret_cast(nodep) : NULL; ") + fh.write("}\n") + + fh.write(" // These for use by VN_CAST_CONST macro only\n") + for typen in sorted(Classes.keys()): + fh.write("template<> inline const Ast" + typen + + "* AstNode::privateConstCast(const AstNode* nodep) { ") + if typen == "Node": + fh.write("return nodep; ") + else: + fh.write("return AstNode::privateIs(nodep) ? ") + fh.write("reinterpret_cast(nodep) : NULL; ") + fh.write("}\n") + + +def write_type_enum(fh, typen, idx, processed, kind, indent): # Skip this if it has already been processed - return $idx if (exists $processed->{$type}); - + if typen in processed: + return idx # Mark processed - $processed->{$type} = 1; + processed[typen] = 1 # The last used index - my $last; - - if ($type !~ /^Node/) { - $last = $idx; - if ($kind eq "concrete-enum") { - print $fh " "x($indent*4), "at",$type," = ",$idx,",\n"; - } elsif ($kind eq "concrete-ascii") { - print $fh " "x($indent*4), "\"", uc $type, "\",\n"; - } - $idx += 1; - } elsif ($kind eq "abstract-enum") { - print $fh " "x($indent*4), "first",$type," = ",$idx,",\n"; - } - - foreach my $child (sort keys %{$::Children{$type}}) { - ($idx, $last) = write_type_enum($fh, $child, $idx, $processed, $kind, $indent); - } - - if ($type =~ /^Node/ && ($kind eq "abstract-enum")) { - print $fh " "x($indent*4), "last",$type," = ",$last,",\n"; - } - - return $idx, $last; -} - -sub write_types { - my $fh = open_file(@_); - - printf $fh " enum en : uint16_t {\n"; - (my $final, undef) = write_type_enum($fh, "Node", 0, {}, "concrete-enum", 2); - printf $fh " _ENUM_END = $final\n"; - printf $fh " };\n"; - - printf $fh " enum bounds : uint16_t {\n"; - write_type_enum($fh, "Node", 0, {}, "abstract-enum", 2); - printf $fh " _BOUNDS_END\n"; - printf $fh " };\n"; - - printf $fh " const char* ascii() const {\n"; - printf $fh " static const char* const names[_ENUM_END + 1] = {\n"; - write_type_enum($fh, "Node", 0, {}, "concrete-ascii", 3); - printf $fh " \"_ENUM_END\"\n"; - printf $fh " };\n"; - printf $fh " return names[m_e];\n"; - printf $fh " }\n"; - $fh->close(); -} - -sub write_header { - my $fh = open_file(@_); - - my $type = "None"; - my $base = "None"; - - my $in_filename = "V3AstNodes.h"; - my $ifile = "$Opt_I[0]/$in_filename"; - my $ifh = IO::File->new($ifile) or die "%Error: $! $ifile,"; - - $fh->print("#line 1 \"../$in_filename\"\n"); - - while (defined (my $line = $ifh->getline())) { - # Drop expanded macro definitions - but keep empty line so compiler - # message locations are accurate - $line =~ s/^\s*#(define|undef)\s+ASTGEN_.*$//; - - # Track current node type and base class - if ($line =~ /^\s*class\s*Ast(\S+)\s*(final|VL_NOT_FINAL)?\s*:\s*(public)?\s*(AstNode\S*)/) { - $type = $1; - $base = $4; - } - - # Substitute macros - $line =~ s/\bASTGEN_SUPER\s*\(/$base(AstType::at$type, /; - - # Emit the line - print $fh $line; - } - - $ifh->close(); - $fh->close(); -} - -####################################################################### - -package Cpt; - -sub error { - my $self = shift; - my $txt = join('', @_); - die "%Error: $self->{in_filename}:$self->{in_linenum}: $txt\n"; -} - -sub print { - my $self = shift; - my $txt = join('', @_); - push @{$self->{out_lines}}, $txt; -} - -sub output_func { - my $self = shift; - my $func = shift; - push @{$self->{out_lines}}, $func; -} - -sub _output_line { - my $self = shift; - $self->print("#line ",$self->{out_linenum}+2," \"$self->{out_filename}\"\n"); -} - -sub process { - my $self = { - in_filename => undef, - out_filename => undef, - out_lines => [], - out_linenum => 1, - @_, - }; - bless $self, __PACKAGE__; - - my $ln = 1; - my $didln; - - # Read the file and parse into list of functions that generate output - my $fhi = IO::File->new($self->{in_filename}) or die "%Error: $! $self->{in_filename},"; - while (defined(my $line = $fhi->getline)) { - if (!$didln) { - $self->print("#line $. \"$self->{in_filename}\"\n"); - $didln = 1; - } - if ($line =~ /^\s+(TREE.*)$/) { - my $func = $1; - $self->{in_linenum} = $.; - $self->print("//$line"); - $self->output_func(sub{my $self=shift; $self->_output_line(); }); - $self->tree_line($func); - $didln = 0; - } - elsif ($line !~ /^\s*\/[\/\*]\s*TREE/ - && $line =~ /\s+TREE/) { - $self->error("Unknown astgen line: $line"); - } - else { - $self->print($line); - } - } - $fhi->close; - - # Put out the resultant file, if the list has a reference to a - # function, then call that func to generate output - my $fho = ::open_file($self->{out_filename}); - my @togen = @{$self->{out_lines}}; - foreach my $line (@togen) { - if (ref $line) { - $self->{out_lines} = []; - &$line($self); - } else { - $self->{out_lines} = [$line]; - } - foreach my $out (@{$self->{out_lines}}) { - $self->{out_linenum}++ while ($out =~ /\n/smg); - print $fho $out; - } - } - $fho->close; -} - -sub tree_line { - my $self = shift; - my $func = shift; - - $func =~ s!\s*//.*$!!; - $func =~ s!\s*;\s*$!!; - - # doflag "S" indicates an op specifying short-circuiting for a type. - if ($func =~ /TREEOP(1?)([VCS]?)\s*\(\s* \"([^\"]*)\" \s*,\s* \"([^\"]*)\" \s*\)/sx) { - my $order = $1; my $doflag = $2; my $from = $3; my $to = $4; - #$self->print("// $from $to\n"); - if (!$self->{did_out_tree}) { - $self->{did_out_tree} = 1; - $self->output_func(sub{ my $self=shift; - $self->tree_match(); - $self->tree_base(); - }); - } - $from =~ /Ast([a-zA-Z0-9]+)\s*\{(.*)\}\s*$/ - or $self->error("Can't parse from function: $func"); - my $type = $1; - my $subnodes = $2; - (::subclasses_of($type)) or $self->error("Unknown AstNode type: $type: in $func"); - - my $mif; - if ($doflag eq '') { $mif = "m_doNConst"; } - elsif ($doflag eq 'V') { $mif = "m_doV"; } - elsif ($doflag eq 'C') { $mif = ""; } - elsif ($doflag eq 'S') { $mif = "m_doNConst"; } # Not just for m_doGenerate - else { die; } - $subnodes =~ s/,,/__ESCAPEDCOMMA__/g; - foreach my $subnode (split /\s*,\s*/, $subnodes) { - $subnode =~ s/__ESCAPEDCOMMA__/,/g; - next if $subnode =~ /^\$([a-z0-9]+)$/gi; # "$lhs" is just a comment that this op has a lhs - $mif .= " && " if $mif; - my $subnodeif = $subnode; - $subnodeif =~ s/\$([a-zA-Z0-9]+)\.cast([A-Z][A-Za-z0-9]+)$/VN_IS(nodep->$1(),$2)/g; - $subnodeif =~ s/\$([a-zA-Z0-9]+)\.([a-zA-Z0-9]+)$/nodep->$1()->$2()/g; - $subnodeif = add_nodep($subnodeif); - $mif .= $subnodeif; - } - - my $exec_func = treeop_exec_func($self, $to); - while ($exec_func =~ s/([-()a-zA-Z0-9_>]+)->cast([A-Z][A-Za-z0-9]+)\(\)/VN_CAST($1,$2)/) {} - - $self->{treeop}{$type} ||= []; - my $n = $#{$self->{treeop}{$type}} + 1; - my $typefunc = { - order => $order, - comment => $func, - match_func => "match_${type}_${n}", - match_if => $mif, - exec_func => $exec_func, - uinfo_level => ($to =~ /^!/ ? 0:7), - short_circuit => ($doflag eq 'S'), - }; - - ($typefunc->{uinfo} = $func) =~ s/[ \t\"\{\}]+/ /g; - push @{$self->{treeop}{$type}}, $typefunc; - } - elsif ($func =~ /TREE_SKIP_VISIT\s*\(\s* \"([^\"]*)\" \s*\)/sx) { - my $type = $1; - $self->{tree_skip_visit}{$type} = 1; - $::Classes{$type} or $self->error("Unknown node type: $type"); - } - else { - $self->error("Unknown astgen op: $func"); - } -} - -sub add_nodep { - my $str = shift; - $str =~ s/\$([a-zA-Z0-9]+)/nodep->$1()/g; - return $str; -} - -our %_Exec_Syms; -our $_Exec_Nsyms; -sub _exec_syms_recurse { - my $aref = shift; - foreach my $sym (@{$aref}) { - if (ref $sym) { _exec_syms_recurse($sym); } - elsif ($sym =~ /^\$.*/) { - if (!defined $_Exec_Syms{$sym}) { - $_Exec_Syms{$sym} = "arg".(++$_Exec_Nsyms)."p"; - } - } - } -} - -sub _exec_new_recurse { - my $aref = shift; - my $out = "new ".$aref->[0]."(nodep->fileline()"; - my $first = 1; - foreach my $sym (@{$aref}) { - if ($first) { $first=0; next; } - $out .= ", "; - if (ref $sym) { $out.=_exec_new_recurse($sym); } - elsif ($sym =~ /^\$.*/) { - $out .= $_Exec_Syms{$sym}; - } else { - $out .= $sym; - } - } - return $out.")"; -} - -sub treeop_exec_func { - my $self = shift; - my $func = shift; - my $out = ""; - $func =~ s/^!//; - if ($func =~ /^\s*[a-zA-Z0-9]+\s*\(/) { # Function call - (my $outl = $func) =~ s/\$([a-zA-Z0-9]+)/nodep->$1()/g; - $out .= $outl.";"; - } - elsif ($func =~ /^\s*Ast([a-zA-Z0-9]+) \s*\{\s* (.*) \s* \}$/x) { - - my $nargs = 0; - my %argnums; # Number for each argument name - - my $aref = undef; # Recursive array with structure to form - my @astack; - my $forming = ""; - my $argtext = $func . "\000"; # EOF character - #print "FF $func\n" if $Debug; - while ($argtext =~ s/^(.)//) { - my $tok = $1; - #print "TOK: $tok $forming\n" if $tok !~ /[a-zA-Z0-9]/; - - if ($tok eq "\000") { - } elsif ($tok =~ /\s+/) { - } elsif ($tok eq "{") { - my $newref = [$forming]; - push @{$aref}, $newref; - push @astack, $aref if $aref; - $aref = $newref; - $forming = ""; - } elsif ($tok eq "}") { - push @{$aref}, $forming if $forming; - $aref = pop @astack; - $aref or $self->error("Too many } in execution function: $func\n"); - $forming = ""; - } elsif ($tok eq ",") { - push @{$aref}, $forming if $forming; - $forming = ""; - } else { - $forming .= $tok; - } - } - ($aref && ref $aref->[0] && !$aref->[1]) or $self->error("Badly formed execution function: $func\n"); - $aref = $aref->[0]; - #use Data::Dumper; print Dumper($aref),"\n"; - - # Assign numbers to each $ symbol - %_Exec_Syms = (); - $_Exec_Nsyms = 0; - _exec_syms_recurse($aref); - - foreach my $sym (sort {$_Exec_Syms{$a} cmp $_Exec_Syms{$b}} (keys %_Exec_Syms)) { - my $argnp = $_Exec_Syms{$sym}; - my $arg = add_nodep($sym); - $out .= "AstNode* ${argnp} = ${arg}->unlinkFrBack();\n"; - } - - $out .= "AstNode* newp = " . _exec_new_recurse($aref).";\n"; - $out .= "nodep->replaceWith(newp);"; - $out .= "VL_DO_DANGLING(nodep->deleteTree(), nodep);"; - #print "FF $out\n" if $Debug; - } elsif ($func eq "NEVER") { - $out .= "nodep->v3fatalSrc(\"Executing transform that was NEVERed\");"; - } elsif ($func eq "DONE") { - } else { - $self->error("Unknown execution function format: $func\n"); - } - return $out; -} - -sub tree_match { - my $self = shift; - $self->print(" // TREEOP functions, each return true if they matched & transformed\n"); - #use Data::Dumper; print Dumper($self); - foreach my $base (sort (keys %{$self->{treeop}})) { - foreach my $typefunc (@{$self->{treeop}{$base}}) { - $self->print(" // Generated by astgen\n"); - $self->print(" bool $typefunc->{match_func}(Ast${base}* nodep) {\n", - "\t// $typefunc->{comment}\n",); - $self->print( "\tif ($typefunc->{match_if}) {\n"); - $self->print( "\t UINFO($typefunc->{uinfo_level},cvtToHex(nodep)" - ."<<\" $typefunc->{uinfo}\\n\");\n"); - $self->print( "\t $typefunc->{exec_func}\n"); - $self->print( "\t return true;\n"); - $self->print( "\t}\n"); - $self->print( "\treturn false;\n"); - $self->print(" }\n",); - } - } -} - -sub tree_base { - my $self = shift; - $self->print(" // TREEOP visitors, call each base type's match\n"); - $self->print(" // Bottom class up, as more simple transforms are generally better\n"); - foreach my $type (sort (keys %::Classes)) { - my $base = $::Classes{$type}; - my @out_for_type_sc; - my @out_for_type; - foreach my $base (::subclasses_of($type), $type) { - foreach my $typefunc (@{$self->{treeop}{$base}}) { - my @lines = (" if ($typefunc->{match_func}(nodep)) return;\n",); - if ($typefunc->{short_circuit}) { # short-circuit match fn - push @out_for_type_sc, @lines; - } else { # Standard match fn - if ($typefunc->{order}) { - unshift @out_for_type, @lines; # TREEOP1's go in front of others - } else { - push @out_for_type, @lines; - } - } - } - } - - # We need to deal with two cases. For short circuited functions we - # evaluate the LHS, then apply the short-circuit matches, then - # evaluate the RHS and possibly THS (ternary operators may - # short-circuit) and apply all the other matches. - - # For types without short-circuits, we just use iterateChildren, which - # saves one comparison. - if ($out_for_type_sc[0]) { # Short-circuited types - $self->print(" // Generated by astgen with short-circuiting\n", - " virtual void visit(Ast${type}* nodep) override {\n", - " iterateAndNextNull(nodep->lhsp());\n", - @out_for_type_sc); - $self->print(" iterateAndNextNull(nodep->rhsp());\n", - " AstNodeTriop *tnp = VN_CAST(nodep, NodeTriop);\n", - " if (tnp && tnp->thsp()) iterateAndNextNull(tnp->thsp());\n", - @out_for_type, - " }\n") if ($out_for_type[0]); - } elsif ($out_for_type[0]) { # Other types with something to print - my $skip = $self->{tree_skip_visit}{$type}; - my $gen = $skip ? "Gen" : ""; - my $override = $skip ? "" : " override"; - $self->print(" // Generated by astgen\n", - " virtual void visit$gen(Ast${type}* nodep)${override} {\n", - ($skip?"": - " iterateChildren(nodep);\n"), - @out_for_type, - " }\n"); - } - } -} - -####################################################################### -package main; -__END__ - -=pod - -=head1 NAME - -astgen - Generate V3Ast headers to reduce C++ code duplication - -=head1 SYNOPSIS - - astgen - -=head1 DESCRIPTION - -Generates several files for Verilator compilations. - -=head1 ARGUMENTS - -=over 4 - -=item --help - -Displays this message and program version and exits. - -=item --classes - -Makes class declaration files. - -=item --report - -Makes a report report. - -=back - -=head1 DISTRIBUTION - -Copyright 2002-2021 by Wilson Snyder. This program is free software; you + last = None + + if not re.match(r'^Node', typen): + last = idx + if kind == "concrete-enum": + fh.write(" " * (indent * 4) + "at" + typen + " = " + str(idx) + + ",\n") + elif kind == "concrete-ascii": + fh.write(" " * (indent * 4) + "\"" + typen.upper() + "\",\n") + idx += 1 + elif kind == "abstract-enum": + fh.write(" " * (indent * 4) + "first" + typen + " = " + str(idx) + + ",\n") + + if typen in Children: + for child in sorted(Children[typen].keys()): + (idx, last) = write_type_enum(fh, child, idx, processed, kind, + indent) + + if re.match(r'^Node', typen) and kind == "abstract-enum": + fh.write(" " * (indent * 4) + "last" + typen + " = " + str(last) + + ",\n") + + return [idx, last] + + +def write_types(filename): + with open_file(filename) as fh: + fh.write(" enum en : uint16_t {\n") + (final, last) = write_type_enum(fh, "Node", 0, {}, "concrete-enum", 2) + fh.write(" _ENUM_END = " + str(final) + "\n") + fh.write(" };\n") + + fh.write(" enum bounds : uint16_t {\n") + write_type_enum(fh, "Node", 0, {}, "abstract-enum", 2) + fh.write(" _BOUNDS_END\n") + fh.write(" };\n") + + fh.write(" const char* ascii() const {\n") + fh.write(" static const char* const names[_ENUM_END + 1] = {\n") + write_type_enum(fh, "Node", 0, {}, "concrete-ascii", 3) + fh.write(" \"_ENUM_END\"\n") + fh.write(" };\n") + fh.write(" return names[m_e];\n") + fh.write(" }\n") + + +def write_header(filename): + with open_file(filename) as fh: + typen = "None" + base = "None" + + in_filename = "V3AstNodes.h" + ifile = Args.I + "/" + in_filename + with open(ifile) as ifh: + + fh.write("#line 1 \"../" + in_filename + "\"\n") + + for line in ifh: + # Drop expanded macro definitions - but keep empty line so compiler + # message locations are accurate + line = re.sub(r'^\s*#(define|undef)\s+ASTGEN_.*$', '', line) + + # Track current node type and base class + match = re.search( + r'\s*class\s*Ast(\S+)\s*(final|VL_NOT_FINAL)?\s*:\s*(public)?\s*(AstNode\S*)', + line) + if match: + typen = match.group(1) + base = match.group(4) + + # Substitute macros + line = re.sub(r'\bASTGEN_SUPER\s*\(', + base + "(AstType::at" + typen + ", ", line) + + # Emit the line + fh.write(line) + + +###################################################################### +# main + +parser = argparse.ArgumentParser( + allow_abbrev=False, + formatter_class=argparse.RawDescriptionHelpFormatter, + description="""Generate V3Ast headers to reduce C++ code duplication.""", + epilog= + """Copyright 2002-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 +SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""") -=head1 AUTHORS +parser.add_argument('-I', action='store', help='source code include directory') +parser.add_argument('--classes', + action='store_true', + help='makes class declaration files') +parser.add_argument('--debug', action='store_true', help='enable debug') -Wilson Snyder +parser.add_argument('infiles', nargs='*', help='list of input .cpp filenames') -=head1 SEE ALSO +Args = parser.parse_args() -=cut +read_types(Args.I + "/V3Ast.h") +read_types(Args.I + "/V3AstNodes.h") +for typen in sorted(Classes.keys()): + # Check all leaves are not AstNode* and non-leaves are AstNode* + children = children_of(typen) + if re.match(r'^Node', typen): + if len(children) == 0: + sys.exit( + "%Error: Final AstNode subclasses must not be named AstNode*: Ast" + + typen) + else: + if len(children) != 0: + sys.exit( + "%Error: Non-final AstNode subclasses must be named AstNode*: Ast" + + typen) + +read_stages(Args.I + "/Verilator.cpp") + +source_files = glob.glob(Args.I + "/*.y") +source_files.extend(glob.glob(Args.I + "/*.h")) +source_files.extend(glob.glob(Args.I + "/*.cpp")) +for filename in source_files: + read_refs(filename) + +if Args.classes: + write_report("V3Ast__gen_report.txt") + write_classes("V3Ast__gen_classes.h") + write_visitor("V3Ast__gen_visitor.h") + write_impl("V3Ast__gen_impl.h") + write_types("V3Ast__gen_types.h") + write_header("V3AstNodes__gen.h") + +for cpt in Args.infiles: + if not re.search(r'.cpp$', cpt): + sys.exit("%Error: Expected argument to be .cpp file: " + cpt) + cpt = re.sub(r'.cpp$', '', cpt) + Cpt().process(in_filename=Args.I + "/" + cpt + ".cpp", + out_filename=cpt + "__gen.cpp") ###################################################################### ### Local Variables: -### compile-command: "./astgen -I. --report" +### compile-command: "cd obj_dbg && ../astgen -I.. V3Const.cpp" ### End: diff --git a/src/bisonpre b/src/bisonpre index f4555c823..2d1c79934 100755 --- a/src/bisonpre +++ b/src/bisonpre @@ -1,601 +1,559 @@ -#!/usr/bin/env perl -# See copyright, etc in below POD section. +#!/usr/bin/env python3 ###################################################################### -use warnings; -use Getopt::Long; -use IO::File; -use Pod::Usage; -use strict; -use vars qw($Debug $VERSION); +import argparse +import os +import re +import subprocess +import sys +from pprint import pprint, pformat -$VERSION = '3.404'; +###################################################################### -our $Self; -#====================================================================== -# main +def process(): + unlink_outputs() -our $Opt_Debug; -our $Opt_Definitions; -our $Opt_File_Prefix; -our $Opt_Name_Prefix; -our $Opt_Output; -our $Opt_Token_Table; -our $Opt_Verbose; -our $Opt_Yacc = "bison"; -our $Opt_Input; + bison_version_check() + supports_report = Bison_Version >= 2.3 -autoflush STDOUT 1; -autoflush STDERR 1; -Getopt::Long::config("no_auto_abbrev"); -if (! GetOptions( - # Local options - "help" => \&usage, - "version" => sub { print "Version $VERSION\n"; exit(0); }, - "yacc=s" => \$Opt_Yacc, - # Passed to Bison - "t|debug" => sub { $Opt_Debug = 1; }, - "b|file-prefix=s" => \$Opt_File_Prefix, - "d" => \$Opt_Definitions, - "k|token-table" => \$Opt_Token_Table, - "o=s" => \$Opt_Output, - "p|name-prefix=s" => \$Opt_Name_Prefix, - "v|verbose" => \$Opt_Verbose, - "<>" => \¶meter, - )) { - die "%Error: Bad usage, try 'bisonpre --help'\n"; -} - -$Opt_Input or die "bisonpre: %Error: input file not specified\n"; -$Opt_Output or die "bisonpre: %Error: --o option is required\n"; - -process(); - -#---------------------------------------------------------------------- - -sub usage { - print "Version $VERSION\n"; - pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT, -noperldoc=>1); - exit(1); # Unreachable -} - -sub parameter { - my $param = shift; - if (!defined $Opt_Input) { - $Opt_Input = $param; - } else { - die "bisonpre: %Error: Unknown parameter: $param\n"; - } -} - -####################################################################### - -sub process { - remove_outputs(); - - $Self->{bison_version} = bison_version_check(); - my $supports_report = ($Self->{bison_version} >= 2.3); - - clean_input($Opt_Input, tmp_prefix().".y"); + clean_input(Args.input, tmp_prefix() + ".y") # Run bison - my $command = ($Opt_Yacc - .($Opt_Debug?" -t":"") - .($Opt_Definitions?" -d":"") - .($Opt_Token_Table?" -k":"") - .($Opt_Verbose?" -v":"") - .(($Opt_Verbose && $supports_report)?" --report=itemset --report=lookahead":"") - # -p required for GLR parsers; they write to -p basename, not -o - .($Opt_Name_Prefix?" -p $Opt_Name_Prefix":"") - ." -b ".tmp_prefix() - ." -o ".tmp_prefix().".c" - ." ".tmp_prefix().".y" ); + command = ( + Args.yacc + (" -t" if Args.debug else "") + + (" -d" if Args.definitions else "") + + (" -k" if Args.token_table else "") + (" -v" if Args.verbose else "") + + (" --report=itemset --report=lookahead" if + (Args.verbose and supports_report) else "") + # -p required for GLR parsers; they write to -p basename, not -o + + ((" -p " + Args.name_prefix) if Args.name_prefix else "") + " -b " + + tmp_prefix() + " -o " + tmp_prefix() + ".c" + " " + tmp_prefix() + + ".y") - print " $command\n"; - system $command; - my $status = $?; - if ($status != 0) { - remove_outputs(); - my $v = bison_version_check(); - die "bisonpre: %Error: $Opt_Yacc version $v run failed due to errors\n"; - } + print(" " + command) + status = subprocess.call(command, shell=True) + if status != 0: + unlink_outputs() + sys.exit("bisonpre: %Error: " + Args.yacc + " version " + + Bison_Version + " run failed due to errors\n") - clean_output(tmp_prefix().".output",output_prefix().".output", 1,0); - warning_check(output_prefix().".output"); + clean_output(tmp_prefix() + ".output", + output_prefix() + ".output", True, False) + warning_check(output_prefix() + ".output") - clean_output(tmp_prefix().".c", output_prefix().".c", 0,1); - clean_output(tmp_prefix().".h", output_prefix().".h", 0,1); - remove_tmp(); -} + clean_output(tmp_prefix() + ".c", output_prefix() + ".c", False, True) + clean_output(tmp_prefix() + ".h", output_prefix() + ".h", False, True) + unlink_tmp() -sub tmp_prefix { - return output_prefix()."_pretmp"; -} -sub output_prefix { - my $o; - if ($Opt_Output) { - (my $o = $Opt_Output) =~ s!\.[^.]*$!!; - return $o; - } else { - return $Opt_File_Prefix.".tab"; - } -} +def tmp_prefix(): + return output_prefix() + "_pretmp" -sub remove_tmp { - unlink(tmp_prefix().".c"); # Ok if errors - unlink(tmp_prefix().".h"); # Ok if errors - unlink(tmp_prefix().".output"); # Ok if errors -} -sub remove_outputs { - remove_tmp(); - unlink(output_prefix().".c"); # Ok if errors - unlink(output_prefix().".h"); # Ok if errors +def output_prefix(): + if Args.output: + o = re.sub(r'\.[^.]*$', '', Args.output) + return o + else: + return Args.file_prefix + ".tab" + + +def unlink_ok(filename): + try: + os.unlink(filename) + except OSError: + pass + + +def unlink_tmp(): + unlink_ok(tmp_prefix() + ".c") + unlink_ok(tmp_prefix() + ".h") + unlink_ok(tmp_prefix() + ".output") + + +def unlink_outputs(): + unlink_tmp() + unlink_ok(output_prefix() + ".c") + unlink_ok(output_prefix() + ".h") # We don't remove .output file, as it's useful for debugging errors -} -sub bison_version_check { - my $v = `$Opt_Yacc --version`; - if ($v && $v =~ /([0-9]+\.[0-9]+)/) { - my $v = $1; - ($v >= 1.875) or die "bisonpre: %Error: '$Opt_Yacc' is version $v; version 1.875 or newer is required\n"; - return $v; - } else { - die "bisonpre: %Error: '$Opt_Yacc' is not installed, or not working\n"; - } -} -sub clean_output { - my $filename = shift; - my $outname = shift || $filename; - my $is_output = shift; - my $is_c = shift; - print " edit $filename $outname\n"; +def bison_version_check(): + sp = subprocess.Popen(Args.yacc + " --version", + shell=True, + stdout=subprocess.PIPE) + out = str(sp.stdout.read()) + match = re.search(r'([0-9]+\.[0-9]+)', out) + if match: + v = float(match.group(1)) + if v < 1.875: + sys.exit("bisonpre: %Error: '" + Args.yacc + "' is version " + v + + "; version 1.875 or newer is required\n") + global Bison_Version + Bison_Version = v + return - my $fh = IO::File->new("<$filename") or die "%Error: $! $filename\n"; - my @lines = $fh->getlines; - $fh->close; + sys.exit("bisonpre: %Error: '" + Args.yacc + + "' is not installed, or not working\n") - (my $basename = tmp_prefix().".") =~ s!.*/!!; - $basename = quotemeta($basename); - (my $newbase = $Opt_Input) =~ s!.*/!!; - $newbase =~ s/\.y/./; - if ($is_output) { - my %state_line; my $l=0; - foreach my $line (@lines) { - $l++; +def clean_output(filename, outname, is_output, is_c): + print(" edit " + filename + " " + outname) + + with open(filename) as fh: + lines = fh.readlines() + + basename = re.sub(r'.*/', '', tmp_prefix() + ".") + basename = re.escape(basename) + newbase = re.sub(r'.*/', '', Args.input) + newbase = re.sub(r'\.y', '.', newbase) + + if is_output: + state_line = {} + l = 0 + for line in lines: + l += 1 # We add a colon so it's easy to search for the definition - $state_line{$1} = $l if $line =~ s/^state (\d+)\s*$/state $1:/; - } - my @out; - foreach my $line (@lines) { - if ($line =~ /^State (\d+) (conflicts)/) { - chomp $line; - $line .= " // line $state_line{$1}" if $state_line{$1}; - $line .= "\n"; - } - push @out, $line; - } - @lines = @out; @out = (); - } - if ($is_c) { - my %token_values; - my $in_en=0; - foreach my $line (@lines) { - $in_en=1 if $line =~ /enum\s+yytokentype/; - $in_en=0 if $line =~ /;/; - $token_values{$2} = $1 if $in_en && $line =~ /\b(\S+) = (\d+)/; - } - my @out; - foreach my $line (@lines) { - if (_enaline($line) && $line =~ /BISONPRE_TOKEN_NAMES/) { - push @out, $line; - foreach my $tv (sort keys %token_values) { - push @out, sprintf("\tcase %d: return \"%s\";\n", - $tv, $token_values{$tv}); - } - next; - } - push @out, $line; - } - @lines = @out; @out = (); - } + match = re.match(r'^state (\d+)\s*', line) + if match: + state_line[match.group(1)] = l + out = [] + for line in lines: + match = re.match(r'^State (\d+) (conflicts)', line) + if match: + line = line.rstrip() + if match.group(1) in state_line: + line += " // line " + state_line[match.group(1)] + line += "\n" + out.append(line) + lines = out + out = [] - $fh = IO::File->new(">$outname") or die "%Error: $! writing $outname\n"; - foreach my $line (@lines) { - # Fix filename refs - $line =~ s!$basename!$newbase!g; - # Fix bison 2.3 and GCC 4.2.1 - $line =~ s!\(YY_\("!(YY_((char*)"!g; - # Fix bison 2.3 glr-parser warning about yyerrorloc.YYTYPE::yydummy uninit - $line =~ s!(YYLTYPE yyerrloc;)!$1 yyerrloc.yydummy=0;/*bisonpre*/!g; - # Fix bison 3.6.1 unexpected nested-comment - $line =~ s!/\* "/\*.*\*/" \*/!!g; - $fh->write($line); - } - $fh->close; -} + if is_c: + token_values = {} + for line in lines: + if re.search(r'enum\s+yytokentype', + line) and not re.search(r';', line): + match = re.search(r'\b(\S+) = (\d+)', line) + if match: + token_values[match.group(2)] = match.group(1) + out = [] + for line in lines: + if _enaline(line) and re.search(r'BISONPRE_TOKEN_NAMES', line): + out.append(line) + for tv in sorted(token_values.keys()): + out.append("\tcase %d: return \"%s\";\n" % + (tv, token_values[tv])) + continue + out.append(line) + lines = out + out = [] -sub warning_check { - my $filename = shift; + with open(outname, "w") as fh: + for line in lines: + # Fix filename refs + line = re.sub(basename, newbase, line) + # Fix bison 2.3 and GCC 4.2.1 + line = re.sub(r'\(YY_\("', '(YY_((char*)"', line) + # Fix bison 2.3 glr-parser warning about yyerrorloc.YYTYPE::yydummy uninit + line = re.sub(r'(YYLTYPE yyerrloc;)', + r'\1 yyerrloc.yydummy=0;/*bisonpre*/', line) + # Fix bison 3.6.1 unexpected nested-comment + line = re.sub(r'/\* "/\*.*\*/" \*/', '', line) + fh.write(line) - my $fh = IO::File->new("<$filename") or die "%Error: $! $filename\n"; - while (defined(my $line = $fh->getline)) { - if ($line =~ /(conflicts|warning:|^useless)/i) { - die "%Error: $filename:$.: $line\n"; - } - } - $fh->close; -} -####################################################################### +def warning_check(filename): + with open(filename) as fh: + linenum = 0 + for line in fh: + linenum += 1 + if re.search(r'(conflicts|warning:|^useless)', + line, + flags=re.IGNORECASE): + sys.exit("%Error: " + filename + ":" + linenum + ": " + line + + "\n") -sub clean_input { - my $filename = shift; - my $outname = shift || $filename; # Can == filename if desired - print " edit $filename $outname\n"; - $Self->{filename} = $filename; - my $fh = IO::File->new("<$filename") or die "%Error: $! $filename\n"; - my @lines = $fh->getlines; - $fh->close; +###################################################################### + + +def clean_input(filename, outname): + print(" edit " + filename + " " + outname) + + global Filename + Filename = filename + + with open(filename) as fh: + lines = fh.readlines() # Find "%tokens:" # Find "rule:" and replace with just "rule:" - my %types; - my %rules; $Self->{rules} = \%rules; - my %tokens; - my $last_rule; - my $section = 1; - { - my @linesin = @lines; @lines=(); my $l=0; - foreach my $line (@linesin) { - $l++; - # ^/ to prevent comments from matching - $line =~ m!^[a-zA-Z0-9_<>]+:[^/]*[a-zA-Z]! and die "%Error: $filename:$l: Move text on rule line to next line: $line\n"; - if ($line =~ /^%%/) { - $section++; - if ($section==2) { $last_rule = undef; } - } - elsif ($line =~ s/^([a-zA-Z0-9_]+)<(\S*)>:/$1:/) { - !$rules{$1}{name} or die "%Error: $filename:$l: Redeclaring '$1': $line\n"; - $types{$2}{$1} = 1; - $rules{$1}{name} = $1; - $rules{$1}{type} = $2; - !$last_rule or die "%Error: $filename:$l: Unterminated previous rule\n"; - $last_rule = $1; - } elsif ($line =~ /^([a-zA-Z0-9_]+):/) { - !$rules{$1}{name} or die "%Error: $filename:$l: Redeclaring '$1': $line\n"; - $rules{$1}{name} = $1; - $rules{$1}{type} = ""; - !$last_rule or die "%Error: $filename:$l: Unterminated previous rule\n"; - $last_rule = $1; - } - push @lines, $line; - # Now clean the line and extract some more info - (my $cline = $line) =~ s/\/\/.*$/\n/; - (my $rline = $line) =~ s/\/\/.*$/\n/; - if ($cline =~ /^\s*;/) { - $last_rule or die "%Error: $filename:$l: Stray semicolon\n"; - $last_rule = undef; - } elsif ($last_rule) { - $rules{$last_rule}{rules_and_productions} .= $cline; - } - if ($cline =~ /^%token\s*<(\S+)>\s*(\S+)/) { - !$tokens{$2} or die "%Error: $filename:$l: Redeclaring '$2': $line\n"; - $tokens{$2} = $1; - } - foreach my $tok (split /[^a-zA-Z0-9_]+/, $cline) { - if ($last_rule && $tok=~/^[a-zA-Z]/) { - #print "TT $last_rule $tok\n"; - $rules{$last_rule}{subrules}{$tok} = 1; - $rules{$tok}{parentrules}{$last_rule} = 1; - } - } - } - } + global Rules + Rules = {} - #use Data::Dumper; print Dumper(\%rules); + types = {} + tokens = {} + last_rule = None + section = 1 + if True: + linesin = lines + lines = [] + l = 0 + for line in linesin: + l += 1 + # ^/ to prevent comments from matching + if re.match(r'^[a-zA-Z0-9_<>]+:[^/]*[a-zA-Z]', line): + sys.exit("%Error: " + filename + ":" + str(l) + + ": Move text on rule line to next line: " + line + + "\n") + + matcha = re.match(r'^([a-zA-Z0-9_]+)<(\S*)>(.*)$', + line, + flags=re.DOTALL) + matchb = re.match(r'^([a-zA-Z0-9_]+):', line) + + if re.match(r'^%%', line): + section += 1 + if section == 2: + last_rule = None + elif matcha: + name = matcha.group(1) + dtype = matcha.group(2) + line = name + matcha.group(3) + if name in Rules: + sys.exit("%Error: " + filename + ":" + str(l) + + ": Redeclaring '" + name + "': " + line) + if dtype not in types: + types[dtype] = {} + types[dtype][name] = 1 + Rules[name] = { + 'name': name, + 'type': dtype, + 'rules_and_productions': "", + 'subrules': {} + } + if last_rule: + sys.exit("%Error: " + filename + ":" + str(l) + + ": Unterminated previous rule\n") + last_rule = name + elif matchb: + name = matchb.group(1) + if name != 'public' and name != 'private': + if name in Rules: + sys.exit("%Error: " + filename + ":" + str(l) + + ": Redeclaring '" + name + "': " + line) + Rules[name] = { + 'name': name, + 'type': "", + 'rules_and_productions': "", + 'subrules': {} + } + if last_rule: + sys.exit("%Error: " + filename + ":" + str(l) + + ": Unterminated previous rule\n") + last_rule = name + + lines.append(line) + # Now clean the line and extract some more info + cline = re.sub(r'//.*$', '\n', line) + rline = re.sub(r'//.*$', '\n', line) + if re.match(r'^\s*;', cline): + if not last_rule: + sys.exit("%Error: " + filename + ":" + str(l) + + ": Stray semicolon\n") + last_rule = None + elif last_rule: + Rules[last_rule]['rules_and_productions'] += cline + + match = re.match(r'^%token\s*<(\S+)>\s*(\S+)', cline) + if match: + dtype = match.group(1) + tok = match.group(2) + if tok in tokens: + sys.exit("%Error: " + filename + ":" + str(l) + + ": Redeclaring '" + tok + "': " + line) + tokens[tok] = dtype + + for tok in re.split(r'[^a-zA-Z0-9_]+', cline): + if last_rule and re.match(r'^[a-zA-Z]', tok): + # print("TT "+last_rule+" "+tok+"\n") + Rules[last_rule]['subrules'][tok] = 1 + + #pprint(Rules) # Replace BISONPRE_VERSION(ver,,...) with expanded list - { - my @linesin = @lines; @lines=(); my $l=0; - foreach my $line (@linesin) { - $l++; - if (_enaline($line) && $line =~ /BISONPRE_VERSION/) { - # 1 3 4 - ($line =~ /BISONPRE_VERSION\((\S+)\s*,\s*((\S+)\s*,)?\s*([^\),]+)\)\s*$/) - or die "%Error: $filename:$l: Bad form of BISONPRE_VERSION: $line\n"; - my $ver=$1; my $ver_max=$3; my $cmd=$4; - if ($Self->{bison_version} >= $1 - && (!$ver_max || $Self->{bison_version} <= $ver_max)) { - $line = $cmd."\n"; - } else { - $line = "//NOP: $line"; - } - } - push @lines, $line; - } - } + if True: + linesin = lines + lines = [] + l = 0 + for line in linesin: + l += 1 + if _enaline(line) and re.search(r'BISONPRE_VERSION', line): + # 1 2 3 4 + match = re.search( + r'BISONPRE_VERSION\((\S+)\s*,\s*((\S+)\s*,)?\s*([^\),]+)\)\s*$', + line) + if not match: + sys.exit("%Error: " + filename + ":" + str(l) + + ": Bad form of BISONPRE_VERSION: " + line) + ver = match.group(1) + ver_max = match.group(3) + cmd = match.group(4) + if Bison_Version >= float(ver) and ( + not ver_max or Bison_Version <= float(ver_max)): + line = cmd + "\n" + else: + line = "//NOP: " + line + lines.append(line) # Replace BISONPRE_NOT(type,...) with expanded list - { - my @linesin = @lines; @lines=(); my $l=0; - foreach my $line (@linesin) { - $l++; - if (_enaline($line) && $line =~ /BISONPRE_NOT/) { - ($line =~ s/BISONPRE_NOT\((\S+)\)\s*(\{[^}]+})\s*$//) - or die "%Error: $filename:$l: Bad form of BISONPRE_NOT: $line\n"; - my $endtok = $1; my $action = $2; - my @endtoks = split(/,/, $endtok); - map { $tokens{$_} or die "%Error: $filename:$l: Can't find definition for token: $_\n" - } @endtoks; + if True: + linesin = lines + lines = [] + l = 0 + for line in linesin: + l += 1 + if _enaline(line) and re.search(r'BISONPRE_NOT', line): + match = re.search( + r'(.*)BISONPRE_NOT\((\S+)\)\s*(\{[^}]+})\s*(.*)$', + line, + flags=re.DOTALL) + if not match: + sys.exit("%Error: " + filename + ":" + str(l) + + ": Bad form of BISONPRE_NOT: " + line) + line = match.group(1) + match.group(4) + endtok = match.group(2) + action = match.group(3) + endtoks = endtok.split(',') + for etok in endtoks: + if not etok in tokens: + sys.exit("%Error: " + filename + ":" + str(l) + + ": Can't find definition for token: " + etok + + "\n") # Push it all onto one line to avoid error messages changing - my $bar = ""; - tok: - foreach my $tok (sort keys %tokens) { - foreach (@endtoks) { - next tok if $tok eq $_; - } - if ($endtok ne $tok) { - $line .= "\t$bar $tok $action"; - $bar = "|"; - } - } - $line .= "\n"; - } - push @lines, $line; - } - } + bar = "" + for tok in sorted(tokens.keys()): + hit = False + for etok in endtoks: + if tok == etok: + hit = True + break + if not hit and endtok != tok: + line += "\t" + bar + " " + tok + " " + action + bar = "|" + line += "\n" + lines.append(line) # Replace BISONPRE_COPY(type,{code}) - { - my @linesin = @lines; @lines=(); my $l=0; - foreach my $line (@linesin) { - $l++; - if (_enaline($line) && $line =~ /BISONPRE_COPY/) { - $line = _bisonpre_copy($line,$l,0); - } - push @lines, $line; - } - } + if True: + linesin = lines + lines = [] + l = 0 + for line in linesin: + l += 1 + if _enaline(line) and re.search(r'BISONPRE_COPY', line): + line = _bisonpre_copy(line, l, 0) + lines.append(line) # Replace ~[x]~ - must be after BISONPRE_COPY expansion - { - my @linesin = @lines; @lines=(); my $l=0; - foreach my $line (@linesin) { - $l++; - $line =~ s/~[a-zA-Z0-9_]+~//g; - push @lines, $line; - } - } + if True: + linesin = lines + lines = [] + l = 0 + for line in linesin: + l += 1 + line = re.sub(r'~[a-zA-Z0-9_]+~', '', line) + lines.append(line) # Find "BISONPRE_TYPES" - { - my @linesin = @lines; @lines=(); my $l=0; - my $needmore = 0; - foreach my $line (@linesin) { - $l++; - if (_enaline($line) && $line =~ m!//BISONPRE_TYPES!) { - push @lines, $line; - foreach my $type (sort keys %types) { - next if !$type; - my $line = "%type<$type>\t"; - foreach my $rule (sort keys %{$types{$type}}) { - $line.=" ".$rule; - } - $line .= "\n"; - push @lines, $line; - $needmore++ - } - } elsif ($needmore) { + if True: + linesin = lines + lines = [] + l = 0 + needmore = 0 + for line in linesin: + l += 1 + if _enaline(line) and re.search(r'//BISONPRE_TYPES', line): + lines.append(line) + for type in sorted(types.keys()): + if not type: + continue + line = "%type<" + type + ">\t" + for rule in sorted(types[type].keys()): + line += " " + rule + line += "\n" + lines.append(line) + needmore += 1 + elif needmore > 0: # Bison doesn't have a #line directive, so we need somewhere to insert into - $line =~ s!^\s*//.*$!!; - ($line =~ m/^\s*$/) - or die "%Error: $filename:$l: Need $needmore more blank lines to keep line numbers are constant\n"; - $needmore--; - } else { - push @lines, $line; - } - } - } + line = re.sub(r'^\s*//.*$', '', line) + if not re.match(r'^\s*$', line): + sys.exit( + "%Error: " + filename + ":" + str(l) + ": Need " + + needmore + + " more blank lines to keep line numbers are constant\n" + ) + needmore -= 1 + else: + lines.append(line) - $fh = IO::File->new(">$outname") or die "%Error: $! writing $outname\n"; - foreach my $line (@lines) { - $fh->write($line); - } - $fh->close; -} + with open(outname, "w") as fh: + for line in lines: + fh.write(line) -sub _bisonpre_copy { - my $text = shift; - my $l = shift; - my $depth = shift; - while ($text =~ /BISONPRE_COPY/) { - ($text =~ s/BISONPRE_COPY(_ONCE)?\((\S+)\s*,\s*\{([^}]*)}\s*\)/{HERE}/) - or die "%Error: $Self->{filename}:$l: Bad form of BISONPRE_NOT: $text\n"; - my $once = $1; my $rule = $2; my $code = $3; - $Self->{rules}{$rule} or die "%Error: $Self->{filename}:$l: Can't find definition for rule: $rule\n"; - if ($depth > 0 && $once) { + +def _bisonpre_copy(text, l, depth): + while re.search(r'BISONPRE_COPY', text): + match = re.match( + # 1 2 3 4 5 + r'(.*)BISONPRE_COPY(_ONCE)?\((\S+)\s*,\s*\{([^}]*)}\s*\)(.*)', + text, + flags=re.DOTALL) + if not match: + sys.exit("%Error: " + Filename + ":" + str(l) + + ": Bad form of BISONPRE_NOT: " + text) + text = match.group(1) + '{HERE}' + match.group(5) + once = match.group(2) + rule = match.group(3) + code = match.group(4) + if not rule in Rules: + sys.exit("%Error: " + Filename + ":" + str(l) + + ": Can't find definition for rule: " + rule) + if depth > 0 and once: # _ONCE means don't inherit - $text =~ s/\|[ \t]+{HERE}//; # Don't OR in nothing - $text =~ s/{HERE}//; - } else { + text = re.sub(r'\|[ \t]+{HERE}', '', text) # Don't OR in nothing + text = re.sub(r'{HERE}', '', text) + else: # Push it all onto one line to avoid error messages changing - my $insert = $Self->{rules}{$rule}{rules_and_productions}; - $insert =~ s/^\S+://g; # Strip rule name + insert = Rules[rule]['rules_and_productions'] + insert = re.sub(r'^\S+:', '', insert) # Strip rule name # Recurse so BISONPRE under B - #print "COPY $l code $code\n"; - #print "COPY $l in $insert\n"; - $_=$insert; eval("$code; \$_;"); $insert = $_; - #print "COPY $l out $insert\n"; - while ($insert =~ s/[ \t\n]+\n/\n/go) {} - while ($insert =~ s/\n/ /go) {} # Optional - preserve line numbering - $text =~ s/{HERE}/$insert/; - } - $depth++; - } - return $text; -} + #print "COPY $l code $code\n" + #print "COPY $l in $insert\n" + for op in code.split(';'): + if re.match(r'^\s*$', op): + continue + match = re.match(r'^\s*s/(.*?)/(.*?)/g\s*$', op) + if not match: + sys.exit("%Error: " + Filename + ":" + str(l) + + ": Didn't understand replacement: " + op) + left = match.group(1) + right = match.group(2) + insert = re.sub(left, right, insert) -sub _enaline { - my $line = shift; - return 0 if $line =~ m!//UN!; - return 1; -} - -####################################################################### -__END__ - -=pod - -=head1 NAME - -bisonpre - Bison wrapper with pre and post processing - -=head1 SYNOPSIS - - bisonpre --yacc bison --debug --verbose --defines X.h -k $< -pX -o X.c + #print "COPY $l out $insert\n" + insert = re.sub(r'[ \t\n]+\n', "\n", insert) + insert = re.sub(r'\n', " ", + insert) # Optional - preserve line numbering + text = re.sub(r'{HERE}', insert, text) + depth += 1 + return text -=head1 DESCRIPTION +def _enaline(line): + return not re.search(r'//UN', line) -Bisonpre is a wrapper for the Bison YACC replacement. Input to Bison is + +###################################################################### +# main + +parser = argparse.ArgumentParser( + allow_abbrev=False, + formatter_class=argparse.RawDescriptionHelpFormatter, + description= + """Bisonpre is a wrapper for the Bison YACC replacement. Input to Bison is preprocessed with substitution as described below under EXTENSIONS. Output from Bison is checked for additional errors, and corrected to work around -various compile warnings. +various compile warnings.""", + epilog=""" +BISON GRAMMAR EXTENSIONS -=head1 EXTENSIONS + //BISONPRE_TYPES -=over 4 + This is expanded into %type declarations. -=item //BISONPRE_TYPES + ~[a-z]+~ -This is expanded into %type declarations. + Any text matching ~[a-z]+~ is removed. This allows optional text to be + used only when the rule containing the ~~ is used in a BISONPRE_COPY. -=item ~[a-z]+~ + rule_label: -Any text matching ~[a-z]+~ is removed. This allows optional text to be -used only when the rule containing the ~~ is used in a BISONPRE_COPY. + This allows the label declaring a rule to also specify the type of the + rule. The type will be inserted where /*BISONPRE_TYPES*/ is + encountered. -=item rule_label: + BISONPRE_COPY(rule, {code}) -This allows the label declaring a rule to also specify the type of the -rule. The type will be inserted where /*BISONPRE_TYPES*/ is encountered. + Copy the rules and productions from the specified rule, filter through + the Python code provided in the {} and insert here into the output + file. -=item BISONPRE_COPY(rule, {code}) + BISONPRE_COPY_ONCE(rule, {code}) -Copy the rules and productions from the specified rule, filter through the -Perl code provided in the {} and insert here into the output file. + As with BISONPRE_COPY, but if called from underneath another + BISONPRE_COPY rule, ignore it. -=item BISONPRE_COPY_ONCE(rule, {code}) + BISONPRE_NOT(token[, token...]) -As with BISONPRE_COPY, but if called from underneath another BISONPRE_COPY -rule, ignore it. + Create a rule that matches every token except for those specified. -=item BISONPRE_NOT(token[, token...]) + BISONPRE_VERSION(ver, cmd) -Create a rule that matches every token except for those specified. + If the bison version is >= the specified version, include the given + command. -=item BISONPRE_VERSION(ver, cmd) - -If the bison version is >= the specified version, include the given command. - -=back - -=head1 ARGUMENTS - -=over 4 - -=item -b file-prefix -=item --file-prefix=file-prefix - -Passed to bison. - -Specify a prefix to use for all bison output file names. The names are -chosen as if the input file were named file-prefix.c. - -=item -d - -Passed to bison. - -Write an extra output file containing macro definitions for the token type -names defined in the grammar and the semantic value type YYSTYPE, as well -as a few extern variable declarations. If the parser output file is named -name.c then this file is named name.h. This output file is essential if -you wish to put the definition of yylex in a separate source file, because -yylex needs to be able to refer to token type codes and the variable -yylval. - -=item --help - -Displays this message and program version and exits. - -=item -k -=item --token-table - -Passed to bison. - -This switch causes the name.tab.c output to include a list of token names -in order by their token numbers; this is defined in the array yytname. -Also generated are #defines for YYNTOKENS, YYNNTS, YYNRULES, and YYNSTATES. - -=item -t -=item --debug - -Passed to bison. - -In the parser file, define the macro YYDEBUG to 1 if it is not already -defined, so that the debugging facilities are compiled. - -=item -v -=item --verbose - -Passed to bison. - -Write an extra output file containing verbose descriptions of the parser -states and what is done for each type of look-ahead token in that state. -This file also describes all the conflicts, both those resolved by operator -precedence and the unresolved ones. The file's name is made by removing -.tab.c or .c from the parser output file name, and adding .output instead. -Therefore, if the input file is foo.y, then the parser file is called -foo.tab.c by default. As a consequence, the verbose output file is called -foo.output. - -=item --version - -Print the version number and exit. - -=item --yacc - -Specify the name of the bison executable, defaults to "bison." - -=back - -=head1 DISTRIBUTION - -This is part of the L free Verilog EDA software -tool suite. The latest version is available from CPAN and from -L. - -Copyright 2008-2021 by Wilson Snyder. This program is free software; you +Copyright 2002-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. +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 +SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""") -=head1 AUTHORS +# Local options +parser.add_argument('--yacc', + action='store', + default='bison', + help='name of the bison executable, defaults to "bison"') -Wilson Snyder +# Arguments passed through to bison +parser.add_argument('-b', + '--file-prefix', + action='store', + help='Passed to bison.') +parser.add_argument('-d', + '--definitions', + action='store_true', + help='Passed to bison.') +parser.add_argument('-k', + '--token-table', + action='store_true', + help='Passed to bison.') +parser.add_argument('-o', + '--output', + action='store', + required=True, + help='Passed to bison. Sets output file name') +parser.add_argument('-p', + '--name-prefix', + action='store', + help='Passed to bison.') +parser.add_argument('-t', + '--debug', + action='store_true', + help='Passed to bison.') +parser.add_argument('-v', + '--verbose', + action='store_true', + help='Passed to bison.') -=head1 SEE ALSO +parser.add_argument('input', help='Passed to bison. Input grammar file.') -C +Args = parser.parse_args() -=cut +process() ###################################################################### ### Local Variables: diff --git a/src/verilog.y b/src/verilog.y index bdf07ddb9..e887c3a3d 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -1380,9 +1380,9 @@ portDirNetE: // IEEE: part of port, optional net type and/or direction /* empty */ { } // // Per spec, if direction given default the nettype. // // The higher level rule may override this VARDTYPE with one later in the parse. - | port_direction { VARDECL(PORT); VARDTYPE_NDECL(nullptr/*default_nettype*/); } - | port_direction { VARDECL(PORT); } net_type { VARDTYPE_NDECL(nullptr/*default_nettype*/); } // net_type calls VARDECL - | net_type { } // net_type calls VARDECL + | port_direction { VARDECL(PORT); VARDTYPE_NDECL(nullptr); } + | port_direction { VARDECL(PORT); } net_type { VARDTYPE_NDECL(nullptr); } // net_type calls VARDECL + | net_type { VARDTYPE_NDECL(nullptr); } // net_type calls VARDECL ; port_declNetE: // IEEE: part of port_declaration, optional net type @@ -2209,7 +2209,7 @@ type_declaration: // ==IEEE: type_declaration AstNodeDType* dtp = GRAMMARP->createArray(refp, $4, true); $$ = GRAMMARP->createTypedef($5, *$5, $7, dtp, $6); } // // - | yTYPEDEF id/*interface*/ '.' idAny/*type*/ idAny/*type*/ ';' + | yTYPEDEF id/*interface*/ '.' idAny/*type*/ idAny/*type*/ dtypeAttrListE ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: SystemVerilog 2005 typedef in this context"); } // // Allow redeclaring same typedef again // // Alternative is use of idAny below, but this will cause conflicts with ablve diff --git a/test_regress/driver.pl b/test_regress/driver.pl index ff7c66fc1..48fd12a05 100755 --- a/test_regress/driver.pl +++ b/test_regress/driver.pl @@ -598,15 +598,19 @@ sub new { .(($^O eq "darwin" ) ? " -Wl,-undefined,dynamic_lookup" : " -export-dynamic") + .($opt_verbose ? " -DTEST_VERBOSE=1":"") ." -o $self->{obj_dir}/libvpi.so"], + tool_c_flags => [], # ATSIM atsim => 0, + atsim_define => 'ATSIM', atsim_flags => [split(/\s+/,"-c +sv +define+ATSIM"), "+sv_dir+$self->{obj_dir}/.athdl_compile"], atsim_flags2 => [], # Overridden in some sim files atsim_run_flags => [], # GHDL ghdl => 0, + ghdl_define => 'GHDL', ghdl_work_dir => "$self->{obj_dir}/ghdl_compile", ghdl_flags => [($::Debug?"-v":""), "--workdir=$self->{obj_dir}/ghdl_compile", ], @@ -614,29 +618,34 @@ sub new { ghdl_run_flags => [], # IV iv => 0, + iv_define => 'IVERILOG', iv_flags => [split(/\s+/,"+define+IVERILOG -g2012 -o $self->{obj_dir}/simiv")], iv_flags2 => [], # Overridden in some sim files iv_pli => 0, # need to use pli iv_run_flags => [], # VCS vcs => 0, + vcs_define => 'VCS', vcs_flags => [split(/\s+/,"+vcs+lic+wait +cli -debug_access +define+VCS+1 -q -sverilog -CFLAGS '-DVCS' ")], vcs_flags2 => [], # Overridden in some sim files vcs_run_flags => [split(/\s+/,"+vcs+lic_wait")], # NC nc => 0, + nc_define => 'NC', nc_flags => [split(/\s+/,("+licqueue +nowarn+LIBNOU +define+NC=1 -q +assert +sv -c " .($opt_trace ? " +access+r":"")))], nc_flags2 => [], # Overridden in some sim files nc_run_flags => [split(/\s+/,"+licqueue -q +assert +sv -R")], # ModelSim ms => 0, + ms_define => 'MS', ms_flags => [split(/\s+/, ("-sv -work $self->{obj_dir}/work +define+MS=1 -ccflags \"-DMS=1\""))], ms_flags2 => [], # Overridden in some sim files ms_pli => 1, # need to use pli ms_run_flags => [split(/\s+/,"-lib $self->{obj_dir}/work -c -do 'run -all;quit' ")], # XSim xsim => 0, + xsim_define => 'XSIM', xsim_flags => [split(/\s+/,("--nolog --sv --define XSIM --work $self->{name}=$self->{obj_dir}/xsim"))], xsim_flags2 => [], # Overridden in some sim files xsim_run_flags => [split(/\s+/,("--nolog --runall --lib $self->{name}=$self->{obj_dir}/xsim" @@ -645,6 +654,7 @@ sub new { # Verilator vlt => 0, vltmt => 0, + verilator_define => 'VERILATOR', verilator_flags => ["-cc", "-Mdir $self->{obj_dir}", "-OD", # As currently disabled unless -O3 @@ -886,6 +896,7 @@ sub compile_vlt_flags { unshift @verilator_flags, "--trace-threads 1" if $param{vltmt} && $checkflags =~ /-trace /; unshift @verilator_flags, "--trace-threads 2" if $param{vltmt} && $checkflags =~ /-trace-fst /; unshift @verilator_flags, "--debug-partition" if $param{vltmt}; + unshift @verilator_flags, "-CFLAGS -ggdb -LDFLAGS -ggdb" if $opt_gdbsim; unshift @verilator_flags, "-CFLAGS -fsanitize=address -LDFLAGS -fsanitize=address" if $param{sanitize}; unshift @verilator_flags, "--make gmake" if $param{verilator_make_gmake}; unshift @verilator_flags, "--make cmake" if $param{verilator_make_cmake}; @@ -957,6 +968,7 @@ sub compile { } if ($param{atsim}) { + $param{tool_define} ||= $param{atsim_define}; $self->_make_top(); $self->_run(logfile=>"$self->{obj_dir}/atsim_compile.log", fails=>$param{fails}, @@ -971,6 +983,7 @@ sub compile { ]); } elsif ($param{ghdl}) { + $param{tool_define} ||= $param{ghdl_define}; mkdir $self->{ghdl_work_dir}; $self->_make_top(); $self->_run(logfile=>"$self->{obj_dir}/ghdl_compile.log", @@ -989,6 +1002,7 @@ sub compile { ]); } elsif ($param{vcs}) { + $param{tool_define} ||= $param{vcs_define}; $self->_make_top(); $self->_run(logfile=>"$self->{obj_dir}/vcs_compile.log", fails=>$param{fails}, @@ -1003,6 +1017,7 @@ sub compile { ]); } elsif ($param{nc}) { + $param{tool_define} ||= $param{nc_define}; $self->_make_top(); my @more_args; if ($self->vhdl) { @@ -1024,6 +1039,7 @@ sub compile { ]); } elsif ($param{ms}) { + $param{tool_define} ||= $param{ms_define}; $self->_make_top(); $self->_run(logfile=>"$self->{obj_dir}/ms_compile.log", fails=>$param{fails}, @@ -1039,6 +1055,7 @@ sub compile { ]); } elsif ($param{iv}) { + $param{tool_define} ||= $param{iv_define}; $self->_make_top(); my @cmd = (($ENV{VERILATOR_IVERILOG}||"iverilog"), @{$param{iv_flags}}, @@ -1055,6 +1072,7 @@ sub compile { cmd=>\@cmd); } elsif ($param{xsim}) { + $param{tool_define} ||= $param{xsim_define}; $self->_make_top(); $self->_run(logfile=>"$self->{obj_dir}/xsim_compile.log", fails=>$param{fails}, @@ -1069,6 +1087,7 @@ sub compile { ]); } elsif ($param{vlt_all}) { + $param{tool_define} ||= $param{verilator_define}; if ($self->sc && !$self->have_sc) { $self->skip("Test requires SystemC; ignore error since not installed\n"); @@ -1166,7 +1185,9 @@ sub compile { if ($param{make_pli}) { $self->oprint("Compile vpi\n") if $self->{verbose}; - my @cmd = ($ENV{CXX}, @{$param{pli_flags}}, "-DIS_VPI", $ENV{CFLAGS}, + my @cmd = ($ENV{CXX}, @{$param{pli_flags}}, + "-D".$param{tool_define}, + "-DIS_VPI", ($ENV{CFLAGS}||''), "$self->{t_dir}/$self->{pli_filename}"); $self->_run(logfile=>"$self->{obj_dir}/pli_compile.log", @@ -1219,7 +1240,7 @@ sub execute { @{$param{iv_run_flags}}, @{$param{all_run_flags}}, ); - if ($param{iv_pli}) { + if ($param{use_libvpi}) { # don't enter command line on $stop, include vpi unshift @cmd, "vvp -n -m $self->{obj_dir}/libvpi.so"; } @@ -1233,7 +1254,7 @@ sub execute { } elsif ($param{ms}) { my @pli_opt=(); - if ($param{ms_pli}) { + if ($param{use_libvpi}) { unshift @pli_opt, "-pli $self->{obj_dir}/libvpi.so"; } $self->_run(logfile=>"$self->{obj_dir}/ms_sim.log", @@ -1385,6 +1406,12 @@ sub errors { return $self->{errors}; } +sub golden_filename { + my $self = (ref $_[0]? shift : $Self); + $self->{golden_filename} = shift if defined $_[0]; + return $self->{golden_filename}; +} + sub scenario_off { my $self = (ref $_[0]? shift : $Self); return $self->{scenario_off}; @@ -1711,7 +1738,7 @@ sub _make_main { if (!$self->sc) { if ($self->{vl_time_stamp64}) { print $fh "vluint64_t main_time = 0;\n"; - print $fh "vluint64_t vl_time_stamp() { return main_time; }\n"; + print $fh "vluint64_t vl_time_stamp64() { return main_time; }\n"; } else { print $fh "double main_time = 0;\n"; print $fh "double sc_time_stamp() { return main_time; }\n"; @@ -1818,7 +1845,7 @@ sub _make_main { $fh->print(" if (sc_time_stamp() == save_time && save_time) {\n"); $fh->print(" save_model(\"$self->{obj_dir}/saved.vltsv\");\n"); $fh->print(" printf(\"Exiting after save_model\\n\");\n"); - $fh->print(" exit(0);\n"); + $fh->print(" return 0;\n"); $fh->print(" }\n"); } _print_advance_time($self, $fh, 1, $action); @@ -1843,7 +1870,7 @@ sub _make_main { $fh->print("\n"); print $fh " topp.reset();\n"; - print $fh " exit(0L);\n"; + print $fh " return 0;\n"; print $fh "}\n"; $fh->close(); } @@ -2232,7 +2259,7 @@ sub _vcd_read { my @hier = ($data); my $lasthier; while (defined(my $line = $fh->getline)) { - if ($line =~ /\$scope module\s+(\S+)/) { + if ($line =~ /\$scope (module|struct)\s+(\S+)/) { $hier[$#hier]->{$1} ||= {}; push @hier, $hier[$#hier]->{$1}; $lasthier = $hier[$#hier]; diff --git a/test_regress/t/TestVpi.h b/test_regress/t/TestVpi.h index 2dd400b11..bf9d1460f 100644 --- a/test_regress/t/TestVpi.h +++ b/test_regress/t/TestVpi.h @@ -11,17 +11,22 @@ #include "vpi_user.h" +// Avoid C++11 in this file as not all simulators allow it + //====================================================================== class TestVpiHandle { /// For testing, etc, wrap vpiHandle in an auto-releasing class - vpiHandle m_handle = NULL; - bool m_freeit = true; + vpiHandle m_handle; // No = as no C++11 + bool m_freeit; // No = as no C++11 public: - TestVpiHandle() {} + TestVpiHandle() + : m_handle(NULL) + , m_freeit(true) {} TestVpiHandle(vpiHandle h) - : m_handle(h) {} + : m_handle(h) + , m_freeit(true) {} ~TestVpiHandle() { release(); } operator vpiHandle() const { return m_handle; } inline TestVpiHandle& operator=(vpiHandle h) { @@ -32,7 +37,11 @@ public: void release() { if (m_handle && m_freeit) { // Below not VL_DO_DANGLING so is portable +#ifdef IVERILOG + vpi_free_object(m_handle); +#else vpi_release_handle(m_handle); +#endif m_handle = NULL; } } diff --git a/test_regress/t/t_EXAMPLE.pl b/test_regress/t/t_EXAMPLE.pl index 9a15dd2cc..2cb5eeaff 100755 --- a/test_regress/t/t_EXAMPLE.pl +++ b/test_regress/t/t_EXAMPLE.pl @@ -2,7 +2,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2019 by Wilson Snyder. This program is free software; you +# Copyright 2021 by Wilson Snyder. This program is free software; you # can redistribute it and/or modify it under the terms of either the GNU # Lesser General Public License Version 3 or the Perl Artistic License # Version 2.0. diff --git a/test_regress/t/t_EXAMPLE.v b/test_regress/t/t_EXAMPLE.v index 3a7373cc0..032785ac5 100644 --- a/test_regress/t/t_EXAMPLE.v +++ b/test_regress/t/t_EXAMPLE.v @@ -13,7 +13,7 @@ // please note it here, otherwise:** // // This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2020 by ____YOUR_NAME_HERE____. +// any use, without warranty, 2021 by ____YOUR_NAME_HERE____. // SPDX-License-Identifier: CC0-1.0 module t(/*AUTOARG*/ diff --git a/test_regress/t/t_array_packed_endian.v b/test_regress/t/t_array_packed_endian.v index 6af307eea..5a4fa03f0 100644 --- a/test_regress/t/t_array_packed_endian.v +++ b/test_regress/t/t_array_packed_endian.v @@ -28,6 +28,13 @@ typedef struct packed { // verilator lint_on LITENDIAN } t2; +logic [2:0][31:0] test2l; +// verilator lint_off LITENDIAN +logic [0:2][31:0] test2b; +logic [0:2][31:0] test1b; +// verilator lint_on LITENDIAN +logic [2:0][31:0] test1l; + module t; t2 t; initial begin @@ -65,6 +72,23 @@ module t; t.dl[7] = 1'b1; `checkh(t, 80'h80_0002040000100800_01); + test1b = '{0, 1, 2}; + test1l = test1b; + test2l = '{2, 1, 0}; + test2b = test2l; + `checkh(test2l[0], 0); + `checkh(test2l[2], 2); + `checkh(test2l, {32'h2, 32'h1, 32'h0}); + `checkh(test2b[0], 2); + `checkh(test2b[2], 0); + `checkh(test2b, {32'h2, 32'h1, 32'h0}); + `checkh(test1b[0], 0); + `checkh(test1b[2], 2); + `checkh(test1b, {32'h0, 32'h1, 32'h2}); + `checkh(test1l[0], 2); + `checkh(test1l[2], 0); + `checkh(test1l, {32'h0, 32'h1, 32'h2}); + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_assert_cover.v b/test_regress/t/t_assert_cover.v index dd90d552e..b80282802 100644 --- a/test_regress/t/t_assert_cover.v +++ b/test_regress/t/t_assert_cover.v @@ -19,6 +19,9 @@ module t (/*AUTOARG*/ .toggle (toggle), .cyc (cyc[31:0])); + Sub sub1 (.*); + Sub sub2 (.*); + always @ (posedge clk) begin if (cyc!=0) begin cyc <= cyc + 1; @@ -142,3 +145,14 @@ module Test `endif endmodule + +module Sub + ( + input clk, + input integer cyc + ); + + // Simple cover, per-instance + pi_sub: + cover property (@(posedge clk) cyc == 3); +endmodule diff --git a/test_regress/t/t_class_extends.pl b/test_regress/t/t_class_extends.pl index aabcde63e..696e24d17 100755 --- a/test_regress/t/t_class_extends.pl +++ b/test_regress/t/t_class_extends.pl @@ -11,6 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( + make_flags => 'VM_PARALLEL_BUILDS=1', # bug2775 ); execute( diff --git a/test_regress/t/t_clk_inp_init.cpp b/test_regress/t/t_clk_inp_init.cpp index ffbd585c3..79c85635a 100644 --- a/test_regress/t/t_clk_inp_init.cpp +++ b/test_regress/t/t_clk_inp_init.cpp @@ -14,7 +14,7 @@ vluint64_t main_time; double sc_time_stamp() { return main_time; } void oneTest(int seed) { - double sim_time = 1000; + vluint64_t sim_time = 1000; #ifdef TEST_VERBOSE VL_PRINTF("== Seed=%d\n", seed); @@ -33,7 +33,7 @@ void oneTest(int seed) { topp->eval(); // Tick for a little bit - while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { topp->clk = 0; topp->eval(); diff --git a/test_regress/t/t_math_concat_sel_bad.pl b/test_regress/t/t_concat_sel.pl similarity index 85% rename from test_regress/t/t_math_concat_sel_bad.pl rename to test_regress/t/t_concat_sel.pl index f3e3b1181..9a15dd2cc 100755 --- a/test_regress/t/t_math_concat_sel_bad.pl +++ b/test_regress/t/t_concat_sel.pl @@ -8,10 +8,13 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # Version 2.0. # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -scenarios(linter => 1); +scenarios(simulator => 1); -lint( - fails => 1, +compile( + ); + +execute( + check_finished => 1, ); ok(1); diff --git a/test_regress/t/t_math_concat_sel_bad.v b/test_regress/t/t_concat_sel.v similarity index 75% rename from test_regress/t/t_math_concat_sel_bad.v rename to test_regress/t/t_concat_sel.v index 88620ca3d..33dc12a99 100644 --- a/test_regress/t/t_math_concat_sel_bad.v +++ b/test_regress/t/t_concat_sel.v @@ -27,6 +27,21 @@ module t (/*AUTOARG*/ // Aggregate outputs into a single result vector wire [63:0] result = {51'h0, out4, out3, out2, out1}; + initial begin + if ({16'h1234}[0] != 1'b0) $stop; + if ({16'h1234}[2] != 1'b1) $stop; + if ({16'h1234}[11:4] != 8'h23) $stop; + if ({16'h1234}[4+:8] != 8'h23) $stop; + if ({16'h1234}[11-:8] != 8'h23) $stop; + if ({8'h12, 8'h34}[0] != 1'b0) $stop; + if ({8'h12, 8'h34}[2] != 1'b1) $stop; + if ({8'h12, 8'h34}[11:4] != 8'h23) $stop; + if ({8'h12, 8'h34}[4+:8] != 8'h23) $stop; + if ({8'h12, 8'h34}[11-:8] != 8'h23) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + // Test loop always @ (posedge clk) begin `ifdef TEST_VERBOSE diff --git a/test_regress/t/t_const_opt_red.pl b/test_regress/t/t_const_opt_red.pl index b8d7ae067..573f3e618 100755 --- a/test_regress/t/t_const_opt_red.pl +++ b/test_regress/t/t_const_opt_red.pl @@ -11,12 +11,16 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( - verilator_flags2=>["-Wno-UNOPTTHREADS"], + verilator_flags2=>["-Wno-UNOPTTHREADS", "--stats"], ); execute( check_finished => 1, ); +if ($Self->{vlt}) { + file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 135); +} + ok(1); 1; diff --git a/test_regress/t/t_const_opt_red.v b/test_regress/t/t_const_opt_red.v index d36faf72b..ee5d9fd06 100644 --- a/test_regress/t/t_const_opt_red.v +++ b/test_regress/t/t_const_opt_red.v @@ -24,19 +24,39 @@ module t(/*AUTOARG*/ logic a3; // From test of Test.v logic a4; // From test of Test.v logic a5; // From test of Test.v + logic a6; // From test of Test.v + logic a7; // From test of Test.v + logic a8; // From test of Test.v + logic a9; // From test of Test.v + logic a10; // From test of Test.v + logic a11; // From test of Test.v logic o1; // From test of Test.v logic o2; // From test of Test.v logic o3; // From test of Test.v logic o4; // From test of Test.v logic o5; // From test of Test.v + logic o6; // From test of Test.v + logic o7; // From test of Test.v + logic o8; // From test of Test.v + logic o9; // From test of Test.v + logic o10; // From test of Test.v + logic o11; // From test of Test.v logic x1; // From test of Test.v logic x2; // From test of Test.v logic x3; // From test of Test.v logic x4; // From test of Test.v logic x5; // From test of Test.v + logic x6; // From test of Test.v + logic x7; // From test of Test.v + logic x8; // From test of Test.v + logic x9; // From test of Test.v logic z1; // From test of Test.v logic z2; // From test of Test.v logic z3; // From test of Test.v + logic z4; // From test of Test.v + logic z5; // From test of Test.v + logic z6; // From test of Test.v + logic z7; // From test of Test.v // End of automatics wire [31:0] i = crc[31:0]; @@ -48,37 +68,57 @@ module t(/*AUTOARG*/ .a3 (a3), .a4 (a4), .a5 (a5), + .a6 (a6), + .a7 (a7), + .a8 (a8), + .a9 (a9), + .a10 (a10), + .a11 (a11), .o1 (o1), .o2 (o2), .o3 (o3), .o4 (o4), .o5 (o5), + .o6 (o6), + .o7 (o7), + .o8 (o8), + .o9 (o9), + .o10 (o10), + .o11 (o11), .x1 (x1), .x2 (x2), .x3 (x3), .x4 (x4), .x5 (x5), + .x6 (x6), + .x7 (x7), + .x8 (x8), + .x9 (x9), .z1 (z1), .z2 (z2), .z3 (z3), + .z4 (z4), + .z5 (z5), + .z6 (z6), + .z7 (z7), // Inputs .clk (clk), .i (i[31:0])); // Aggregate outputs into a single result vector // verilator lint_off WIDTH - wire [63:0] result = {a1,a2,a3,a4,a5, - o1,o2,o3,o4,o5, - x1,x2,x3,x4,x5}; + wire [63:0] result = {a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11, + o1,o2,o3,o4,o5,o6,o7,o8,o9,o10,o11, + x1,x2,x3,x4,x5,x6,x7,x8,x9}; // verilator lint_on WIDTH // Test loop always @ (posedge clk) begin `ifdef TEST_VERBOSE $write("[%0t] cyc==%0d crc=%x result=%x\n",$time, cyc, crc, result); - $display("a %b %b %b %b %b", a1, a2, a3, a4, a5); - $display("o %b %b %b %b %b", o1, o2, o3, o4, o5); - $display("x %b %b %b %b %b", x1, x2, x3, x4, x5); + $display("a %b %b %b %b %b %b %b %b %b %b %b", a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); + $display("o %b %b %b %b %b %b %b %b %b %b %b", o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11); + $display("x %b %b %b %b %b %b %b %b %b", x1, x2, x3, x4, x5, x6, x7, x8, x9); `endif cyc <= cyc + 1; crc <= {crc[62:0], crc[63]^crc[2]^crc[0]}; @@ -96,23 +136,33 @@ module t(/*AUTOARG*/ if (a1 != a3) $stop; if (a1 != a4) $stop; if (a1 != a5) $stop; + if (a6 != a7) $stop; + if (a8 != a9) $stop; if (o1 != o2) $stop; if (o1 != o3) $stop; if (o1 != o4) $stop; if (o1 != o5) $stop; + if (o6 != o7) $stop; + if (o8 != o9) $stop; if (x1 != x2) $stop; if (x1 != x3) $stop; if (x1 != x4) $stop; if (x1 != x5) $stop; + if (x1 != x6) $stop; + if (x1 != x7) $stop; if (z1 != '0) $stop; if (z2 != '1) $stop; if (z3 != '0) $stop; + if (z4 != '0) $stop; + if (z5 != '1) $stop; + if (z6 != '1) $stop; + if (z7 != '0) $stop; end else begin $write("[%0t] cyc==%0d crc=%x sum=%x\n",$time, cyc, crc, sum); if (crc !== 64'hc77bb9b3784ea091) $stop; // What checksum will we end up with (above print should match) -`define EXPECTED_SUM 64'hd7bd9c247dc7243c +`define EXPECTED_SUM 64'he9b854d521ebb8b6 if (sum !== `EXPECTED_SUM) $stop; $write("*-* All Finished *-*\n"); $finish; @@ -123,7 +173,10 @@ endmodule module Test(/*AUTOARG*/ // Outputs - a1, a2, a3, a4, a5, o1, o2, o3, o4, o5, x1, x2, x3, x4, x5, z1, z2, z3, + a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, + o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11, + x1, x2, x3, x4, x5, x6, x7, x8, x9, + z1, z2, z3, z4, z5, z6, z7, // Inputs clk, i ); @@ -131,10 +184,13 @@ module Test(/*AUTOARG*/ input clk; input [31:0] i; - output logic a1, a2, a3, a4, a5; - output logic o1, o2, o3, o4, o5; - output logic x1, x2, x3, x4, x5; - output logic z1, z2, z3; + output logic a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11; + output logic o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11; + output logic x1, x2, x3, x4, x5, x6, x7, x8, x9; + output logic z1, z2, z3, z4, z5, z6, z7; + + logic [127:0] d; + always_ff @(posedge clk) d <= {i, ~i, ~i, i}; always_ff @(posedge clk) begin a1 <= (i[5] & ~i[3] & i[1]); @@ -142,23 +198,43 @@ module Test(/*AUTOARG*/ a3 <= &{i[5], ~i[3], i[1]}; a4 <= ((i & 32'b101010) == 32'b100010); a5 <= ((i & 32'b001010) == 32'b000010) & i[5]; + a6 <= &i[5:3]; + a7 <= i[5] & i[4] & i[3] & i[5] & i[4]; + a8 <= &(~i[5:3]); + a9 <= ~i[5] & !i[4] & !i[3] && ~i[5] && !i[4]; + a10 <= ~(i[5] & ~d[3]) & (!i[5] & d[1]); // cannot be optimized + a11 <= d[0] & d[33] & d[66] & d[99] & !d[31] & !d[62] & !d[93] & !d[124]; // o1 <= (~i[5] | i[3] | ~i[1]); o2 <= (i[5]!=1 | i[3]!=0 | i[1]!=1); o3 <= |{~i[5], i[3], ~i[1]}; o4 <= ((i & 32'b101010) != 32'b100010); o5 <= ((i & 32'b001010) != 32'b000010) | !i[5]; + o6 <= |i[5:3]; + o7 <= i[5] | i[4] | i[3] | i[5] | i[4]; + o8 <= |(~i[5:3]); + o9 <= ~i[5] | !i[4] | ~i[3] || !i[5] || ~i[4]; + o10 <= ~(~i[5] | d[3]) | (i[5] | ~d[1]); // cannot be optimized + o11 <= d[0] | d[33] | d[66] | d[99] | !d[31] | !d[62] | !d[93] | !d[124]; // x1 <= (i[5] ^ ~i[3] ^ i[1]); x2 <= (i[5]==1 ^ i[3]==0 ^ i[1]==1); x3 <= ^{i[5], ~i[3], i[1]}; x4 <= ^((i & 32'b101010) ^ 32'b001000); x5 <= ^((i & 32'b001010) ^ 32'b001000) ^ i[5]; + x6 <= i[5] ^ ~i[3] ^ i[1] ^ i[3] ^ !i[1] ^ i[3] ^ ~i[1]; + x7 <= i[5] ^ (^((i & 32'b001010) ^ 32'b001000)); + x8 <= ~(~i[5] ^ d[3]) ^ (i[5] ^ ~d[1]); + x9 <= d[0] ^ d[33] ^ d[66] ^ d[99] ^ !d[31] ^ !d[62] ^ !d[93] ^ !d[124]; // // All zero/all one cases z1 <= (i[5] & ~i[3] & ~i[5]); z2 <= (~i[5] | i[3] | i[5]); z3 <= (i[5] ^ ~i[3] ^ ~i[5] ^ i[3]); + z4 <= &(i[0] && !i[0]); + z5 <= |(i[1] || !i[1]); + z6 <= ^(i[2] ^ !i[2]); + z7 <= ^(i[2] ^ i[2]); end endmodule diff --git a/test_regress/t/t_cover_lib_c.cpp b/test_regress/t/t_cover_lib_c.cpp index a9168e483..cce1893b6 100644 --- a/test_regress/t/t_cover_lib_c.cpp +++ b/test_regress/t/t_cover_lib_c.cpp @@ -49,5 +49,5 @@ int main() { VerilatedCov::write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage4.dat"); printf("*-* All Finished *-*\n"); - exit(failure ? 10 : 0); + return (failure ? 10 : 0); } diff --git a/test_regress/t/t_cover_line_cc.pl b/test_regress/t/t_cover_line_cc.pl index 08a0a6f03..8c8d785ae 100755 --- a/test_regress/t/t_cover_line_cc.pl +++ b/test_regress/t/t_cover_line_cc.pl @@ -11,6 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); top_filename("t/t_cover_line.v"); +golden_filename("t/t_cover_line.out"); compile( verilator_flags2 => ['--cc --coverage-line +define+ATTRIBUTE'], @@ -29,7 +30,7 @@ run(cmd => ["../bin/verilator_coverage", verilator_run => 1, ); -files_identical("$Self->{obj_dir}/annotated/t_cover_line.v", "t/t_cover_line.out"); +files_identical("$Self->{obj_dir}/annotated/t_cover_line.v", $Self->{golden_filename}); # Also try lcov run(cmd => ["../bin/verilator_coverage", diff --git a/test_regress/t/t_cover_line_cc_vlt.pl b/test_regress/t/t_cover_line_cc_vlt.pl index 1d21931e1..6b885fb8f 100755 --- a/test_regress/t/t_cover_line_cc_vlt.pl +++ b/test_regress/t/t_cover_line_cc_vlt.pl @@ -11,6 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); top_filename("t/t_cover_line.v"); +golden_filename("t/t_cover_line.out"); compile( verilator_flags2 => ['--cc', '--coverage-line', "t/t_cover_line.vlt"], @@ -30,7 +31,7 @@ run(cmd => ["../bin/verilator_coverage", verilator_run => 1, ); -files_identical("$Self->{obj_dir}/annotated/t_cover_line.v", "t/t_cover_line.out"); +files_identical("$Self->{obj_dir}/annotated/t_cover_line.v", $Self->{golden_filename}); ok(1); 1; diff --git a/test_regress/t/t_cover_toggle.v b/test_regress/t/t_cover_toggle.v index 1c4008c3b..eb8e24ae2 100644 --- a/test_regress/t/t_cover_toggle.v +++ b/test_regress/t/t_cover_toggle.v @@ -7,11 +7,13 @@ module t (/*AUTOARG*/ // Inputs clk, - check_real + check_real, + check_string ); input clk; input real check_real; // Check issue #2741 + input string check_string; // Check issue #2766 typedef struct packed { union packed { diff --git a/test_regress/t/t_display_noopt.pl b/test_regress/t/t_display_noopt.pl index 795598e65..ced4aa8a5 100755 --- a/test_regress/t/t_display_noopt.pl +++ b/test_regress/t/t_display_noopt.pl @@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); top_filename("t/t_display.v"); -$Self->{golden_filename} = "t/t_display.out"; # Match unopt version +golden_filename("t/t_display.out"); compile( verilator_flags2 => ["-O0"], diff --git a/test_regress/t/t_display_real_noopt.pl b/test_regress/t/t_display_real_noopt.pl index 04d0a8222..a41a102e4 100755 --- a/test_regress/t/t_display_real_noopt.pl +++ b/test_regress/t/t_display_real_noopt.pl @@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); top_filename("t/t_display_real.v"); -$Self->{golden_filename} = "t/t_display_real.out"; # Match unopt version +golden_filename("t/t_display_real.out"); compile( verilator_flags2 => ["-O0"], diff --git a/test_regress/t/t_display_signed_noopt.pl b/test_regress/t/t_display_signed_noopt.pl index 669394f5f..e113d374d 100755 --- a/test_regress/t/t_display_signed_noopt.pl +++ b/test_regress/t/t_display_signed_noopt.pl @@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); top_filename("t/t_display_signed.v"); -$Self->{golden_filename} = "t/t_display_signed.out"; # Match unopt version +golden_filename("t/t_display_signed.out"); compile( verilator_flags2 => ["-O0"], diff --git a/test_regress/t/t_dpi_accessors.cpp b/test_regress/t/t_dpi_accessors.cpp index c20c81745..54e7a9e18 100644 --- a/test_regress/t/t_dpi_accessors.cpp +++ b/test_regress/t/t_dpi_accessors.cpp @@ -26,6 +26,8 @@ using std::hex; using std::setfill; using std::setw; +double sc_time_stamp() { return 0; } + // Convenience function to check we didn't finish unexpectedly static void checkFinish(const char* msg) { if (Verilated::gotFinish()) { @@ -54,7 +56,7 @@ static void logRegHex(int clk, const char* desc, int bitWidth, int val, const ch // Convenience function to check we got an expected result. Silent on success. static void checkResult(bool p, const char* msg_fail) { - if (!p) { vl_fatal(__FILE__, __LINE__, "dut", msg_fail); } + if (!p) vl_fatal(__FILE__, __LINE__, "dut", msg_fail); } // Main function instantiates the model and steps through the test. diff --git a/test_regress/t/t_dpi_arg_inout_type.pl b/test_regress/t/t_dpi_arg_inout_type.pl index c885022e4..9ca750797 100755 --- a/test_regress/t/t_dpi_arg_inout_type.pl +++ b/test_regress/t/t_dpi_arg_inout_type.pl @@ -19,7 +19,7 @@ if ($Self->{nc}) { } compile( - v_flags2 => ["t/t_dpi_arg_inout_type.cpp"], + v_flags2 => ["t/$Self->{name}.cpp"], # --no-decoration so .out file doesn't comment on source lines verilator_flags2 => ["-Wall -Wno-DECLFILENAME --no-decoration"], # NC: Gdd the obj_dir to the C include path @@ -30,16 +30,13 @@ compile( ); if ($Self->{vlt_all}) { - files_identical( - "$Self->{obj_dir}/Vt_dpi_arg_inout_type__Dpi.h", - "t/t_dpi_arg_inout_type__Dpi.out" - ); + files_identical("$Self->{obj_dir}/$Self->{VM_PREFIX}__Dpi.h", + "t/$Self->{name}__Dpi.out"); } execute( check_finished => 1, expect_filename => $Self->{golden_filename}, - ms_pli => 0 ); ok(1); diff --git a/test_regress/t/t_dpi_arg_inout_unpack.cpp b/test_regress/t/t_dpi_arg_inout_unpack.cpp index 3e0b62d80..fffe3fcb1 100644 --- a/test_regress/t/t_dpi_arg_inout_unpack.cpp +++ b/test_regress/t/t_dpi_arg_inout_unpack.cpp @@ -91,7 +91,7 @@ void set_uint(svBitVecVal* v0, sv_longint_unsigned_t val, int bitwidth) { template bool compare(const T& act, const T& exp) { if (exp == act) { - if (VERBOSE_MESSAGE) { std::cout << "OK Exp:" << exp << " actual:" << act << std::endl; } + if (VERBOSE_MESSAGE) std::cout << "OK Exp:" << exp << " actual:" << act << std::endl; return true; } else { std::cout << "NG Exp:" << exp << " actual:" << act << std::endl; @@ -106,7 +106,7 @@ bool compare_scalar(const svScalar v0, sv_longint_unsigned_t val) { std::cout << "Mismatch at bit:" << 0 << " exp:" << exp_bit << " act:" << act_bit; return false; } - if (VERBOSE_MESSAGE) { std::cout << "OK " << val << " as expected " << std::endl; } + if (VERBOSE_MESSAGE) std::cout << "OK " << val << " as expected " << std::endl; return true; } diff --git a/test_regress/t/t_dpi_arg_inout_unpack.pl b/test_regress/t/t_dpi_arg_inout_unpack.pl index b46d023f8..18dfa90cf 100755 --- a/test_regress/t/t_dpi_arg_inout_unpack.pl +++ b/test_regress/t/t_dpi_arg_inout_unpack.pl @@ -19,7 +19,7 @@ if ($Self->{nc}) { } compile( - v_flags2 => ["t/t_dpi_arg_inout_unpack.cpp"], + v_flags2 => ["t/$Self->{name}.cpp"], # --no-decoration so .out file doesn't comment on source lines verilator_flags2 => ["-Wall -Wno-DECLFILENAME --no-decoration"], # NC: Gdd the obj_dir to the C include path @@ -30,15 +30,12 @@ compile( ); if ($Self->{vlt_all}) { - files_identical( - "$Self->{obj_dir}/Vt_dpi_arg_inout_unpack__Dpi.h", - "t/t_dpi_arg_inout_unpack__Dpi.out" - ); + files_identical("$Self->{obj_dir}/$Self->{VM_PREFIX}__Dpi.h", + "t/$Self->{name}__Dpi.out"); } execute( check_finished => 1, - ms_pli => 0 ); ok(1); diff --git a/test_regress/t/t_dpi_arg_input_type.pl b/test_regress/t/t_dpi_arg_input_type.pl index 8c609be40..9ca750797 100755 --- a/test_regress/t/t_dpi_arg_input_type.pl +++ b/test_regress/t/t_dpi_arg_input_type.pl @@ -19,7 +19,7 @@ if ($Self->{nc}) { } compile( - v_flags2 => ["t/t_dpi_arg_input_type.cpp"], + v_flags2 => ["t/$Self->{name}.cpp"], # --no-decoration so .out file doesn't comment on source lines verilator_flags2 => ["-Wall -Wno-DECLFILENAME --no-decoration"], # NC: Gdd the obj_dir to the C include path @@ -30,16 +30,13 @@ compile( ); if ($Self->{vlt_all}) { - files_identical( - "$Self->{obj_dir}/Vt_dpi_arg_input_type__Dpi.h", - "t/t_dpi_arg_input_type__Dpi.out" - ); + files_identical("$Self->{obj_dir}/$Self->{VM_PREFIX}__Dpi.h", + "t/$Self->{name}__Dpi.out"); } execute( check_finished => 1, expect_filename => $Self->{golden_filename}, - ms_pli => 0 ); ok(1); diff --git a/test_regress/t/t_dpi_arg_input_unpack.cpp b/test_regress/t/t_dpi_arg_input_unpack.cpp index 08084ae57..2442f9578 100644 --- a/test_regress/t/t_dpi_arg_input_unpack.cpp +++ b/test_regress/t/t_dpi_arg_input_unpack.cpp @@ -77,7 +77,7 @@ const bool VERBOSE_MESSAGE = false; template bool compare(const T& act, const T& exp) { if (exp == act) { - if (VERBOSE_MESSAGE) { std::cout << "OK Exp:" << exp << " actual:" << act << std::endl; } + if (VERBOSE_MESSAGE) std::cout << "OK Exp:" << exp << " actual:" << act << std::endl; return true; } else { std::cout << "NG Exp:" << exp << " actual:" << act << std::endl; diff --git a/test_regress/t/t_dpi_arg_input_unpack.pl b/test_regress/t/t_dpi_arg_input_unpack.pl index f6772ebcc..18dfa90cf 100755 --- a/test_regress/t/t_dpi_arg_input_unpack.pl +++ b/test_regress/t/t_dpi_arg_input_unpack.pl @@ -19,7 +19,7 @@ if ($Self->{nc}) { } compile( - v_flags2 => ["t/t_dpi_arg_input_unpack.cpp"], + v_flags2 => ["t/$Self->{name}.cpp"], # --no-decoration so .out file doesn't comment on source lines verilator_flags2 => ["-Wall -Wno-DECLFILENAME --no-decoration"], # NC: Gdd the obj_dir to the C include path @@ -30,15 +30,12 @@ compile( ); if ($Self->{vlt_all}) { - files_identical( - "$Self->{obj_dir}/Vt_dpi_arg_input_unpack__Dpi.h", - "t/t_dpi_arg_input_unpack__Dpi.out" - ); + files_identical("$Self->{obj_dir}/$Self->{VM_PREFIX}__Dpi.h", + "t/$Self->{name}__Dpi.out"); } execute( check_finished => 1, - ms_pli => 0 ); ok(1); diff --git a/test_regress/t/t_dpi_arg_output_type.pl b/test_regress/t/t_dpi_arg_output_type.pl index d469bfc8a..9ca750797 100755 --- a/test_regress/t/t_dpi_arg_output_type.pl +++ b/test_regress/t/t_dpi_arg_output_type.pl @@ -19,7 +19,7 @@ if ($Self->{nc}) { } compile( - v_flags2 => ["t/t_dpi_arg_output_type.cpp"], + v_flags2 => ["t/$Self->{name}.cpp"], # --no-decoration so .out file doesn't comment on source lines verilator_flags2 => ["-Wall -Wno-DECLFILENAME --no-decoration"], # NC: Gdd the obj_dir to the C include path @@ -30,16 +30,13 @@ compile( ); if ($Self->{vlt_all}) { - files_identical( - "$Self->{obj_dir}/Vt_dpi_arg_output_type__Dpi.h", - "t/t_dpi_arg_output_type__Dpi.out" - ); + files_identical("$Self->{obj_dir}/$Self->{VM_PREFIX}__Dpi.h", + "t/$Self->{name}__Dpi.out"); } execute( check_finished => 1, expect_filename => $Self->{golden_filename}, - ms_pli => 0 ); ok(1); diff --git a/test_regress/t/t_dpi_arg_output_unpack.cpp b/test_regress/t/t_dpi_arg_output_unpack.cpp index 70fb6f542..918081f8e 100644 --- a/test_regress/t/t_dpi_arg_output_unpack.cpp +++ b/test_regress/t/t_dpi_arg_output_unpack.cpp @@ -175,7 +175,7 @@ void set_3d(svBitVecVal* v, int bitwidth) { template bool compare(const T& act, const T& exp) { if (exp == act) { - if (VERBOSE_MESSAGE) { std::cout << "OK Exp:" << exp << " actual:" << act << std::endl; } + if (VERBOSE_MESSAGE) std::cout << "OK Exp:" << exp << " actual:" << act << std::endl; return true; } else { std::cout << "NG Exp:" << exp << " actual:" << act << std::endl; diff --git a/test_regress/t/t_dpi_arg_output_unpack.pl b/test_regress/t/t_dpi_arg_output_unpack.pl index c4b0c26de..18dfa90cf 100755 --- a/test_regress/t/t_dpi_arg_output_unpack.pl +++ b/test_regress/t/t_dpi_arg_output_unpack.pl @@ -19,7 +19,7 @@ if ($Self->{nc}) { } compile( - v_flags2 => ["t/t_dpi_arg_output_unpack.cpp"], + v_flags2 => ["t/$Self->{name}.cpp"], # --no-decoration so .out file doesn't comment on source lines verilator_flags2 => ["-Wall -Wno-DECLFILENAME --no-decoration"], # NC: Gdd the obj_dir to the C include path @@ -30,15 +30,12 @@ compile( ); if ($Self->{vlt_all}) { - files_identical( - "$Self->{obj_dir}/Vt_dpi_arg_output_unpack__Dpi.h", - "t/t_dpi_arg_output_unpack__Dpi.out" - ); + files_identical("$Self->{obj_dir}/$Self->{VM_PREFIX}__Dpi.h", + "t/$Self->{name}__Dpi.out"); } execute( check_finished => 1, - ms_pli => 0 ); ok(1); diff --git a/test_regress/t/t_dpi_context_c.cpp b/test_regress/t/t_dpi_context_c.cpp index 359aa05b2..5c1060ed4 100644 --- a/test_regress/t/t_dpi_context_c.cpp +++ b/test_regress/t/t_dpi_context_c.cpp @@ -55,7 +55,7 @@ int dpic_line() { #ifdef VERILATOR static int didDump = 0; - if (didDump++ == 0) { Verilated::scopesDump(); } + if (didDump++ == 0) Verilated::scopesDump(); #endif const char* scopenamep = svGetNameFromScope(scope); diff --git a/test_regress/t/t_dpi_open_c.cpp b/test_regress/t/t_dpi_open_c.cpp index cff5d506a..d75c822a1 100644 --- a/test_regress/t/t_dpi_open_c.cpp +++ b/test_regress/t/t_dpi_open_c.cpp @@ -161,6 +161,22 @@ void _dpii_all(int c, int p, int u, const svOpenArrayHandle i, const svOpenArray CHECK_RESULT_HEX(svSize(i, d), 7); } } +#ifdef VERILATOR + // Check out-of-bounds read doesn't access bad memory (when sanitizer used) + (void)svLeft(i, -1); + (void)svRight(i, -1); + (void)svLow(i, -1); + (void)svHigh(i, -1); + (void)svIncrement(i, -1); + (void)svSize(i, -1); + // + (void)svLeft(i, 99); + (void)svRight(i, 99); + (void)svLow(i, 99); + (void)svHigh(i, 99); + (void)svIncrement(i, 99); + (void)svSize(i, 99); +#endif if (c == 2 && p == 1 && u == 3) { for (int a = svLow(i, 1); a <= svHigh(i, 1); ++a) { diff --git a/test_regress/t/t_dpi_open_query.pl b/test_regress/t/t_dpi_open_query.pl index 8a94dbf67..32c447dfd 100755 --- a/test_regress/t/t_dpi_open_query.pl +++ b/test_regress/t/t_dpi_open_query.pl @@ -29,7 +29,6 @@ compile( execute( check_finished => 1, - ms_pli => 0 ); ok(1); diff --git a/test_regress/t/t_dpi_result_type.pl b/test_regress/t/t_dpi_result_type.pl index 7796879c7..9ca750797 100755 --- a/test_regress/t/t_dpi_result_type.pl +++ b/test_regress/t/t_dpi_result_type.pl @@ -19,7 +19,7 @@ if ($Self->{nc}) { } compile( - v_flags2 => ["t/t_dpi_result_type.cpp"], + v_flags2 => ["t/$Self->{name}.cpp"], # --no-decoration so .out file doesn't comment on source lines verilator_flags2 => ["-Wall -Wno-DECLFILENAME --no-decoration"], # NC: Gdd the obj_dir to the C include path @@ -30,16 +30,13 @@ compile( ); if ($Self->{vlt_all}) { - files_identical( - "$Self->{obj_dir}/Vt_dpi_result_type__Dpi.h", - "t/t_dpi_result_type__Dpi.out" - ); + files_identical("$Self->{obj_dir}/$Self->{VM_PREFIX}__Dpi.h", + "t/$Self->{name}__Dpi.out"); } execute( check_finished => 1, expect_filename => $Self->{golden_filename}, - ms_pli => 0 ); ok(1); diff --git a/test_regress/t/t_dpi_var.cpp b/test_regress/t/t_dpi_var.cpp index bbab2e36c..f62388ccb 100644 --- a/test_regress/t/t_dpi_var.cpp +++ b/test_regress/t/t_dpi_var.cpp @@ -114,7 +114,7 @@ unsigned int main_time = 0; double sc_time_stamp() { return main_time; } int main(int argc, char** argv, char** env) { - double sim_time = 1100; + vluint64_t sim_time = 1100; Verilated::commandArgs(argc, argv); Verilated::debug(0); @@ -132,7 +132,7 @@ int main(int argc, char** argv, char** env) { topp->clk = 0; main_time += 10; - while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { main_time += 1; topp->eval(); topp->clk = !topp->clk; @@ -144,5 +144,5 @@ int main(int argc, char** argv, char** env) { topp->final(); VL_DO_DANGLING(delete topp, topp); - exit(0L); + return 0; } diff --git a/test_regress/t/t_enum_public.cpp b/test_regress/t/t_enum_public.cpp index 328140331..e46b8d57d 100644 --- a/test_regress/t/t_enum_public.cpp +++ b/test_regress/t/t_enum_public.cpp @@ -12,6 +12,8 @@ #include "Vt_enum_public_p3.h" #include "Vt_enum_public_p62.h" +double sc_time_stamp() { return 0; } + int main(int argc, char* argv[]) { Vt_enum_public* topp = new Vt_enum_public; diff --git a/test_regress/t/t_flag_fi.cpp b/test_regress/t/t_flag_fi.cpp index c977decf8..f91a5f303 100644 --- a/test_regress/t/t_flag_fi.cpp +++ b/test_regress/t/t_flag_fi.cpp @@ -26,7 +26,7 @@ int main(int argc, char* argv[]) { Verilated::debug(0); topp->eval(); - if (!gotit) { vl_fatal(__FILE__, __LINE__, "dut", "Never got call to myfunction"); } + if (!gotit) vl_fatal(__FILE__, __LINE__, "dut", "Never got call to myfunction"); topp->final(); VL_DO_DANGLING(delete topp, topp); diff --git a/test_regress/t/t_flag_main.pl b/test_regress/t/t_flag_main.pl index 397fea235..381004460 100755 --- a/test_regress/t/t_flag_main.pl +++ b/test_regress/t/t_flag_main.pl @@ -15,6 +15,8 @@ compile( verilator_make_cmake => 0, verilator_make_gmake => 0, make_main => 0, + # Check that code --main produces uses only most modern API features + make_flags => 'CPPFLAGS_ADD=-DVL_NO_LEGACY', ); execute( diff --git a/test_regress/t/t_flag_main.v b/test_regress/t/t_flag_main.v index ef63ab8ca..cfbfe9164 100644 --- a/test_regress/t/t_flag_main.v +++ b/test_regress/t/t_flag_main.v @@ -6,7 +6,7 @@ module t(/*AUTOARG*/); initial begin - $write("[%0t] Hello", $time); // Check timestamp works + $write("[%0t] Hello\n", $time); // Check timestamp works $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_flag_timescale.out b/test_regress/t/t_flag_timescale.out index b82ab0b9a..72dfb1fc5 100644 --- a/test_regress/t/t_flag_timescale.out +++ b/test_regress/t/t_flag_timescale.out @@ -1,2 +1,3 @@ -Time scale of t is 1ms / 1us +t: Time scale of t is 1ms / 1us +sub: Time scale of sub is 1s / 1us *-* All Finished *-* diff --git a/test_regress/t/t_flag_timescale.v b/test_regress/t/t_flag_timescale.v index ed019c442..ce1d0aee8 100644 --- a/test_regress/t/t_flag_timescale.v +++ b/test_regress/t/t_flag_timescale.v @@ -5,9 +5,20 @@ // SPDX-License-Identifier: CC0-1.0 module t; + sub sub(); initial begin + $write("t: "); $printtimescale; + sub.pts(); $write("*-* All Finished *-*\n"); $finish; end endmodule + +`timescale 1s/1s +module sub; + task pts; + $write("sub: "); + $printtimescale; + endtask +endmodule diff --git a/test_regress/t/t_func_crc.pl b/test_regress/t/t_func_crc.pl index 7d49cd2cd..6549284aa 100755 --- a/test_regress/t/t_func_crc.pl +++ b/test_regress/t/t_func_crc.pl @@ -11,12 +11,16 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( - verilator_flags2 => ["--compiler msvc"], # We have deep expressions we want to test + verilator_flags2 => ["--compiler msvc", "--stats"], # We have deep expressions we want to test ); execute( check_finished => 1, ); +if ($Self->{vlt}) { + file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 3888); +} + ok(1); 1; diff --git a/test_regress/t/t_func_rand.cpp b/test_regress/t/t_func_rand.cpp index 6a93db0ff..c3b43a385 100644 --- a/test_regress/t/t_func_rand.cpp +++ b/test_regress/t/t_func_rand.cpp @@ -9,6 +9,8 @@ #include #include "Vt_func_rand.h" +double sc_time_stamp() { return 0; } + int main(int argc, char* argv[]) { Vt_func_rand* topp = new Vt_func_rand; diff --git a/test_regress/t/t_gate_ormux.pl b/test_regress/t/t_gate_ormux.pl index a2b95b689..996e05170 100755 --- a/test_regress/t/t_gate_ormux.pl +++ b/test_regress/t/t_gate_ormux.pl @@ -15,9 +15,13 @@ $Self->{sim_time} = $Self->{cycles} * 10 + 1000; compile( v_flags2 => ["+define+SIM_CYCLES=$Self->{cycles}",], - verilator_flags2=>["-Wno-UNOPTTHREADS"], + verilator_flags2=>["-Wno-UNOPTTHREADS", "--stats"], ); +if ($Self->{vlt}) { + file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 994); +} + execute( ); diff --git a/test_regress/t/t_gen_genblk_noinl.pl b/test_regress/t/t_gen_genblk_noinl.pl index 86adb4a8e..7574a1cfb 100755 --- a/test_regress/t/t_gen_genblk_noinl.pl +++ b/test_regress/t/t_gen_genblk_noinl.pl @@ -9,6 +9,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 top_filename("t_gen_genblk.v"); +golden_filename("t/t_gen_genblk.out"); scenarios(simulator => 1); @@ -19,7 +20,7 @@ compile( ); execute( - expect_filename => "t/t_gen_genblk.out", + expect_filename => $Self->{golden_filename}, ); ok(1); diff --git a/test_regress/t/t_interface_ref_trace.out b/test_regress/t/t_interface_ref_trace.out index 75875933b..b9de5a2a5 100644 --- a/test_regress/t/t_interface_ref_trace.out +++ b/test_regress/t/t_interface_ref_trace.out @@ -14,7 +14,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 $ value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 % val100 [31:0] $end $var wire 32 & val200 [31:0] $end $upscope $end @@ -25,7 +25,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 ' value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 ( val100 [31:0] $end $var wire 32 ) val200 [31:0] $end $upscope $end @@ -36,7 +36,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 * value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 + val100 [31:0] $end $var wire 32 , val200 [31:0] $end $upscope $end @@ -47,7 +47,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 * value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 + val100 [31:0] $end $var wire 32 , val200 [31:0] $end $upscope $end @@ -57,7 +57,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 * value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 + val100 [31:0] $end $var wire 32 , val200 [31:0] $end $upscope $end @@ -66,7 +66,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 $ value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 % val100 [31:0] $end $var wire 32 & val200 [31:0] $end $upscope $end @@ -75,7 +75,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 ' value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 ( val100 [31:0] $end $var wire 32 ) val200 [31:0] $end $upscope $end @@ -87,7 +87,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 ' value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 ( val100 [31:0] $end $var wire 32 ) val200 [31:0] $end $upscope $end @@ -98,7 +98,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 $ value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 % val100 [31:0] $end $var wire 32 & val200 [31:0] $end $upscope $end @@ -109,7 +109,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 - value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 . val100 [31:0] $end $var wire 32 / val200 [31:0] $end $upscope $end @@ -120,7 +120,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 - value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 . val100 [31:0] $end $var wire 32 / val200 [31:0] $end $upscope $end @@ -130,7 +130,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 - value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 . val100 [31:0] $end $var wire 32 / val200 [31:0] $end $upscope $end @@ -139,7 +139,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 ' value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 ( val100 [31:0] $end $var wire 32 ) val200 [31:0] $end $upscope $end @@ -148,7 +148,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 $ value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 % val100 [31:0] $end $var wire 32 & val200 [31:0] $end $upscope $end @@ -159,7 +159,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 $ value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 % val100 [31:0] $end $var wire 32 & val200 [31:0] $end $upscope $end @@ -170,7 +170,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 ' value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 ( val100 [31:0] $end $var wire 32 ) val200 [31:0] $end $upscope $end @@ -180,7 +180,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 $ value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 % val100 [31:0] $end $var wire 32 & val200 [31:0] $end $upscope $end @@ -189,7 +189,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 ' value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 ( val100 [31:0] $end $var wire 32 ) val200 [31:0] $end $upscope $end @@ -199,7 +199,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 $ value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 % val100 [31:0] $end $var wire 32 & val200 [31:0] $end $upscope $end @@ -210,7 +210,7 @@ $timescale 1ps $end $var wire 1 0 clk $end $var wire 32 # cyc [31:0] $end $var wire 32 ' value [31:0] $end - $scope module the_struct $end + $scope struct the_struct $end $var wire 32 ( val100 [31:0] $end $var wire 32 ) val200 [31:0] $end $upscope $end diff --git a/test_regress/t/t_interface_ref_trace_inla.pl b/test_regress/t/t_interface_ref_trace_inla.pl index bbcf8c7d1..60ba35eb9 100755 --- a/test_regress/t/t_interface_ref_trace_inla.pl +++ b/test_regress/t/t_interface_ref_trace_inla.pl @@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); top_filename("t/t_interface_ref_trace.v"); -$Self->{golden_filename} = "t/t_interface_ref_trace.out"; +golden_filename("t/t_interface_ref_trace.out"); compile( v_flags2 => ['+define+NO_INLINE_A'], diff --git a/test_regress/t/t_interface_ref_trace_inlab.pl b/test_regress/t/t_interface_ref_trace_inlab.pl index 35e91ccf0..68d5d7a0e 100755 --- a/test_regress/t/t_interface_ref_trace_inlab.pl +++ b/test_regress/t/t_interface_ref_trace_inlab.pl @@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); top_filename("t/t_interface_ref_trace.v"); -$Self->{golden_filename} = "t/t_interface_ref_trace.out"; +golden_filename("t/t_interface_ref_trace.out"); compile( v_flags2 => ['+define+NO_INLINE_A +define+NO_INLINE_B'], diff --git a/test_regress/t/t_interface_ref_trace_inlb.pl b/test_regress/t/t_interface_ref_trace_inlb.pl index fd06fd865..10e5c5036 100755 --- a/test_regress/t/t_interface_ref_trace_inlb.pl +++ b/test_regress/t/t_interface_ref_trace_inlb.pl @@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); top_filename("t/t_interface_ref_trace.v"); -$Self->{golden_filename} = "t/t_interface_ref_trace.out"; +golden_filename("t/t_interface_ref_trace.out"); compile( v_flags2 => ['+define+NO_INLINE_B'], diff --git a/test_regress/t/t_interface_typedef.out b/test_regress/t/t_interface_typedef.out new file mode 100644 index 000000000..73fe2430f --- /dev/null +++ b/test_regress/t/t_interface_typedef.out @@ -0,0 +1,7 @@ +%Error-UNSUPPORTED: t/t_interface_typedef.v:46:4: Unsupported: SystemVerilog 2005 typedef in this context + 46 | typedef ifc_if.struct_t struct_t; + | ^~~~~~~ +%Error: t/t_interface_typedef.v:51:16: syntax error, unexpected IDENTIFIER + 51 | struct_t substruct; + | ^~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_interface_typedef.pl b/test_regress/t/t_interface_typedef.pl new file mode 100755 index 000000000..3a6c0a6d3 --- /dev/null +++ b/test_regress/t/t_interface_typedef.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-2009 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + fails => $Self->{vlt_all}, + expect_filename => $Self->{golden_filename}, + ); + +execute( + check_finished => 1, + ) if !$Self->{vlt_all}; + +ok(1); +1; diff --git a/test_regress/t/t_interface_typedef.v b/test_regress/t/t_interface_typedef.v new file mode 100644 index 000000000..a8faecb9b --- /dev/null +++ b/test_regress/t/t_interface_typedef.v @@ -0,0 +1,56 @@ +// 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 + +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0) + +interface ifc + #( + parameter int unsigned WIDTH + ) (); + typedef struct { + logic [WIDTH-1:0] data; + } struct_t; +endinterface + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + ifc #(10) i_ifc10(); + ifc #(20) i_ifc20(); + + sub #(10) u_sub10 (.clk, .ifc_if(i_ifc10)); + sub #(20) u_sub20 (.clk, .ifc_if(i_ifc20)); + + integer cyc = 1; + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc==20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule + +module sub #( + parameter int EXP_WIDTH) + ( + input logic clk, + ifc ifc_if); + typedef ifc_if.struct_t struct_t; + + wire [EXP_WIDTH-1:0] expval = '1; + + initial begin + struct_t substruct; + substruct.data = '1; + `checkh(substruct.data, expval); + end + +endmodule diff --git a/test_regress/t/t_math_imm2.cpp b/test_regress/t/t_math_imm2.cpp index 163e797b7..1238fff1f 100644 --- a/test_regress/t/t_math_imm2.cpp +++ b/test_regress/t/t_math_imm2.cpp @@ -6,6 +6,8 @@ #include #include "Vt_math_imm2.h" +double sc_time_stamp() { return 0; } + QData MaskVal(int lbit, int hbit) { QData val; for (val = 0; lbit <= hbit; lbit++) val |= (1ULL << lbit); @@ -52,6 +54,6 @@ int main(int argc, char* argv[]) { exit(10); } else { printf("*-* All Finished *-*\n"); - exit(0); + return 0; } } diff --git a/test_regress/t/t_math_shift.v b/test_regress/t/t_math_shift.v index cd071be78..e8f9379c4 100644 --- a/test_regress/t/t_math_shift.v +++ b/test_regress/t/t_math_shift.v @@ -24,30 +24,86 @@ module t (/*AUTOARG*/ localparam [3:0] PBIG29 = 4'b1 << 33'h100000000; // verilator lint_on WIDTH - reg [31:0] right; - reg [31:0] left; + reg [31:0] iright; + reg signed [31:0] irights; + reg [31:0] ileft; reg [P64-1:0] qright; + reg signed [P64-1:0] qrights; reg [P64-1:0] qleft; - reg [31:0] amt; + reg [95:0] wright; + reg signed [95:0] wrights; + reg [95:0] wleft; + + reg [31:0] q_iright; + reg signed [31:0] q_irights; + reg [31:0] q_ileft; + reg [P64-1:0] q_qright; + reg signed [P64-1:0] q_qrights; + reg [P64-1:0] q_qleft; + reg [95:0] q_wright; + reg signed [95:0] q_wrights; + reg [95:0] q_wleft; + + + reg [31:0] w_iright; + reg signed [31:0] w_irights; + reg [31:0] w_ileft; + reg [P64-1:0] w_qright; + reg signed [P64-1:0] w_qrights; + reg [P64-1:0] w_qleft; + reg [95:0] w_wright; + reg signed [95:0] w_wrights; + reg [95:0] w_wleft; + + reg [31:0] iamt; + reg [63:0] qamt; + reg [95:0] wamt; assign ign = {31'h0, clk} >>> 4'bx; // bug760 - assign ign2 = {amt[1:0] >> {22{amt[5:2]}}, amt[1:0] << (0 <<< amt[5:2])}; // bug1174 - assign ign3 = {amt[1:0] >> {22{amt[5:2]}}, - amt[1:0] >> {11{amt[5:2]}}, - $signed(amt[1:0]) >>> {22{amt[5:2]}}, - $signed(amt[1:0]) >>> {11{amt[5:2]}}, - amt[1:0] << {22{amt[5:2]}}, - amt[1:0] << {11{amt[5:2]}}}; + assign ign2 = {iamt[1:0] >> {22{iamt[5:2]}}, iamt[1:0] << (0 <<< iamt[5:2])}; // bug1174 + assign ign3 = {iamt[1:0] >> {22{iamt[5:2]}}, + iamt[1:0] >> {11{iamt[5:2]}}, + $signed(iamt[1:0]) >>> {22{iamt[5:2]}}, + $signed(iamt[1:0]) >>> {11{iamt[5:2]}}, + iamt[1:0] << {22{iamt[5:2]}}, + iamt[1:0] << {11{iamt[5:2]}}}; - wire [95:0] wamt = {amt,amt,amt}; - output wire [95:0] ign4 = wamt >> {11{amt[5:2]}}; - output wire signed [95:0] ign4s = $signed(wamt) >>> {11{amt[5:2]}}; + wire [95:0] wamtt = {iamt,iamt,iamt}; + output wire [95:0] ign4; + assign ign4 = wamtt >> {11{iamt[5:2]}}; + output wire signed [95:0] ign4s; + assign ign4s = $signed(wamtt) >>> {11{iamt[5:2]}}; always @* begin - right = 32'h819b018a >> amt; - left = 32'h819b018a << amt; - qright = 64'hf784bf8f_12734089 >> amt; - qleft = 64'hf784bf8f_12734089 >> amt; + iright = 32'h819b018a >> iamt; + irights = 32'sh819b018a >>> signed'(iamt); + ileft = 32'h819b018a << iamt; + qright = 64'hf784bf8f_12734089 >> iamt; + qrights = 64'shf784bf8f_12734089 >>> signed'(iamt); + qleft = 64'hf784bf8f_12734089 << iamt; + wright = 96'hf784bf8f_12734089_190abe48 >> iamt; + wrights = 96'shf784bf8f_12734089_190abe48 >>> signed'(iamt); + wleft = 96'hf784bf8f_12734089_190abe48 << iamt; + + q_iright = 32'h819b018a >> qamt; + q_irights = 32'sh819b018a >>> signed'(qamt); + q_ileft = 32'h819b018a << qamt; + q_qright = 64'hf784bf8f_12734089 >> qamt; + q_qrights = 64'shf784bf8f_12734089 >>> signed'(qamt); + q_qleft = 64'hf784bf8f_12734089 << qamt; + q_wright = 96'hf784bf8f_12734089_190abe48 >> qamt; + q_wrights = 96'shf784bf8f_12734089_190abe48 >>> signed'(qamt); + q_wleft = 96'hf784bf8f_12734089_190abe48 << qamt; + + w_iright = 32'h819b018a >> wamt; + w_irights = 32'sh819b018a >>> signed'(wamt); + w_ileft = 32'h819b018a << wamt; + w_qright = 64'hf784bf8f_12734089 >> wamt; + w_qrights = 64'shf784bf8f_12734089 >>> signed'(wamt); + w_qleft = 64'hf784bf8f_12734089 << wamt; + w_wright = 96'hf784bf8f_12734089_190abe48 >> wamt; + w_wrights = 96'shf784bf8f_12734089_190abe48 >>> signed'(wamt); + w_wleft = 96'hf784bf8f_12734089_190abe48 << wamt; end integer cyc; initial cyc=1; @@ -55,10 +111,12 @@ module t (/*AUTOARG*/ if (cyc!=0) begin cyc <= cyc + 1; `ifdef TEST_VERBOSE - $write("%d %x %x %x %x\n", cyc, left, right, qleft, qright); + $write("%d %x %x %x %x %x %x\n", cyc, ileft, iright, qleft, qright, wleft, wright); `endif if (cyc==1) begin - amt <= 32'd0; + iamt <= 0; + qamt <= 0; + wamt <= 0; if (P64 != 64) $stop; if (5'b10110>>2 != 5'b00101) $stop; if (5'b10110>>>2 != 5'b00101) $stop; // Note it cares about sign-ness @@ -72,57 +130,110 @@ module t (/*AUTOARG*/ if ((64'sh458c2de282e30f8b >> 68'sh4) !== 64'sh0458c2de282e30f8) $stop; end if (cyc==2) begin - amt <= 32'd28; - if (left != 32'h819b018a) $stop; - if (right != 32'h819b018a) $stop; - if (qleft != 64'hf784bf8f_12734089) $stop; - if (qright != 64'hf784bf8f_12734089) $stop; + iamt <= 28; + qamt <= 28; + wamt <= 28; + if (ileft != 32'h819b018a) $stop; + if (iright != 32'h819b018a) $stop; + if (irights != 32'h819b018a) $stop; + if (qleft != 64'hf784bf8f_12734089) $stop; + if (qright != 64'hf784bf8f_12734089) $stop; + if (qrights != 64'hf784bf8f_12734089) $stop; + if (wleft != 96'hf784bf8f12734089190abe48) $stop; + if (wright != 96'hf784bf8f12734089190abe48) $stop; + if (wrights != 96'hf784bf8f12734089190abe48) $stop; end if (cyc==3) begin - amt <= 32'd31; - if (left != 32'ha0000000) $stop; - if (right != 32'h8) $stop; - if (qleft != 64'h0000000f784bf8f1) $stop; + iamt <= 31; + qamt <= 31; + wamt <= 31; + if (ileft != 32'ha0000000) $stop; + if (iright != 32'h8) $stop; + if (irights != 32'hfffffff8) $stop; + if (qleft != 64'hf127340890000000) $stop; if (qright != 64'h0000000f784bf8f1) $stop; + if (qrights != 64'hffffffff784bf8f1) $stop; + if (wleft != 96'hf12734089190abe480000000) $stop; + if (wright != 96'h0000000f784bf8f127340891) $stop; + if (wrights != 96'hffffffff784bf8f127340891) $stop; end if (cyc==4) begin - amt <= 32'd32; - if (left != 32'h0) $stop; - if (right != 32'h1) $stop; - if (qleft != 64'h00000001ef097f1e) $stop; + iamt <= 32; + qamt <= 32; + wamt <= 32; + if (ileft != 32'h0) $stop; + if (iright != 32'h1) $stop; + if (qleft != 64'h8939a04480000000) $stop; if (qright != 64'h00000001ef097f1e) $stop; end if (cyc==5) begin - amt <= 32'd33; - if (left != 32'h0) $stop; - if (right != 32'h0) $stop; - if (qleft != 64'h00000000f784bf8f) $stop; + iamt <= 33; + qamt <= 33; + wamt <= 33; + if (ileft != 32'h0) $stop; + if (iright != 32'h0) $stop; + if (qleft != 64'h1273408900000000) $stop; if (qright != 64'h00000000f784bf8f) $stop; end if (cyc==6) begin - amt <= 32'd64; - if (left != 32'h0) $stop; - if (right != 32'h0) $stop; - if (qleft != 64'h000000007bc25fc7) $stop; + iamt <= 64; + qamt <= 64; + wamt <= 64; + if (ileft != 32'h0) $stop; + if (iright != 32'h0) $stop; + if (qleft != 64'h24e6811200000000) $stop; if (qright != 64'h000000007bc25fc7) $stop; end if (cyc==7) begin - amt <= 32'd128; - if (left != 32'h0) $stop; - if (right != 32'h0) $stop; + iamt <= 128; + qamt <= 128; + wamt <= 128; + if (ileft != 32'h0) $stop; + if (iright != 32'h0) $stop; if (qleft != 64'h0) $stop; if (qright != 64'h0) $stop; end if (cyc==8) begin - if (left != 32'h0) $stop; - if (right != 32'h0) $stop; - if (qleft != 64'h0) $stop; - if (qright != 64'h0) $stop; + iamt <= 100; + qamt <= {32'h10, 32'h0}; + wamt <= {32'h10, 64'h0}; + if (ileft != '0) $stop; + if (iright != '0) $stop; + if (irights != '1) $stop; + if (qleft != '0) $stop; + if (qright != '0) $stop; + if (qrights != '1) $stop; + if (wleft != '0) $stop; + if (wright != '0) $stop; + if (wrights != '1) $stop; end - if (cyc==9) begin + if (cyc==19) begin $write("*-* All Finished *-*\n"); $finish; end + + // General rule to test all q's + if (cyc != 0) begin + if (ileft != q_ileft) $stop; + if (iright != q_iright) $stop; + if (irights != q_irights) $stop; + if (qleft != q_qleft) $stop; + if (qright != q_qright) $stop; + if (qrights != q_qrights) $stop; + if (wleft != q_wleft) $stop; + if (wright != q_wright) $stop; + if (wrights != q_wrights) $stop; + + if (ileft != w_ileft) $stop; + if (iright != w_iright) $stop; + if (irights != w_irights) $stop; + if (qleft != w_qleft) $stop; + if (qright != w_qright) $stop; + if (qrights != w_qrights) $stop; + if (wleft != w_wleft) $stop; + if (wright != w_wright) $stop; + if (wrights != w_wrights) $stop; + end end end endmodule diff --git a/test_regress/t/t_mem_multi_ref_bad.out b/test_regress/t/t_mem_multi_ref_bad.out index e37aa73f6..558de331d 100644 --- a/test_regress/t/t_mem_multi_ref_bad.out +++ b/test_regress/t/t_mem_multi_ref_bad.out @@ -2,10 +2,11 @@ : ... In instance t 15 | dimn[1:0] = 0; | ^ -%Error: t/t_mem_multi_ref_bad.v:15:11: Extracting 2 bits from only 1 bit number - : ... In instance t +%Warning-SELRANGE: t/t_mem_multi_ref_bad.v:15:11: Extracting 2 bits from only 1 bit number + : ... In instance t 15 | dimn[1:0] = 0; | ^ + ... Use "/* verilator lint_off SELRANGE */" and lint_on around source to disable this message. %Error: t/t_mem_multi_ref_bad.v:16:14: Illegal bit or array select; type does not have a bit range, or bad dimension: data type is 'logic' : ... In instance t 16 | dim0[1][1] = 0; @@ -14,7 +15,6 @@ : ... In instance t 16 | dim0[1][1] = 0; | ^ - ... Use "/* verilator lint_off SELRANGE */" and lint_on around source to disable this message. %Error: t/t_mem_multi_ref_bad.v:17:17: Illegal bit or array select; type does not have a bit range, or bad dimension: data type is 'logic' : ... In instance t 17 | dim1[1][1][1] = 0; diff --git a/test_regress/t/t_mem_slot.cpp b/test_regress/t/t_mem_slot.cpp index 501c9113f..02f228c9b 100644 --- a/test_regress/t/t_mem_slot.cpp +++ b/test_regress/t/t_mem_slot.cpp @@ -7,6 +7,8 @@ #include #include "Vt_mem_slot.h" +double sc_time_stamp() { return 0; } + unsigned int Array[3]; unsigned int StepSim(Vt_mem_slot* sim, unsigned int slot, unsigned int bit, unsigned int val, diff --git a/test_regress/t/t_merge_cond.pl b/test_regress/t/t_merge_cond.pl index 834265b01..51f97242d 100755 --- a/test_regress/t/t_merge_cond.pl +++ b/test_regress/t/t_merge_cond.pl @@ -21,9 +21,9 @@ execute( if ($Self->{vlt}) { # Note, with vltmt this might be split differently, so only checking vlt file_grep($Self->{stats}, qr/Optimizations, MergeCond merges\s+(\d+)/i, - 11); + 10); file_grep($Self->{stats}, qr/Optimizations, MergeCond merged items\s+(\d+)/i, - 644); + 580); file_grep($Self->{stats}, qr/Optimizations, MergeCond longest merge\s+(\d+)/i, 64); } diff --git a/test_regress/t/t_mod_dollar$.pl b/test_regress/t/t_mod_dollar$.pl new file mode 100755 index 000000000..2d8172ca7 --- /dev/null +++ b/test_regress/t/t_mod_dollar$.pl @@ -0,0 +1,25 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2019 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt => 1); + +# This doesn't use the general compile rule as we want to make sure we form +# prefix properly using post-escaped identifiers +run(cmd => ["../bin/verilator", + "--cc", + "--Mdir obj_vlt/t_mod_dollar", + "--exe --build --main", + 't/t_mod_dollar$.v', + ], + verilator_run => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_mod_dollar$.v b/test_regress/t/t_mod_dollar$.v new file mode 100644 index 000000000..ee7d6ad75 --- /dev/null +++ b/test_regress/t/t_mod_dollar$.v @@ -0,0 +1,12 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020 by engr248. +// SPDX-License-Identifier: CC0-1.0 + +module \foo$bar (/*AUTOARG*/); + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_mod_interface_array4.pl b/test_regress/t/t_mod_interface_array4.pl new file mode 100755 index 000000000..2cb5eeaff --- /dev/null +++ b/test_regress/t/t_mod_interface_array4.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2021 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_mod_interface_array4.v b/test_regress/t/t_mod_interface_array4.v new file mode 100644 index 000000000..62ab2eebd --- /dev/null +++ b/test_regress/t/t_mod_interface_array4.v @@ -0,0 +1,78 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2021 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0) + +interface intf (); + integer index; +endinterface + +module sub + ( + input logic clk, + input int cyc, + intf alh[1:2], + intf ahl[2:1], + intf blh[1:2], + intf bhl[2:1] + ); + + always @(posedge clk) begin + if (cyc == 5) begin + `checkh(alh[1].index, 2); + `checkh(alh[2].index, 1); + `checkh(ahl[1].index, 1); + `checkh(ahl[2].index, 2); + `checkh(blh[1].index, 1); + `checkh(blh[2].index, 2); + `checkh(bhl[1].index, 2); + `checkh(bhl[2].index, 1); + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module t + ( + clk + ); + input clk; + + intf ifa1_intf[2:1](); + intf ifa2_intf[2:1](); + intf ifb1_intf[1:2](); + intf ifb2_intf[1:2](); + + int cyc; + + sub sub + ( + .clk, + .cyc, + .alh(ifa1_intf), + .ahl(ifa2_intf), + .blh(ifb1_intf), + .bhl(ifb2_intf) + ); + + always @(posedge clk) begin + cyc <= cyc + 1; + if (cyc == 1) begin + ifa1_intf[1].index = 1; + ifa1_intf[2].index = 2; + ifa2_intf[1].index = 1; + ifa2_intf[2].index = 2; + ifb1_intf[1].index = 1; + ifb1_intf[2].index = 2; + ifb2_intf[1].index = 1; + ifb2_intf[2].index = 2; + end + end + +endmodule diff --git a/test_regress/t/t_module_class_static_method.pl b/test_regress/t/t_module_class_static_method.pl old mode 100644 new mode 100755 diff --git a/test_regress/t/t_multitop_sig.cpp b/test_regress/t/t_multitop_sig.cpp index e89b40853..4cdca1235 100644 --- a/test_regress/t/t_multitop_sig.cpp +++ b/test_regress/t/t_multitop_sig.cpp @@ -10,6 +10,8 @@ #include #include "Vt_multitop_sig.h" +double sc_time_stamp() { return 0; } + // Use cout to avoid issues with %d/%lx etc #define CHECK_RESULT(got, exp) \ if ((got) != (exp)) { \ diff --git a/test_regress/t/t_order_multidriven.cpp b/test_regress/t/t_order_multidriven.cpp index 5f6003e4b..9a5993967 100644 --- a/test_regress/t/t_order_multidriven.cpp +++ b/test_regress/t/t_order_multidriven.cpp @@ -8,6 +8,8 @@ #include "verilated.h" #include "verilated_vcd_c.h" +double sc_time_stamp() { return 0; } + Vt_order_multidriven* vcore; VerilatedVcdC* vcd; vluint64_t vtime; diff --git a/test_regress/t/t_param_public.cpp b/test_regress/t/t_param_public.cpp index f2e158a78..c206441e3 100644 --- a/test_regress/t/t_param_public.cpp +++ b/test_regress/t/t_param_public.cpp @@ -12,6 +12,8 @@ #include "Vt_param_public_p.h" #include "Vt_param_public_t.h" +double sc_time_stamp() { return 0; } + int main(int argc, char* argv[]) { Vt_param_public* topp = new Vt_param_public; diff --git a/test_regress/t/t_param_unreachable.pl b/test_regress/t/t_param_unreachable.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_param_unreachable.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_unreachable.v b/test_regress/t/t_param_unreachable.v new file mode 100644 index 000000000..856944f5f --- /dev/null +++ b/test_regress/t/t_param_unreachable.v @@ -0,0 +1,34 @@ +// DESCRIPTION: Verilator: Verilog Test module +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020 by Pierre-Henri Horrein +// SPDX-License-Identifier: CC0-1.0 + +module t (input clk); + + parameter DEPTH = 1; + reg [DEPTH-1:0] shiftreg_gen; + reg [DEPTH-1:0] shiftreg; + reg my_sr_input = '1; + + // shiftreg_gen is generated: it should not raise any warning or error + always_ff @(posedge clk) begin + shiftreg_gen[0] <= my_sr_input; + end + if (DEPTH > 1) begin + always_ff @(posedge clk) begin + shiftreg_gen[DEPTH-1:1] <= shiftreg_gen[DEPTH-2:0]; + end + end + // shiftreg is not generated: it can raise a warning + always_ff @(posedge clk) begin + shiftreg[0] <= my_sr_input; + /* verilator lint_off SELRANGE */ + if (DEPTH > 1) shiftreg[DEPTH-1:1] <= shiftreg[DEPTH-2:0]; + /* verilator lint_on SELRANGE */ + end + + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_pp_circ_subst_bad.out b/test_regress/t/t_pp_circ_subst_bad.out index 657d44e8a..1f566721d 100644 --- a/test_regress/t/t_pp_circ_subst_bad.out +++ b/test_regress/t/t_pp_circ_subst_bad.out @@ -1,3 +1,3 @@ -%Error: t/t_pp_circ_subst_bad.v:8:40002: Too many preprocessor tokens on a line (>20000); perhaps recursive `define +%Error: t/t_pp_circ_subst_bad.v:8:80001: Too many preprocessor tokens on a line (>40000); perhaps recursive `define %Error: t/t_pp_circ_subst_bad.v:8:1: syntax error, unexpected IDENTIFIER %Error: Exiting due to diff --git a/test_regress/t/t_preproc_dos.pl b/test_regress/t/t_preproc_dos.pl index 166adf78f..a32871293 100755 --- a/test_regress/t/t_preproc_dos.pl +++ b/test_regress/t/t_preproc_dos.pl @@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); top_filename("$Self->{obj_dir}/$Self->{name}.v"); -$Self->{golden_filename} = "$Self->{obj_dir}/$Self->{name}.out"; +golden_filename("$Self->{obj_dir}/$Self->{name}.out"); # Rather then having to maintain a new .v and .out, simply add returns # to all lines of the existing t_preproc test. diff --git a/test_regress/t/t_randomize_method.pl b/test_regress/t/t_randomize_method.pl old mode 100644 new mode 100755 diff --git a/test_regress/t/t_randomize_method_bad.pl b/test_regress/t/t_randomize_method_bad.pl old mode 100644 new mode 100755 diff --git a/test_regress/t/t_randomize_method_types_unsup.pl b/test_regress/t/t_randomize_method_types_unsup.pl old mode 100644 new mode 100755 diff --git a/test_regress/t/t_randomize_method_unsup.pl b/test_regress/t/t_randomize_method_unsup.pl old mode 100644 new mode 100755 diff --git a/test_regress/t/t_runflag_uninit_bad.cpp b/test_regress/t/t_runflag_uninit_bad.cpp index 2a389c6e1..e1484272f 100644 --- a/test_regress/t/t_runflag_uninit_bad.cpp +++ b/test_regress/t/t_runflag_uninit_bad.cpp @@ -24,5 +24,5 @@ int main(int argc, char* argv[]) { // We aren't calling Verilated::commandArgs(argc, argv) topp->eval(); - exit(0); + return 0; } diff --git a/test_regress/t/t_savable_open_bad2.cpp b/test_regress/t/t_savable_open_bad2.cpp index 0e4c383cc..1fb06a851 100644 --- a/test_regress/t/t_savable_open_bad2.cpp +++ b/test_regress/t/t_savable_open_bad2.cpp @@ -44,5 +44,5 @@ int main(int argc, char* argv[]) { CHECK_RESULT_HEX(os.isOpen(), false); } - exit(0); + return 0; } diff --git a/test_regress/t/t_scope_map.cpp b/test_regress/t/t_scope_map.cpp index b27cb4c97..912150699 100644 --- a/test_regress/t/t_scope_map.cpp +++ b/test_regress/t/t_scope_map.cpp @@ -115,9 +115,6 @@ int main(int argc, char** argv, char** env) { tfp->dump((unsigned int)(main_time)); ++main_time; - // Code coverage of historical flush function - Verilated::flushCall(); - for (VerilatedScopeNameMap::const_iterator it = scopeMapp->begin(); it != scopeMapp->end(); ++it) { VerilatedVarNameMap* varNameMap = it->second->varsp(); diff --git a/test_regress/t/t_select_bad_msb.out b/test_regress/t/t_select_bad_msb.out index 69147264b..2401969bb 100644 --- a/test_regress/t/t_select_bad_msb.out +++ b/test_regress/t/t_select_bad_msb.out @@ -3,8 +3,8 @@ 12 | reg [0:22] backwd; | ^ ... Use "/* verilator lint_off LITENDIAN */" and lint_on around source to disable this message. -%Error: t/t_select_bad_msb.v:16:16: [1:4] Range extract has backward bit ordering, perhaps you wanted [4:1] - : ... In instance t +%Warning-SELRANGE: t/t_select_bad_msb.v:16:16: [1:4] Range extract has backward bit ordering, perhaps you wanted [4:1] + : ... In instance t 16 | sel2 = mi[1:4]; | ^ %Error: Exiting due to diff --git a/test_regress/t/t_string.v b/test_regress/t/t_string.v index 6b8e48e07..758d5744e 100644 --- a/test_regress/t/t_string.v +++ b/test_regress/t/t_string.v @@ -15,6 +15,10 @@ module t (/*AUTOARG*/ integer cyc=0; + reg [1*8:1] vstr1; + reg [2*8:1] vstr2; + reg [6*8:1] vstr6; + reg [4*8:1] vstr; const string s = "a"; // Check static assignment string s2; @@ -29,6 +33,15 @@ module t (/*AUTOARG*/ // a.itoa, a.hextoa, a.octoa, a.bintoa, a.realtoa initial begin + $sformat(vstr1, "%s", s); + `checks(vstr1, "a"); + + $sformat(vstr2, "=%s", s); + `checks(vstr2, "=a"); + + $sformat(vstr6, "--a=%s", s); + `checks(vstr6, "--a=a"); + $sformat(vstr, "s=%s", s); `checks(vstr, "s=a"); `checks(string'(vstr), "s=a"); diff --git a/test_regress/t/t_sys_fread.v b/test_regress/t/t_sys_fread.v index 60921f3f0..9b1818faa 100644 --- a/test_regress/t/t_sys_fread.v +++ b/test_regress/t/t_sys_fread.v @@ -79,7 +79,8 @@ module t; clear; code = $fread(r_upb, file, 15); `checkd(code, 6); - code = $fread(r_ups, file, 15, 2); `checkd(code, 4); + // Bug where fread in if() broke. + if ($fread(r_ups, file, 15, 2) != 4) $stop; dump; $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_sys_plusargs.pl b/test_regress/t/t_sys_plusargs.pl index 05eeca761..362a0c53c 100755 --- a/test_regress/t/t_sys_plusargs.pl +++ b/test_regress/t/t_sys_plusargs.pl @@ -16,7 +16,7 @@ compile( execute( check_finished => 1, - all_run_flags => ['+PLUS +INT=1234 +STRSTR +REAL=1.2345'], + all_run_flags => ['+PLUS +INT=1234 +STRSTR +REAL=1.2345 +IP%P101'], ); ok(1); diff --git a/test_regress/t/t_sys_plusargs.v b/test_regress/t/t_sys_plusargs.v index f387b5fa2..015ccfb2c 100644 --- a/test_regress/t/t_sys_plusargs.v +++ b/test_regress/t/t_sys_plusargs.v @@ -89,6 +89,11 @@ module t; $display("str='%s'",sv_str); if (sv_str != "T=1234") $stop; + sv_str = "none"; + if ($value$plusargs("IP%%P%b", p_i)!==1) $stop; + $display("str='%s'",sv_str); + if (p_i != 'b101) $stop; + sv_in = "INT=%d"; `ifdef VERILATOR if ($c1(0)) sv_in = "NEVER"; // Prevent constant propagation diff --git a/test_regress/t/t_timescale_lint_bad.out b/test_regress/t/t_timescale_lint_bad.out index e397440d8..fece9ec2e 100644 --- a/test_regress/t/t_timescale_lint_bad.out +++ b/test_regress/t/t_timescale_lint_bad.out @@ -1,4 +1,6 @@ %Error-TIMESCALEMOD: t/t_timescale_lint_bad.v:7:8: Timescale missing on this module as other modules have it (IEEE 1800-2017 3.14.2.2) + 7 | module pre_no_ts; + | ^~~~~~~~~ t/t_timescale_lint_bad.v:12:8: ... Location of module with timescale 12 | module t; | ^ diff --git a/test_regress/t/t_timescale_udp.pl b/test_regress/t/t_timescale_udp.pl new file mode 100755 index 000000000..4e42b8db0 --- /dev/null +++ b/test_regress/t/t_timescale_udp.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( + ); + +execute( + check_finished => 1, + ); + +ok(1); + +1; diff --git a/test_regress/t/t_timescale_udp.v b/test_regress/t/t_timescale_udp.v new file mode 100644 index 000000000..4b30c71ef --- /dev/null +++ b/test_regress/t/t_timescale_udp.v @@ -0,0 +1,43 @@ +// 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 + +`timescale 1ns/1ns +module t; + p p (); + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule + +`timescale 1ns/1ns +program p; +endprogram + +`celldefine +`timescale 1ns/1ns + +primitive a_udp(out, in); +output out; +input in; +reg out; + +table +0 : 1; +1 : 0; +? : ?; +x : x; +endtable +endprimitive +`endcelldefine + +`celldefine +module c_not(in, out); +input in; +output out; +assign out = !in1; +endmodule +`endcelldefine diff --git a/test_regress/t/t_trace_array_fst_portable.pl b/test_regress/t/t_trace_array_fst_portable.pl index f182ef1b7..3a34b9113 100755 --- a/test_regress/t/t_trace_array_fst_portable.pl +++ b/test_regress/t/t_trace_array_fst_portable.pl @@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); top_filename("t/t_trace_array.v"); -$Self->{golden_filename} = "t/t_trace_array_fst.out"; +golden_filename("t/t_trace_array_fst.out"); compile( verilator_flags2 => ['--cc --trace-fst --trace-structs', diff --git a/test_regress/t/t_trace_array_fst_threads_1.pl b/test_regress/t/t_trace_array_fst_threads_1.pl index 0be5ffbc2..c75beb0ca 100755 --- a/test_regress/t/t_trace_array_fst_threads_1.pl +++ b/test_regress/t/t_trace_array_fst_threads_1.pl @@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); top_filename("t/t_trace_array.v"); -$Self->{golden_filename} = "t/t_trace_array_fst.out"; +golden_filename("t/t_trace_array_fst.out"); compile( verilator_flags2 => ['--cc --trace-fst --trace-threads 1 --trace-structs'], diff --git a/test_regress/t/t_trace_array_fst_threads_2.pl b/test_regress/t/t_trace_array_fst_threads_2.pl index e20a9056b..75bbc7391 100755 --- a/test_regress/t/t_trace_array_fst_threads_2.pl +++ b/test_regress/t/t_trace_array_fst_threads_2.pl @@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); top_filename("t/t_trace_array.v"); -$Self->{golden_filename} = "t/t_trace_array_fst.out"; +golden_filename("t/t_trace_array_fst.out"); compile( verilator_flags2 => ['--cc --trace-fst --trace-threads 2 --trace-structs'], diff --git a/test_regress/t/t_trace_c_api.cpp b/test_regress/t/t_trace_c_api.cpp index 5b8e5dbc7..d2d3f0921 100644 --- a/test_regress/t/t_trace_c_api.cpp +++ b/test_regress/t/t_trace_c_api.cpp @@ -11,6 +11,8 @@ #include VM_PREFIX_INCLUDE +double sc_time_stamp() { return 0; } + extern void vcdTestMain(const char* filenamep); int main(int argc, char** argv, char** env) { diff --git a/test_regress/t/t_trace_complex_fst_threads_1.pl b/test_regress/t/t_trace_complex_fst_threads_1.pl index e38cce31c..a43a89337 100755 --- a/test_regress/t/t_trace_complex_fst_threads_1.pl +++ b/test_regress/t/t_trace_complex_fst_threads_1.pl @@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); top_filename("t/t_trace_complex.v"); -$Self->{golden_filename} = "t/t_trace_complex_fst.out"; +golden_filename("t/t_trace_complex_fst.out"); compile( verilator_flags2 => ['--cc --trace-fst --trace-threads 1'], diff --git a/test_regress/t/t_trace_complex_fst_threads_2.pl b/test_regress/t/t_trace_complex_fst_threads_2.pl index 72d74d432..b2c4721c7 100755 --- a/test_regress/t/t_trace_complex_fst_threads_2.pl +++ b/test_regress/t/t_trace_complex_fst_threads_2.pl @@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); top_filename("t/t_trace_complex.v"); -$Self->{golden_filename} = "t/t_trace_complex_fst.out"; +golden_filename("t/t_trace_complex_fst.out"); compile( verilator_flags2 => ['--cc --trace-fst --trace-threads 2'], diff --git a/test_regress/t/t_trace_complex_old_api.pl b/test_regress/t/t_trace_complex_old_api.pl index f1b4027b6..bdf9a0dec 100755 --- a/test_regress/t/t_trace_complex_old_api.pl +++ b/test_regress/t/t_trace_complex_old_api.pl @@ -13,6 +13,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); top_filename("t/t_trace_complex.v"); +golden_filename("t/t_trace_complex.out"); compile( verilator_flags2 => ['--cc --trace -CFLAGS -DVL_TRACE_VCD_OLD_API'], @@ -32,7 +33,7 @@ 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", "t/t_trace_complex.out"); +vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); ok(1); 1; diff --git a/test_regress/t/t_trace_complex_portable.pl b/test_regress/t/t_trace_complex_portable.pl index 5ddd2e3a4..ec7e5462e 100755 --- a/test_regress/t/t_trace_complex_portable.pl +++ b/test_regress/t/t_trace_complex_portable.pl @@ -13,6 +13,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); top_filename("t/t_trace_complex.v"); +golden_filename("t/t_trace_complex.out"); compile( verilator_flags2 => ['--cc --trace -CFLAGS -DVL_PORTABLE_ONLY'], @@ -32,7 +33,7 @@ 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", "t/t_trace_complex.out"); +vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); ok(1); 1; diff --git a/test_regress/t/t_trace_complex_structs.out b/test_regress/t/t_trace_complex_structs.out index dad2d3736..6fc0af14a 100644 --- a/test_regress/t/t_trace_complex_structs.out +++ b/test_regress/t/t_trace_complex_structs.out @@ -38,52 +38,52 @@ $timescale 1ps $end $var wire 32 H a [31:0] $end $upscope $end $upscope $end - $scope module 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 module 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 module 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 module 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 - $scope module v_enumb2_str $end + $scope struct v_enumb2_str $end $var wire 3 E a [2:0] $end $var wire 3 F b [2:0] $end $upscope $end - $scope module v_str32x2(0) $end + $scope struct v_str32x2(0) $end $var wire 32 @ data [31:0] $end $upscope $end - $scope module v_str32x2(1) $end + $scope struct v_str32x2(1) $end $var wire 32 A data [31:0] $end $upscope $end - $scope module v_strp $end + $scope struct v_strp $end $var wire 1 & b0 $end $var wire 1 % b1 $end $upscope $end - $scope module v_strp_strp $end - $scope module x0 $end + $scope struct v_strp_strp $end + $scope struct x0 $end $var wire 1 * b0 $end $var wire 1 ) b1 $end $upscope $end - $scope module x1 $end + $scope struct x1 $end $var wire 1 ( b0 $end $var wire 1 ' b1 $end $upscope $end $upscope $end - $scope module v_unip_strp $end - $scope module x0 $end + $scope struct v_unip_strp $end + $scope struct x0 $end $var wire 1 , b0 $end $var wire 1 + b1 $end $upscope $end - $scope module x1 $end + $scope struct x1 $end $var wire 1 , b0 $end $var wire 1 + b1 $end $upscope $end diff --git a/test_regress/t/t_trace_complex_threads_1.pl b/test_regress/t/t_trace_complex_threads_1.pl index 483faf3ab..3c7085576 100755 --- a/test_regress/t/t_trace_complex_threads_1.pl +++ b/test_regress/t/t_trace_complex_threads_1.pl @@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); top_filename("t/t_trace_complex.v"); -$Self->{golden_filename} = "t/t_trace_complex.out"; +golden_filename("t/t_trace_complex.out"); compile( verilator_flags2 => ['--cc --trace --trace-threads 1'] diff --git a/test_regress/t/t_trace_public_func.pl b/test_regress/t/t_trace_public_func.pl index e17f41601..519178385 100755 --- a/test_regress/t/t_trace_public_func.pl +++ b/test_regress/t/t_trace_public_func.pl @@ -11,6 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt_all => 1); top_filename("t/t_trace_public.v"); +golden_filename("t/t_trace_public.out"); compile( make_top_shell => 0, @@ -22,8 +23,7 @@ execute( check_finished => 1, ); -vcd_identical("$Self->{obj_dir}/simx.vcd", - "t/t_trace_public.out"); +vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); ok(1); 1; diff --git a/test_regress/t/t_trace_public_func_vlt.pl b/test_regress/t/t_trace_public_func_vlt.pl index 3c627949a..f532f19d0 100755 --- a/test_regress/t/t_trace_public_func_vlt.pl +++ b/test_regress/t/t_trace_public_func_vlt.pl @@ -11,6 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt_all => 1); top_filename("t/t_trace_public.v"); +golden_filename("t/t_trace_public.out"); compile( make_top_shell => 0, @@ -22,8 +23,7 @@ execute( check_finished => 1, ); -vcd_identical("$Self->{obj_dir}/simx.vcd", - "t/t_trace_public.out"); +vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); ok(1); 1; diff --git a/test_regress/t/t_trace_public_sig.pl b/test_regress/t/t_trace_public_sig.pl index 0a577a343..edb8bf2c4 100755 --- a/test_regress/t/t_trace_public_sig.pl +++ b/test_regress/t/t_trace_public_sig.pl @@ -11,6 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt_all => 1); top_filename("t/t_trace_public.v"); +golden_filename("t/t_trace_public.out"); compile( make_top_shell => 0, @@ -22,8 +23,7 @@ execute( check_finished => 1, ); -vcd_identical("$Self->{obj_dir}/simx.vcd", - "t/t_trace_public.out"); +vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); # vcd_identical doesn't detect "$var a.b;" vs "$scope module a; $var b;" file_grep("$Self->{obj_dir}/simx.vcd", qr/module glbl/i); diff --git a/test_regress/t/t_trace_public_sig_vlt.pl b/test_regress/t/t_trace_public_sig_vlt.pl index 2f20c9ff2..c29e1cdc2 100755 --- a/test_regress/t/t_trace_public_sig_vlt.pl +++ b/test_regress/t/t_trace_public_sig_vlt.pl @@ -11,6 +11,8 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt_all => 1); top_filename("t/t_trace_public.v"); +golden_filename("t/t_trace_public.out"); + my $out_filename = "$Self->{obj_dir}/V$Self->{name}.xml"; compile( @@ -27,8 +29,7 @@ execute( check_finished => 1, ); -vcd_identical("$Self->{obj_dir}/simx.vcd", - "t/t_trace_public.out"); +vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); # vcd_identical doesn't detect "$var a.b;" vs "$scope module a; $var b;" file_grep("$Self->{obj_dir}/simx.vcd", qr/module glbl/i); diff --git a/test_regress/t/t_trace_two_cc.cpp b/test_regress/t/t_trace_two_cc.cpp index 3cfd0ef46..4b445b8ed 100644 --- a/test_regress/t/t_trace_two_cc.cpp +++ b/test_regress/t/t_trace_two_cc.cpp @@ -33,7 +33,7 @@ vluint64_t main_time = 0; double sc_time_stamp() { return main_time; } int main(int argc, char** argv, char** env) { - double sim_time = 1100; + vluint64_t sim_time = 1100; Verilated::commandArgs(argc, argv); Verilated::debug(0); Verilated::traceEverOn(true); @@ -70,7 +70,7 @@ int main(int argc, char** argv, char** env) { ap->clk = false; main_time += 10; } - while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { ap->clk = !ap->clk; bp->clk = ap->clk; ap->eval_step(); @@ -90,9 +90,10 @@ int main(int argc, char** argv, char** env) { #ifdef TEST_HDR_TRACE if (tfp) tfp->close(); + VL_DO_DANGLING(delete tfp, tfp); #endif VL_DO_DANGLING(delete ap, ap); VL_DO_DANGLING(delete bp, bp); - exit(0L); + return 0; } diff --git a/test_regress/t/t_trace_two_sc.cpp b/test_regress/t/t_trace_two_sc.cpp index ebfffc68d..6d0c0e125 100644 --- a/test_regress/t/t_trace_two_sc.cpp +++ b/test_regress/t/t_trace_two_sc.cpp @@ -67,5 +67,5 @@ int sc_main(int argc, char** argv) { VL_DO_DANGLING(delete ap, ap); VL_DO_DANGLING(delete bp, bp); - exit(0L); + return 0; } diff --git a/test_regress/t/t_tri_gate.cpp b/test_regress/t/t_tri_gate.cpp index 89810bce7..f16ae0a45 100644 --- a/test_regress/t/t_tri_gate.cpp +++ b/test_regress/t/t_tri_gate.cpp @@ -44,7 +44,7 @@ int main() { for (tb->SEL = 0; tb->SEL < 2; tb->SEL++) { for (tb->A = 0; tb->A < 4; tb->A++) { tb->eval(); - if (!check()) { pass = false; } + if (!check()) pass = false; } } diff --git a/test_regress/t/t_tri_inout.cpp b/test_regress/t/t_tri_inout.cpp index 6156bfdbf..b3bd8aa46 100644 --- a/test_regress/t/t_tri_inout.cpp +++ b/test_regress/t/t_tri_inout.cpp @@ -43,7 +43,7 @@ int main() { for (tb->A = 0; tb->A < 2; tb->A++) { for (tb->B = 0; tb->B < 2; tb->B++) { tb->eval(); - if (!check()) { pass = false; } + if (!check()) pass = false; } } } diff --git a/test_regress/t/t_tri_pullup.cpp b/test_regress/t/t_tri_pullup.cpp index a85093968..12cbf9a1c 100644 --- a/test_regress/t/t_tri_pullup.cpp +++ b/test_regress/t/t_tri_pullup.cpp @@ -53,7 +53,7 @@ int main() { for (tb->OE = 0; tb->OE < 2; tb->OE++) { for (tb->A = 0; tb->A < 2; tb->A++) { tb->eval(); - if (!check()) { pass = false; } + if (!check()) pass = false; } } diff --git a/test_regress/t/t_tri_select.cpp b/test_regress/t/t_tri_select.cpp index b2efad8fa..c33f2c7cf 100644 --- a/test_regress/t/t_tri_select.cpp +++ b/test_regress/t/t_tri_select.cpp @@ -54,7 +54,7 @@ int main() { for (tb->A1 = 0; tb->A1 < 4; tb->A1++) { for (tb->A2 = 0; tb->A2 < 4; tb->A2++) { tb->eval(); - if (!check()) { pass = false; } + if (!check()) pass = false; } } } diff --git a/test_regress/t/t_var_overwidth_bad.cpp b/test_regress/t/t_var_overwidth_bad.cpp index 30573b0b4..2aa7d801a 100644 --- a/test_regress/t/t_var_overwidth_bad.cpp +++ b/test_regress/t/t_var_overwidth_bad.cpp @@ -34,5 +34,5 @@ int main(int argc, char** argv, char** env) { topp->final(); VL_DO_DANGLING(delete topp, topp); - exit(0L); + return 0; } diff --git a/test_regress/t/t_var_port_xml.out b/test_regress/t/t_var_port_xml.out new file mode 100644 index 000000000..b2e6f9f4f --- /dev/null +++ b/test_regress/t/t_var_port_xml.out @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_regress/t/t_var_port_xml.pl b/test_regress/t/t_var_port_xml.pl new file mode 100755 index 000000000..acd3ab33d --- /dev/null +++ b/test_regress/t/t_var_port_xml.pl @@ -0,0 +1,25 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2012 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt => 1); + +my $out_filename = "$Self->{obj_dir}/V$Self->{name}.xml"; + +compile( + verilator_flags2 => ['--xml-only'], + verilator_make_gmake => 0, + make_top_shell => 0, + make_main => 0, + ); + +files_identical($out_filename, $Self->{golden_filename}); + +ok(1); +1; diff --git a/test_regress/t/t_var_port_xml.v b/test_regress/t/t_var_port_xml.v new file mode 100644 index 000000000..88418b19c --- /dev/null +++ b/test_regress/t/t_var_port_xml.v @@ -0,0 +1,59 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2021 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +// This checks IEEE ports work correctly, we use XML output to make it easy to +// see all attributes are propagated + +// verilator lint_off MULTITOP + +`ifndef VERILATOR +module mh0 (wire x_inout_wire_logic); +endmodule +module mh1 (integer x_inout_wire_integer); +endmodule +`endif +module mh2 (inout integer x_inout_wire_integer); +endmodule +`ifndef VERILATOR +module mh3 ([5:0] x_inout_wire_logic_p6); +endmodule +`endif +module mh5 (input x_input_wire_logic); +endmodule +module mh6 (input var x_input_var_logic); +endmodule +module mh7 (input var integer x_input_var_integer); +endmodule +module mh8 (output x_output_wire_logic); +endmodule +module mh9 (output var x_output_var_logic); +endmodule +module mh10(output signed [5:0] x_output_wire_logic_signed_p6); +endmodule +module mh11(output integer x_output_var_integer); +endmodule +module mh12(ref [5:0] x_ref_logic_p6); +endmodule +module mh13(ref x_ref_var_logic_u6 [5:0]); +endmodule +`ifndef VERILATOR +module mh14(wire x_inout_wire_logic, y_inout_wire_logic_p8 [7:0]); +endmodule +module mh15(integer x_inout_wire_integer, signed [5:0] y_inout_wire_logic_signed6); +endmodule +module mh16([5:0] x_inout_wire_logic_p6, wire y_inout_wire_logic); +endmodule +`endif +module mh17(input var integer x_input_var_integer, wire y_input_wire_logic); +endmodule +module mh18(output var x_output_var_logic, input y_input_wire_logic); +endmodule +module mh19(output signed [5:0] x_output_wire_logic_signed_p6, integer y_output_var_integer); +endmodule +module mh20(ref [5:0] x_ref_var_logic_p6, y_ref_var_logic_p6); +endmodule +module mh21(ref ref_var_logic_u6 [5:0], y_ref_var_logic); +endmodule diff --git a/test_regress/t/t_vpi_cb_iter.cpp b/test_regress/t/t_vpi_cb_iter.cpp index a4a6641cb..b71f33ff6 100644 --- a/test_regress/t/t_vpi_cb_iter.cpp +++ b/test_regress/t/t_vpi_cb_iter.cpp @@ -76,7 +76,7 @@ static int the_rw_callback(p_cb_data cb_data) { static void reregister_value_cb() { if (vh_value_cb) { - if (verbose) { vpi_printf(const_cast("- Removing cbValueChange callback\n")); } + if (verbose) vpi_printf(const_cast("- Removing cbValueChange callback\n")); int ret = vpi_remove_cb(vh_value_cb); vh_value_cb.freed(); CHECK_RESULT(ret, 1); @@ -88,7 +88,7 @@ static void reregister_value_cb() { CHECK_RESULT_NE(main_time, last_value_cb_time); last_value_cb_time = main_time; } - if (verbose) { vpi_printf(const_cast("- Registering cbValueChange callback\n")); } + if (verbose) vpi_printf(const_cast("- Registering cbValueChange callback\n")); t_cb_data cb_data_testcase; bzero(&cb_data_testcase, sizeof(cb_data_testcase)); cb_data_testcase.cb_rtn = the_value_callback; @@ -109,7 +109,7 @@ static void reregister_value_cb() { static void reregister_rw_cb() { if (vh_rw_cb) { - if (verbose) { vpi_printf(const_cast("- Removing cbReadWriteSynch callback\n")); } + if (verbose) vpi_printf(const_cast("- Removing cbReadWriteSynch callback\n")); int ret = vpi_remove_cb(vh_rw_cb); vh_rw_cb.freed(); CHECK_RESULT(ret, 1); @@ -121,7 +121,7 @@ static void reregister_rw_cb() { CHECK_RESULT_NE(main_time, last_rw_cb_time); last_rw_cb_time = main_time; } - if (verbose) { vpi_printf(const_cast("- Registering cbReadWriteSynch callback\n")); } + if (verbose) vpi_printf(const_cast("- Registering cbReadWriteSynch callback\n")); t_cb_data cb_data_testcase; bzero(&cb_data_testcase, sizeof(cb_data_testcase)); cb_data_testcase.cb_rtn = the_rw_callback; @@ -169,7 +169,7 @@ static void register_filler_cb() { double sc_time_stamp() { return main_time; } int main(int argc, char** argv, char** env) { - double sim_time = 100; + vluint64_t sim_time = 100; Verilated::commandArgs(argc, argv); Verilated::debug(0); @@ -184,14 +184,14 @@ int main(int argc, char** argv, char** env) { topp->eval(); topp->clk = 0; - while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { main_time += 1; - if (verbose) { VL_PRINTF("Sim Time %d got_error %d\n", main_time, got_error); } + if (verbose) VL_PRINTF("Sim Time %d got_error %d\n", main_time, got_error); topp->clk = !topp->clk; topp->eval(); VerilatedVpi::callValueCbs(); VerilatedVpi::callCbs(cbReadWriteSynch); - if (got_error) { vl_stop(__FILE__, __LINE__, "TOP-cpp"); } + if (got_error) vl_stop(__FILE__, __LINE__, "TOP-cpp"); } if (!Verilated::gotFinish()) { @@ -200,5 +200,5 @@ int main(int argc, char** argv, char** env) { topp->final(); VL_DO_DANGLING(delete topp, topp); - exit(0L); + return 0; } diff --git a/test_regress/t/t_vpi_cbs_called.cpp b/test_regress/t/t_vpi_cbs_called.cpp index 8a8b929d3..3b81b1840 100644 --- a/test_regress/t/t_vpi_cbs_called.cpp +++ b/test_regress/t/t_vpi_cbs_called.cpp @@ -108,7 +108,7 @@ static int register_cb(const int next_state) { } // State of callback next time through loop - if (verbose) { vpi_printf(const_cast(" Updating callback for next loop:\n")); } + if (verbose) vpi_printf(const_cast(" Updating callback for next loop:\n")); switch (next_state) { case ACTIVE: { if (verbose) { @@ -141,7 +141,7 @@ static int register_cb(const int next_state) { break; } default: - if (verbose) { vpi_printf(const_cast(" - No change\n")); } + if (verbose) vpi_printf(const_cast(" - No change\n")); break; } @@ -161,7 +161,7 @@ static int test_callbacks(p_cb_data cb_data) { t_cb_data cb_data_testcase; bzero(&cb_data_testcase, sizeof(cb_data_testcase)); - if (verbose) { vpi_printf(const_cast(" Checking callback results\n")); } + if (verbose) vpi_printf(const_cast(" Checking callback results\n")); // Check results from previous loop int cb = *cb_iter; @@ -192,7 +192,7 @@ static int test_callbacks(p_cb_data cb_data) { } int ret = register_cb(next_state); - if (ret) { return ret; } + if (ret) return ret; // Update iterators for next loop ++state_iter; @@ -228,7 +228,7 @@ static int register_test_callback() { bzero(&cb_data, sizeof(cb_data)); s_vpi_time t1; - if (verbose) { vpi_printf(const_cast(" Registering test_cbs Timed callback\n")); } + if (verbose) vpi_printf(const_cast(" Registering test_cbs Timed callback\n")); cb_data.reason = cbAfterDelay; t1.type = vpiSimTime; @@ -248,13 +248,13 @@ static int register_test_callback() { double sc_time_stamp() { return main_time; } int main(int argc, char** argv, char** env) { - double sim_time = 100; + vluint64_t sim_time = 100; bool cbs_called; Verilated::commandArgs(argc, argv); VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out - if (verbose) { VL_PRINTF("-- { Sim Time %d } --\n", main_time); } + if (verbose) VL_PRINTF("-- { Sim Time %d } --\n", main_time); register_test_callback(); @@ -262,7 +262,7 @@ int main(int argc, char** argv, char** env) { topp->clk = 0; main_time += 1; - while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { if (verbose) { VL_PRINTF("-- { Sim Time %d , Callback %s (%d) , Testcase State %d } --\n", main_time, cb_reason_to_string(*cb_iter), *cb_iter, *state_iter); @@ -279,7 +279,7 @@ int main(int argc, char** argv, char** env) { } else { cbs_called = VerilatedVpi::callCbs(i); } - if (verbose) { VL_PRINTF(" - any callbacks called? %s\n", cbs_called ? "YES" : "NO"); } + if (verbose) VL_PRINTF(" - any callbacks called? %s\n", cbs_called ? "YES" : "NO"); callbacks_called[i] = cbs_called; } @@ -287,7 +287,7 @@ int main(int argc, char** argv, char** env) { main_time = VerilatedVpi::cbNextDeadline(); if (main_time == -1 && !Verilated::gotFinish()) { - if (verbose) { VL_PRINTF("-- { Sim Time %d , No more testcases } --\n", main_time); } + if (verbose) VL_PRINTF("-- { Sim Time %d , No more testcases } --\n", main_time); if (got_error) { vl_stop(__FILE__, __LINE__, "TOP-cpp"); } else { @@ -308,5 +308,5 @@ int main(int argc, char** argv, char** env) { topp->final(); VL_DO_DANGLING(delete topp, topp); - exit(0L); + return 0; } diff --git a/test_regress/t/t_vpi_finish.pl b/test_regress/t/t_vpi_finish.pl index 050052f66..77affd6a5 100755 --- a/test_regress/t/t_vpi_finish.pl +++ b/test_regress/t/t_vpi_finish.pl @@ -11,7 +11,8 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( - v_flags2 => ["--vpi t/t_vpi_finish_c.cpp"], + v_flags2 => ["t/t_vpi_finish_c.cpp"], + verilator_flags2 => ["--vpi"], ); execute( diff --git a/test_regress/t/t_vpi_get.cpp b/test_regress/t/t_vpi_get.cpp index 2fde87c6b..ba97e83fb 100644 --- a/test_regress/t/t_vpi_get.cpp +++ b/test_regress/t/t_vpi_get.cpp @@ -123,7 +123,7 @@ static int _mon_check_props(TestVpiHandle& handle, int size, int direction, int int vpidir = vpi_get(vpiDirection, handle); // Don't check port directions in verilator // see #681 - if (!TestSimulator::is_verilator()) { CHECK_RESULT(vpidir, direction); } + if (!TestSimulator::is_verilator()) CHECK_RESULT(vpidir, direction); } // check type of object @@ -234,7 +234,7 @@ void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; #else double sc_time_stamp() { return main_time; } int main(int argc, char** argv, char** env) { - double sim_time = 1100; + vluint64_t sim_time = 1100; Verilated::commandArgs(argc, argv); Verilated::debug(0); @@ -258,7 +258,7 @@ int main(int argc, char** argv, char** env) { topp->clk = 0; main_time += 10; - while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { main_time += 1; topp->eval(); VerilatedVpi::callValueCbs(); @@ -278,7 +278,7 @@ int main(int argc, char** argv, char** env) { #endif VL_DO_DANGLING(delete topp, topp); - exit(0L); + return 0; } #endif diff --git a/test_regress/t/t_vpi_get.pl b/test_regress/t/t_vpi_get.pl index dc126dc16..c3f9a81c0 100755 --- a/test_regress/t/t_vpi_get.pl +++ b/test_regress/t/t_vpi_get.pl @@ -23,7 +23,7 @@ compile( ); execute( - iv_pli => 1, + use_libvpi => 1, check_finished => 1 ); diff --git a/test_regress/t/t_vpi_get.v b/test_regress/t/t_vpi_get.v index de893386b..93191301c 100644 --- a/test_regress/t/t_vpi_get.v +++ b/test_regress/t/t_vpi_get.v @@ -9,7 +9,7 @@ `ifdef USE_VPI_NOT_DPI //We call it via $c so we can verify DPI isn't required - see bug572 `else -import "DPI-C" context function integer mon_check(); +import "DPI-C" context function int mon_check(); `endif `ifdef VERILATOR_COMMENTS diff --git a/test_regress/t/t_vpi_get_public_rw_switch.pl b/test_regress/t/t_vpi_get_public_rw_switch.pl index 8c11341ac..3a5cdf231 100755 --- a/test_regress/t/t_vpi_get_public_rw_switch.pl +++ b/test_regress/t/t_vpi_get_public_rw_switch.pl @@ -29,7 +29,7 @@ compile( ); execute( - iv_pli => 1, + use_libvpi => 1, check_finished => 1 ); diff --git a/test_regress/t/t_vpi_memory.cpp b/test_regress/t/t_vpi_memory.cpp index 6478c4b58..c420baebc 100644 --- a/test_regress/t/t_vpi_memory.cpp +++ b/test_regress/t/t_vpi_memory.cpp @@ -245,7 +245,7 @@ void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; double sc_time_stamp() { return main_time; } int main(int argc, char** argv, char** env) { - double sim_time = 1100; + vluint64_t sim_time = 1100; Verilated::commandArgs(argc, argv); Verilated::debug(0); // we're going to be checking for these errors do don't crash out @@ -271,7 +271,7 @@ int main(int argc, char** argv, char** env) { topp->clk = 0; main_time += 10; - while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { main_time += 1; topp->eval(); VerilatedVpi::callValueCbs(); @@ -291,7 +291,7 @@ int main(int argc, char** argv, char** env) { #endif VL_DO_DANGLING(delete topp, topp); - exit(0L); + return 0; } #endif diff --git a/test_regress/t/t_vpi_memory.pl b/test_regress/t/t_vpi_memory.pl index c01c0b9ed..9b95b19c4 100755 --- a/test_regress/t/t_vpi_memory.pl +++ b/test_regress/t/t_vpi_memory.pl @@ -23,7 +23,7 @@ compile( ); execute( - iv_pli => 1, + use_libvpi => 1, check_finished => 1 ); diff --git a/test_regress/t/t_vpi_memory.v b/test_regress/t/t_vpi_memory.v index de2d2b4a7..c16a59cb5 100644 --- a/test_regress/t/t_vpi_memory.v +++ b/test_regress/t/t_vpi_memory.v @@ -9,7 +9,7 @@ `ifdef USE_VPI_NOT_DPI //We call it via $c so we can verify DPI isn't required - see bug572 `else -import "DPI-C" context function integer mon_check(); +import "DPI-C" context function int mon_check(); `endif module t (/*AUTOARG*/ diff --git a/test_regress/t/t_vpi_module.cpp b/test_regress/t/t_vpi_module.cpp index 0d9922f51..7c00dcbf5 100644 --- a/test_regress/t/t_vpi_module.cpp +++ b/test_regress/t/t_vpi_module.cpp @@ -80,6 +80,10 @@ void modDump(const TestVpiHandle& it, int n) { extern "C" { int mon_check() { +#ifdef TEST_VERBOSE + printf("-mon_check()\n"); +#endif + TestVpiHandle it = vpi_iterate(vpiModule, NULL); CHECK_RESULT_NZ(it); // Uncomment to see what other simulators return @@ -167,7 +171,7 @@ void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; double sc_time_stamp() { return main_time; } int main(int argc, char** argv, char** env) { - double sim_time = 1100; + vluint64_t sim_time = 1100; Verilated::commandArgs(argc, argv); Verilated::debug(0); // we're going to be checking for these errors do don't crash out @@ -196,7 +200,7 @@ int main(int argc, char** argv, char** env) { topp->clk = 0; main_time += 10; - while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { main_time += 1; topp->eval(); VerilatedVpi::callValueCbs(); @@ -216,7 +220,7 @@ int main(int argc, char** argv, char** env) { #endif VL_DO_DANGLING(delete topp, topp); - exit(0L); + return 0; } #endif diff --git a/test_regress/t/t_vpi_module.pl b/test_regress/t/t_vpi_module.pl index 69d086d90..bee11c3f0 100755 --- a/test_regress/t/t_vpi_module.pl +++ b/test_regress/t/t_vpi_module.pl @@ -23,7 +23,7 @@ compile( ); execute( - iv_pli => 1, + use_libvpi => 1, check_finished => 1 ); diff --git a/test_regress/t/t_vpi_module.v b/test_regress/t/t_vpi_module.v index e3490e5a4..8d29e6622 100644 --- a/test_regress/t/t_vpi_module.v +++ b/test_regress/t/t_vpi_module.v @@ -9,7 +9,7 @@ `ifdef USE_VPI_NOT_DPI //We call it via $c so we can verify DPI isn't required - see bug572 `else -import "DPI-C" context function integer mon_check(); +import "DPI-C" context function int mon_check(); `endif module t (/*AUTOARG*/ @@ -43,10 +43,10 @@ extern "C" int mon_check(); status = $c32("mon_check()"); `endif `ifdef IVERILOG - status = $mon_check(); + status = $mon_check(); `endif `ifndef USE_VPI_NOT_DPI - status = mon_check(); + status = mon_check(); `endif if (status!=0) begin $write("%%Error: t_vpi_module.cpp:%0d: C Test failed\n", status); diff --git a/test_regress/t/t_vpi_param.cpp b/test_regress/t/t_vpi_param.cpp index 64e54b36f..ac6275b15 100644 --- a/test_regress/t/t_vpi_param.cpp +++ b/test_regress/t/t_vpi_param.cpp @@ -112,7 +112,7 @@ int check_param_int(std::string name, PLI_INT32 format, int exp_value, bool verb } // values - if (verbose) { vpi_printf((PLI_BYTE8*)" Try writing value to %s ...\n", name.c_str()); } + if (verbose) vpi_printf((PLI_BYTE8*)" Try writing value to %s ...\n", name.c_str()); value.value.integer = exp_value; vpi_put_value(param_h, &value, NULL, vpiNoDelay); CHECK_RESULT_NZ(vpi_chk_error(&e)); @@ -120,7 +120,7 @@ int check_param_int(std::string name, PLI_INT32 format, int exp_value, bool verb vpi_printf((PLI_BYTE8*)" vpi_chk_error: %s\n", e.message); } - if (verbose) { vpi_printf((PLI_BYTE8*)" Try reading value of %s ...\n", name.c_str()); } + if (verbose) vpi_printf((PLI_BYTE8*)" Try reading value of %s ...\n", name.c_str()); vpi_get_value(param_h, &value); CHECK_RESULT_NZ(!vpi_chk_error(&e)); if (verbose && vpi_chk_error(&e)) { @@ -166,7 +166,7 @@ int check_param_str(std::string name, PLI_INT32 format, std::string exp_value, b } // values - if (verbose) { vpi_printf((PLI_BYTE8*)" Try writing value to %s ...\n", name.c_str()); } + if (verbose) vpi_printf((PLI_BYTE8*)" Try writing value to %s ...\n", name.c_str()); value.value.str = (PLI_BYTE8*)exp_value.c_str(); vpi_put_value(param_h, &value, NULL, vpiNoDelay); CHECK_RESULT_NZ(vpi_chk_error(&e)); @@ -174,7 +174,7 @@ int check_param_str(std::string name, PLI_INT32 format, std::string exp_value, b vpi_printf((PLI_BYTE8*)" vpi_chk_error: %s\n", e.message); } - if (verbose) { vpi_printf((PLI_BYTE8*)" Try reading value of %s ...\n", name.c_str()); } + if (verbose) vpi_printf((PLI_BYTE8*)" Try reading value of %s ...\n", name.c_str()); vpi_get_value(param_h, &value); CHECK_RESULT_NZ(!vpi_chk_error(&e)); if (verbose && vpi_chk_error(&e)) { @@ -242,7 +242,7 @@ void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; double sc_time_stamp() { return main_time; } int main(int argc, char** argv, char** env) { - double sim_time = 1100; + vluint64_t sim_time = 1100; Verilated::commandArgs(argc, argv); Verilated::debug(0); // we're going to be checking for these errors do don't crash out @@ -268,7 +268,7 @@ int main(int argc, char** argv, char** env) { topp->clk = 0; main_time += 10; - while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { main_time += 1; topp->eval(); VerilatedVpi::callValueCbs(); @@ -288,7 +288,7 @@ int main(int argc, char** argv, char** env) { #endif VL_DO_DANGLING(delete topp, topp); - exit(0L); + return 0; } #endif diff --git a/test_regress/t/t_vpi_param.pl b/test_regress/t/t_vpi_param.pl index f6900846a..e5c448b01 100755 --- a/test_regress/t/t_vpi_param.pl +++ b/test_regress/t/t_vpi_param.pl @@ -23,7 +23,7 @@ compile( ); execute( - iv_pli => 1, + use_libvpi => 1, check_finished => 1 ); diff --git a/test_regress/t/t_vpi_param.v b/test_regress/t/t_vpi_param.v index 082658ff6..4ae1dbb47 100644 --- a/test_regress/t/t_vpi_param.v +++ b/test_regress/t/t_vpi_param.v @@ -9,7 +9,7 @@ `ifdef USE_VPI_NOT_DPI //We call it via $c so we can verify DPI isn't required - see bug572 `else -import "DPI-C" context function integer mon_check(); +import "DPI-C" context function int mon_check(); `endif diff --git a/test_regress/t/t_vpi_release_dup_bad_c.cpp b/test_regress/t/t_vpi_release_dup_bad_c.cpp index 2d05cb61d..1ade9eaac 100644 --- a/test_regress/t/t_vpi_release_dup_bad_c.cpp +++ b/test_regress/t/t_vpi_release_dup_bad_c.cpp @@ -23,6 +23,11 @@ void dpii_check() { // Verilated::scopesDump(); mod = vpi_handle_by_name((PLI_BYTE8*)"top.t", NULL); if (!mod) vpi_printf(const_cast("-- Cannot vpi_find module\n")); +#ifdef VL_NO_LEGACY + vpi_release_handle(mod); + vpi_release_handle(mod); +#else vpi_free_object(mod); // using vpi_free_object instead of vpi_release_handle for coverage vpi_free_object(mod); // error: double free +#endif } diff --git a/test_regress/t/t_vpi_stop_bad.pl b/test_regress/t/t_vpi_stop_bad.pl index 89f7e8b16..061b4561c 100755 --- a/test_regress/t/t_vpi_stop_bad.pl +++ b/test_regress/t/t_vpi_stop_bad.pl @@ -11,11 +11,12 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( - v_flags2 => ["--vpi t/t_vpi_stop_bad_c.cpp"], + v_flags2 => ["t/t_vpi_stop_bad_c.cpp"], + verilator_flags2 => ["--vpi"], ); execute( - fails => 1, + fails => $Self->{vlt_all}, check_finished => 0, expect_filename => $Self->{golden_filename}, ); diff --git a/test_regress/t/t_vpi_time_cb.cpp b/test_regress/t/t_vpi_time_cb.cpp index 54d61a4a1..e3f9748a5 100644 --- a/test_regress/t/t_vpi_time_cb.cpp +++ b/test_regress/t/t_vpi_time_cb.cpp @@ -35,9 +35,6 @@ #include "TestSimulator.h" #include "TestVpi.h" -#define TEST_MSG \ - if (0) printf - unsigned int main_time = 0; unsigned int callback_count_time1 = 3; unsigned int callback_count_time2 = 4; @@ -45,18 +42,6 @@ unsigned int callback_count_start_of_sim = 0; //====================================================================== -#define CHECK_RESULT_VH(got, exp) \ - if ((got) != (exp)) { \ - printf("%%Error: %s:%d: GOT = %p EXP = %p\n", __FILE__, __LINE__, (got), (exp)); \ - return __LINE__; \ - } - -#define CHECK_RESULT_NZ(got) \ - if (!(got)) { \ - printf("%%Error: %s:%d: GOT = NULL EXP = !NULL\n", __FILE__, __LINE__); \ - return __LINE__; \ - } - // Use cout to avoid issues with %d/%lx etc #define CHECK_RESULT(got, exp) \ if ((got) != (exp)) { \ @@ -65,26 +50,6 @@ unsigned int callback_count_start_of_sim = 0; return __LINE__; \ } -#define CHECK_RESULT_HEX(got, exp) \ - if ((got) != (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << hex \ - << ": GOT = " << (got) << " EXP = " << (exp) << std::endl; \ - return __LINE__; \ - } - -#define CHECK_RESULT_CSTR(got, exp) \ - if (strcmp((got), (exp))) { \ - printf("%%Error: %s:%d: GOT = '%s' EXP = '%s'\n", __FILE__, __LINE__, \ - (got) ? (got) : "", (exp) ? (exp) : ""); \ - return __LINE__; \ - } - -#define CHECK_RESULT_CSTR_STRIP(got, exp) CHECK_RESULT_CSTR(got + strspn(got, " "), exp) - -// We cannot replace those with VL_STRINGIFY, not available when PLI is build -#define STRINGIFY(x) STRINGIFY2(x) -#define STRINGIFY2(x) #x - //====================================================================== #ifdef IS_VPI @@ -151,6 +116,10 @@ static int _time_cb2(p_cb_data cb_data) { } static int _start_of_sim_cb(p_cb_data cb_data) { +#ifdef TEST_VERBOSE + printf("-_start_of_sim_cb\n"); +#endif + t_cb_data cb_data_n1, cb_data_n2; bzero(&cb_data_n1, sizeof(cb_data_n1)); bzero(&cb_data_n2, sizeof(cb_data_n2)); @@ -224,7 +193,7 @@ void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; double sc_time_stamp() { return main_time; } int main(int argc, char** argv, char** env) { - double sim_time = 1100; + vluint64_t sim_time = 1100; Verilated::commandArgs(argc, argv); Verilated::debug(0); @@ -243,12 +212,12 @@ int main(int argc, char** argv, char** env) { VL_PRINTF("Enabling waves...\n"); VerilatedVcdC* tfp = new VerilatedVcdC; topp->trace(tfp, 99); - tfp->open(STRINGIFY(TEST_OBJ_DIR) "/simx.vcd"); + tfp->open(VL_STRINGIFY(TEST_OBJ_DIR) "/simx.vcd"); #endif // Load and initialize the PLI application { - const char* filenamep = STRINGIFY(TEST_OBJ_DIR) "/libvpi.so"; + const char* filenamep = VL_STRINGIFY(TEST_OBJ_DIR) "/libvpi.so"; void* lib = dlopen(filenamep, RTLD_LAZY); void* bootstrap = dlsym(lib, "vpi_compat_bootstrap"); if (!bootstrap) { @@ -264,7 +233,7 @@ int main(int argc, char** argv, char** env) { topp->clk = 0; main_time += 1; - while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { main_time += 1; topp->eval(); VerilatedVpi::callValueCbs(); @@ -289,7 +258,7 @@ int main(int argc, char** argv, char** env) { #endif VL_DO_DANGLING(delete topp, topp); - exit(0L); + return 0; } #endif diff --git a/test_regress/t/t_vpi_time_cb.pl b/test_regress/t/t_vpi_time_cb.pl index 0f61b3e1c..98dd60d00 100755 --- a/test_regress/t/t_vpi_time_cb.pl +++ b/test_regress/t/t_vpi_time_cb.pl @@ -8,7 +8,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # Version 2.0. # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -scenarios(simulator => 1, iv => 1); +scenarios(simulator => 1); compile( make_top_shell => 0, @@ -21,8 +21,7 @@ compile( ); execute( - iv_pli => 1, - ms_pli => 1, + use_libvpi => 1, check_finished => 1, all_run_flags => ['+PLUS +INT=1234 +STRSTR'] ); diff --git a/test_regress/t/t_vpi_time_cb.v b/test_regress/t/t_vpi_time_cb.v index a2410c7c5..1ac2d001f 100644 --- a/test_regress/t/t_vpi_time_cb.v +++ b/test_regress/t/t_vpi_time_cb.v @@ -8,110 +8,28 @@ module t (/*AUTOARG*/ // Inputs - input clk + clk ); - -`ifndef VERILATOR - reg clk_r = 0; - always #10 clk_r = ~clk_r; - assign clk = clk_r; -`endif - - reg onebit /*verilator public_flat_rw @(posedge clk) */; - reg [2:1] twoone /*verilator public_flat_rw @(posedge clk) */; - reg [2:1] fourthreetwoone[4:3] /*verilator public_flat_rw @(posedge clk) */; - - reg [61:0] quads[3:2] /*verilator public_flat_rw @(posedge clk) */; + input clk; reg [31:0] count /*verilator public_flat_rd */; - reg [31:0] half_count /*verilator public_flat_rd */; - - reg [7:0] text_byte /*verilator public_flat_rw @(posedge clk) */; - reg [15:0] text_half /*verilator public_flat_rw @(posedge clk) */; - reg [31:0] text_word /*verilator public_flat_rw @(posedge clk) */; - reg [63:0] text_long /*verilator public_flat_rw @(posedge clk) */; - reg [511:0] text /*verilator public_flat_rw @(posedge clk) */; integer status; - sub sub(); - // Test loop initial begin count = 0; - onebit = 1'b0; - fourthreetwoone[3] = 0; // stop icarus optimizing away - text_byte = "B"; - text_half = "Hf"; - text_word = "Word"; - text_long = "Long64b"; - text = "Verilog Test module"; - -/* - if (status!=0) begin - $write("%%Error: t_vpi_var.cpp:%0d: C Test failed\n", status); - $stop; - end - $write("%%Info: Checking results\n"); - if (onebit != 1'b1) $stop; - if (quads[2] != 62'h12819213_abd31a1c) $stop; - if (quads[3] != 62'h1c77bb9b_3784ea09) $stop; - if (text_byte != "A") $stop; - if (text_half != "T2") $stop; - if (text_word != "Tree") $stop; - if (text_long != "44Four44") $stop; - if (text != "lorem ipsum") $stop; - */ end always @(posedge clk) begin +`ifdef TEST_VERBOSE + $display("[%0t] clk", $time); +`endif count <= count + 2; - if (count[1]) - half_count <= half_count + 2; - if (count == 1000) begin -// $write("*-* All Finished *-*\n"); + // See C++ code: $write("*-* All Finished *-*\n"); $finish; end end - genvar i; - generate - for (i=1; i<=128; i=i+1) begin : arr - arr #(.LENGTH(i)) arr(); - end - endgenerate - endmodule : t - -module sub; - reg subsig1 /*verilator public_flat_rd*/; - reg subsig2 /*verilator public_flat_rd*/; -`ifdef IVERILOG - // stop icarus optimizing signals away - wire redundant = subsig1 | subsig2; -`endif -endmodule : sub - -module arr; - - parameter LENGTH = 1; - - reg [LENGTH-1:0] sig /*verilator public_flat_rw*/; - reg [LENGTH-1:0] rfr /*verilator public_flat_rw*/; - - reg check /*verilator public_flat_rw*/; - reg verbose /*verilator public_flat_rw*/; - - initial begin - sig = {LENGTH{1'b0}}; - rfr = {LENGTH{1'b0}}; - end - - always @(posedge check) begin - if (verbose) $display("%m : %x %x", sig, rfr); - if (check && sig != rfr) $stop; - check <= 0; - end - -endmodule : arr diff --git a/test_regress/t/t_vpi_unimpl.cpp b/test_regress/t/t_vpi_unimpl.cpp index 9c7e1ec3a..5706be271 100644 --- a/test_regress/t/t_vpi_unimpl.cpp +++ b/test_regress/t/t_vpi_unimpl.cpp @@ -186,7 +186,7 @@ int mon_check() { double sc_time_stamp() { return main_time; } int main(int argc, char** argv, char** env) { - double sim_time = 1100; + vluint64_t sim_time = 1100; Verilated::commandArgs(argc, argv); Verilated::debug(0); // we're going to be checking for these errors do don't crash out @@ -212,7 +212,7 @@ int main(int argc, char** argv, char** env) { topp->clk = 0; main_time += 10; - while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { main_time += 1; topp->eval(); // VerilatedVpi::callValueCbs(); // Make sure can link without verilated_vpi.h included @@ -233,5 +233,5 @@ int main(int argc, char** argv, char** env) { #endif VL_DO_DANGLING(delete topp, topp); - exit(0L); + return 0; } diff --git a/test_regress/t/t_vpi_unimpl.v b/test_regress/t/t_vpi_unimpl.v index 978303008..ffad82c75 100644 --- a/test_regress/t/t_vpi_unimpl.v +++ b/test_regress/t/t_vpi_unimpl.v @@ -9,7 +9,7 @@ `ifdef VERILATOR //We call it via $c so we can verify DPI isn't required - see bug572 `else -import "DPI-C" context function integer mon_check(); +import "DPI-C" context function int mon_check(); `endif module t (/*AUTOARG*/ diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index b8af76d23..d9ec774b2 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -67,6 +67,12 @@ bool verbose = false; return __LINE__; \ } +#define CHECK_RESULT_Z(got) \ + if ((got)) { \ + printf("%%Error: %s:%d: GOT = !NULL EXP = NULL\n", FILENM, __LINE__); \ + return __LINE__; \ + } + // Use cout to avoid issues with %d/%lx etc #define CHECK_RESULT(got, exp) \ if ((got) != (exp)) { \ @@ -475,7 +481,7 @@ int _mon_check_string() { v.format = vpiStringVal; vpi_get_value(vh1, &v); - if (vpi_chk_error(&e)) { printf("%%vpi_chk_error : %s\n", e.message); } + if (vpi_chk_error(&e)) printf("%%vpi_chk_error : %s\n", e.message); (void)vpi_chk_error(NULL); @@ -568,7 +574,7 @@ int _mon_check_putget_str(p_cb_data cb_data) { TEST_MSG("new value\n"); for (int j = 0; j < 4; j++) { data[i].value.vector[j].aval = rand_r(&seed); - if (j == (words - 1)) { data[i].value.vector[j].aval &= mask; } + if (j == (words - 1)) data[i].value.vector[j].aval &= mask; TEST_MSG(" %08x\n", data[i].value.vector[j].aval); } v.value.vector = data[i].value.vector; @@ -624,6 +630,7 @@ int _mon_check_vlog_info() { CHECK_RESULT_CSTR(vlog_info.argv[1], "+PLUS"); CHECK_RESULT_CSTR(vlog_info.argv[2], "+INT=1234"); CHECK_RESULT_CSTR(vlog_info.argv[3], "+STRSTR"); + CHECK_RESULT_Z(vlog_info.argv[4]); if (TestSimulator::is_verilator()) { CHECK_RESULT_CSTR(vlog_info.product, "Verilator"); CHECK_RESULT(strlen(vlog_info.version) > 0, 1); @@ -633,6 +640,10 @@ int _mon_check_vlog_info() { int mon_check() { // Callback from initial block in monitor +#ifdef TEST_VERBOSE + printf("-mon_check()\n"); +#endif + if (int status = _mon_check_mcd()) return status; if (int status = _mon_check_callbacks()) return status; if (int status = _mon_check_value_callbacks()) return status; @@ -682,7 +693,7 @@ void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; double sc_time_stamp() { return main_time; } int main(int argc, char** argv, char** env) { - double sim_time = 1100; + vluint64_t sim_time = 1100; Verilated::commandArgs(argc, argv); Verilated::debug(0); @@ -706,7 +717,7 @@ int main(int argc, char** argv, char** env) { topp->clk = 0; main_time += 10; - while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { main_time += 1; topp->eval(); VerilatedVpi::callValueCbs(); @@ -730,7 +741,7 @@ int main(int argc, char** argv, char** env) { #endif VL_DO_DANGLING(delete topp, topp); - exit(0L); + return 0; } #endif diff --git a/test_regress/t/t_vpi_var.pl b/test_regress/t/t_vpi_var.pl index 57c6f6cb7..e9f2bac65 100755 --- a/test_regress/t/t_vpi_var.pl +++ b/test_regress/t/t_vpi_var.pl @@ -21,7 +21,7 @@ compile( ); execute( - iv_pli => 1, + use_libvpi => 1, check_finished => 1, all_run_flags => ['+PLUS +INT=1234 +STRSTR'] ); diff --git a/test_regress/t/t_vpi_var.v b/test_regress/t/t_vpi_var.v index 89160dc60..ea2345b87 100644 --- a/test_regress/t/t_vpi_var.v +++ b/test_regress/t/t_vpi_var.v @@ -9,7 +9,7 @@ `ifdef USE_VPI_NOT_DPI //We call it via $c so we can verify DPI isn't required - see bug572 `else -import "DPI-C" context function integer mon_check(); +import "DPI-C" context function int mon_check(); `endif module t (/*AUTOARG*/ @@ -60,10 +60,10 @@ extern "C" int mon_check(); status = $c32("mon_check()"); `endif `ifdef IVERILOG - status = $mon_check(); + status = $mon_check(); `endif `ifndef USE_VPI_NOT_DPI - status = mon_check(); + status = mon_check(); `endif if (status!=0) begin $write("%%Error: t_vpi_var.cpp:%0d: C Test failed\n", status); diff --git a/test_regress/t/t_vpi_zero_time_cb.cpp b/test_regress/t/t_vpi_zero_time_cb.cpp index 00b3a9c28..1c1f15e5d 100644 --- a/test_regress/t/t_vpi_zero_time_cb.cpp +++ b/test_regress/t/t_vpi_zero_time_cb.cpp @@ -35,27 +35,12 @@ #include "TestSimulator.h" #include "TestVpi.h" -#define TEST_MSG \ - if (0) printf - unsigned int main_time = 0; unsigned int callback_count_zero_time = 0; unsigned int callback_count_start_of_sim = 0; //====================================================================== -#define CHECK_RESULT_VH(got, exp) \ - if ((got) != (exp)) { \ - printf("%%Error: %s:%d: GOT = %p EXP = %p\n", __FILE__, __LINE__, (got), (exp)); \ - return __LINE__; \ - } - -#define CHECK_RESULT_NZ(got) \ - if (!(got)) { \ - printf("%%Error: %s:%d: GOT = NULL EXP = !NULL\n", __FILE__, __LINE__); \ - return __LINE__; \ - } - // Use cout to avoid issues with %d/%lx etc #define CHECK_RESULT(got, exp) \ if ((got) != (exp)) { \ @@ -64,22 +49,6 @@ unsigned int callback_count_start_of_sim = 0; return __LINE__; \ } -#define CHECK_RESULT_HEX(got, exp) \ - if ((got) != (exp)) { \ - std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << hex \ - << ": GOT = " << (got) << " EXP = " << (exp) << std::endl; \ - return __LINE__; \ - } - -#define CHECK_RESULT_CSTR(got, exp) \ - if (strcmp((got), (exp))) { \ - printf("%%Error: %s:%d: GOT = '%s' EXP = '%s'\n", __FILE__, __LINE__, \ - (got) ? (got) : "", (exp) ? (exp) : ""); \ - return __LINE__; \ - } - -#define CHECK_RESULT_CSTR_STRIP(got, exp) CHECK_RESULT_CSTR(got + strspn(got, " "), exp) - //====================================================================== #ifdef IS_VPI @@ -90,6 +59,10 @@ static int _zero_time_cb(p_cb_data cb_data) { } static int _start_of_sim_cb(p_cb_data cb_data) { +#ifdef TEST_VERBOSE + printf("-_start_of_sim_cb\n"); +#endif + t_cb_data cb_data_n; bzero(&cb_data_n, sizeof(cb_data_n)); s_vpi_time t; @@ -143,7 +116,7 @@ void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; double sc_time_stamp() { return main_time; } int main(int argc, char** argv, char** env) { - double sim_time = 1100; + vluint64_t sim_time = 1100; Verilated::commandArgs(argc, argv); Verilated::debug(0); @@ -183,7 +156,7 @@ int main(int argc, char** argv, char** env) { topp->clk = 0; main_time += 1; - while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { main_time += 1; topp->eval(); VerilatedVpi::callValueCbs(); @@ -207,7 +180,7 @@ int main(int argc, char** argv, char** env) { #endif VL_DO_DANGLING(delete topp, topp); - exit(0L); + return 0; } #endif diff --git a/test_regress/t/t_vpi_zero_time_cb.pl b/test_regress/t/t_vpi_zero_time_cb.pl index 767fd64a2..d2e13ef9c 100755 --- a/test_regress/t/t_vpi_zero_time_cb.pl +++ b/test_regress/t/t_vpi_zero_time_cb.pl @@ -8,10 +8,9 @@ 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, iv => 1); +scenarios(simulator => 1); compile( - make_top_shell => 0, make_main => 0, make_pli => 1, sim_time => 2100, @@ -21,8 +20,7 @@ compile( ); execute( - iv_pli => 1, - ms_pli => 1, + use_libvpi => 1, check_finished => 1, all_run_flags => ['+PLUS +INT=1234 +STRSTR'] ); diff --git a/test_regress/t/t_vpi_zero_time_cb.v b/test_regress/t/t_vpi_zero_time_cb.v index f88aa8e8c..1ac2d001f 100644 --- a/test_regress/t/t_vpi_zero_time_cb.v +++ b/test_regress/t/t_vpi_zero_time_cb.v @@ -8,110 +8,28 @@ module t (/*AUTOARG*/ // Inputs - input clk + clk ); - -`ifndef VERILATOR - reg clk_r = 0; - always #10 clk_r = ~clk_r; - assign clk = clk_r; -`endif - - reg onebit /*verilator public_flat_rw @(posedge clk) */; - reg [2:1] twoone /*verilator public_flat_rw @(posedge clk) */; - reg [2:1] fourthreetwoone[4:3] /*verilator public_flat_rw @(posedge clk) */; - - reg [61:0] quads[3:2] /*verilator public_flat_rw @(posedge clk) */; + input clk; reg [31:0] count /*verilator public_flat_rd */; - reg [31:0] half_count /*verilator public_flat_rd */; - - reg [7:0] text_byte /*verilator public_flat_rw @(posedge clk) */; - reg [15:0] text_half /*verilator public_flat_rw @(posedge clk) */; - reg [31:0] text_word /*verilator public_flat_rw @(posedge clk) */; - reg [63:0] text_long /*verilator public_flat_rw @(posedge clk) */; - reg [511:0] text /*verilator public_flat_rw @(posedge clk) */; integer status; - sub sub(); - // Test loop initial begin count = 0; - onebit = 1'b0; - fourthreetwoone[3] = 0; // stop icarus optimizing away - text_byte = "B"; - text_half = "Hf"; - text_word = "Word"; - text_long = "Long64b"; - text = "Verilog Test module"; - -/* - if (status!=0) begin - $write("%%Error: t_vpi_var.cpp:%0d: C Test failed\n", status); - $stop; - end - $write("%%Info: Checking results\n"); - if (onebit != 1'b1) $stop; - if (quads[2] != 62'h12819213_abd31a1c) $stop; - if (quads[3] != 62'h1c77bb9b_3784ea09) $stop; - if (text_byte != "A") $stop; - if (text_half != "T2") $stop; - if (text_word != "Tree") $stop; - if (text_long != "44Four44") $stop; - if (text != "lorem ipsum") $stop; - */ end always @(posedge clk) begin +`ifdef TEST_VERBOSE + $display("[%0t] clk", $time); +`endif count <= count + 2; - if (count[1]) - half_count <= half_count + 2; - if (count == 1000) begin -// $write("*-* All Finished *-*\n"); + // See C++ code: $write("*-* All Finished *-*\n"); $finish; end end - genvar i; - generate - for (i=1; i<=128; i=i+1) begin : arr - arr #(.LENGTH(i)) arr(); - end - endgenerate - endmodule : t - -module sub; - reg subsig1 /*verilator public_flat_rd*/; - reg subsig2 /*verilator public_flat_rd*/; -`ifdef IVERILOG - // stop icarus optimizing signals away - wire redundant = subsig1 | subsig2; -`endif -endmodule : sub - -module arr; - - parameter LENGTH = 1; - - reg [LENGTH-1:0] sig /*verilator public_flat_rw*/; - reg [LENGTH-1:0] rfr /*verilator public_flat_rw*/; - - reg check /*verilator public_flat_rw*/; - reg verbose /*verilator public_flat_rw*/; - - initial begin - sig = {LENGTH{1'b0}}; - rfr = {LENGTH{1'b0}}; - end - - always @(posedge check) begin - if (verbose) $display("%m : %x %x", sig, rfr); - if (check && sig != rfr) $stop; - check <= 0; - end - -endmodule : arr diff --git a/test_regress/t/t_wrapper_legacy.cpp b/test_regress/t/t_wrapper_legacy.cpp new file mode 100644 index 000000000..d93356a6a --- /dev/null +++ b/test_regress/t/t_wrapper_legacy.cpp @@ -0,0 +1,108 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2020 by Wilson Snyder and Marlon James. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#include VM_PREFIX_INCLUDE + +#include +#include +#include +#include + +bool got_error = false; + +// Use cout to avoid issues with %d/%lx etc +#define CHECK_RESULT(got, exp) \ + if ((got) != (exp)) { \ + std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << ": GOT = " << (got) \ + << " EXP = " << (exp) << std::endl; \ + got_error = true; \ + } +#define CHECK_RESULT_CSTR(got, exp) \ + if (strcmp((got), (exp))) { \ + printf("%%Error: %s:%d: GOT = '%s' EXP = '%s'\n", __FILE__, __LINE__, \ + (got) ? (got) : "", (exp) ? (exp) : ""); \ + return __LINE__; \ + } + +vluint64_t main_time = 0; + +#ifdef T_WRAPPER_LEGACY_TIME64 +vluint64_t vl_time_stamp64() { return main_time; } +#endif +#ifdef T_WRAPPER_LEGACY +double sc_time_stamp() { return main_time; } +#endif + +int main(int argc, char** argv, char** env) { + // Test that the old non-context Verilated:: calls all work + // (This test should never get updated to use context) + + // Many used only by git@github.com:djg/verilated-rs.git + + Verilated::commandArgs(argc, argv); // Commonly used + CHECK_RESULT_CSTR(Verilated::commandArgsPlusMatch("not-matching"), ""); + + Verilated::assertOn(true); + CHECK_RESULT(Verilated::assertOn(), true); + + Verilated::calcUnusedSigs(true); + CHECK_RESULT(Verilated::calcUnusedSigs(), true); + + Verilated::debug(9); // Commonly used + CHECK_RESULT(Verilated::debug(), 9); + Verilated::debug(0); + + Verilated::fatalOnVpiError(true); + CHECK_RESULT(Verilated::fatalOnVpiError(), true); + + Verilated::gotFinish(false); + CHECK_RESULT(Verilated::gotFinish(), false); // Commonly used + + Verilated::mkdir(VL_STRINGIFY(TEST_OBJ_DIR) "/mkdired"); + + Verilated::randReset(0); + CHECK_RESULT(Verilated::randReset(), 0); + + Verilated::traceEverOn(true); // Commonly used + + CHECK_RESULT_CSTR(Verilated::productName(), Verilated::productName()); + CHECK_RESULT_CSTR(Verilated::productVersion(), Verilated::productVersion()); + + if (Verilated::timeunit()) {} + if (Verilated::timeprecision()) {} + + VM_PREFIX* topp = new VM_PREFIX(); + + topp->eval(); + topp->clk = 0; + + VL_PRINTF("Starting\n"); + + vluint64_t sim_time = 100; + while (vl_time_stamp64() < sim_time && !Verilated::gotFinish()) { + CHECK_RESULT(VL_TIME_Q(), main_time); + CHECK_RESULT(VL_TIME_D(), main_time); + main_time += 1; + topp->clk = !topp->clk; + topp->eval(); + } + + topp->final(); + Verilated::flushCall(); + Verilated::runFlushCallbacks(); + + Verilated::internalsDump(); + Verilated::scopesDump(); + + VL_DO_DANGLING(delete topp, topp); + Verilated::runExitCallbacks(); + return got_error ? 10 : 0; +} diff --git a/test_regress/t/t_wrapper_legacy.pl b/test_regress/t/t_wrapper_legacy.pl new file mode 100755 index 000000000..293973bb4 --- /dev/null +++ b/test_regress/t/t_wrapper_legacy.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 2020 by Wilson Snyder and Marlon James. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt_all => 1); + +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_wrapper_legacy.v b/test_regress/t/t_wrapper_legacy.v new file mode 100644 index 000000000..c6fd4a2e0 --- /dev/null +++ b/test_regress/t/t_wrapper_legacy.v @@ -0,0 +1,24 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2021 Wilson Snyder and Marlon James. +// SPDX-License-Identifier: CC0-1.0 + + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + int count; + + always @(posedge clk) begin + count <= count + 1; + if (count == 10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule : t diff --git a/test_regress/t/t_wrapper_legacy_time64.pl b/test_regress/t/t_wrapper_legacy_time64.pl new file mode 100755 index 000000000..ad170fa1e --- /dev/null +++ b/test_regress/t/t_wrapper_legacy_time64.pl @@ -0,0 +1,27 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2020 by Wilson Snyder and Marlon James. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt_all => 1); + +top_filename("t/t_wrapper_legacy.v"); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe $Self->{t_dir}/t_wrapper_legacy.cpp"], + make_flags => 'CPPFLAGS_ADD=-DVL_TIME_STAMP64', + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_x_assign.cpp b/test_regress/t/t_x_assign.cpp index 502301c7c..9ab268a4f 100644 --- a/test_regress/t/t_x_assign.cpp +++ b/test_regress/t/t_x_assign.cpp @@ -14,6 +14,8 @@ #include "verilated.h" #include VM_PREFIX_INCLUDE +double sc_time_stamp() { return 0; } + // clang-format off #if defined(T_X_ASSIGN_0) # define EXPECTED 0 diff --git a/test_regress/t/t_xml_flat_no_inline_mod.out b/test_regress/t/t_xml_flat_no_inline_mod.out new file mode 100644 index 000000000..803aa990b --- /dev/null +++ b/test_regress/t/t_xml_flat_no_inline_mod.out @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_regress/t/t_xml_flat_no_inline_mod.pl b/test_regress/t/t_xml_flat_no_inline_mod.pl new file mode 100755 index 000000000..9c51c5674 --- /dev/null +++ b/test_regress/t/t_xml_flat_no_inline_mod.pl @@ -0,0 +1,25 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2012 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt => 1); + +my $out_filename = "$Self->{obj_dir}/V$Self->{name}.xml"; + +compile( + verilator_flags2 => ['--xml-only', '--flatten'], + verilator_make_gmake => 0, + make_top_shell => 0, + make_main => 0, + ); + +files_identical("$out_filename", $Self->{golden_filename}); + +ok(1); +1; diff --git a/test_regress/t/t_xml_flat_no_inline_mod.v b/test_regress/t/t_xml_flat_no_inline_mod.v new file mode 100644 index 000000000..cf87b71a0 --- /dev/null +++ b/test_regress/t/t_xml_flat_no_inline_mod.v @@ -0,0 +1,13 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2008 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module foo(input logic i_clk); /* verilator no_inline_module */ +endmodule + +// --flatten forces inlining of 'no_inline_module' module foo. +module top(input logic i_clk); + foo f(.*); +endmodule diff --git a/test_regress/t/t_xml_flat_pub_mod.out b/test_regress/t/t_xml_flat_pub_mod.out new file mode 100644 index 000000000..5515cd69c --- /dev/null +++ b/test_regress/t/t_xml_flat_pub_mod.out @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_regress/t/t_xml_flat_pub_mod.pl b/test_regress/t/t_xml_flat_pub_mod.pl new file mode 100755 index 000000000..9c51c5674 --- /dev/null +++ b/test_regress/t/t_xml_flat_pub_mod.pl @@ -0,0 +1,25 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2012 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt => 1); + +my $out_filename = "$Self->{obj_dir}/V$Self->{name}.xml"; + +compile( + verilator_flags2 => ['--xml-only', '--flatten'], + verilator_make_gmake => 0, + make_top_shell => 0, + make_main => 0, + ); + +files_identical("$out_filename", $Self->{golden_filename}); + +ok(1); +1; diff --git a/test_regress/t/t_xml_flat_pub_mod.v b/test_regress/t/t_xml_flat_pub_mod.v new file mode 100644 index 000000000..4fa40e587 --- /dev/null +++ b/test_regress/t/t_xml_flat_pub_mod.v @@ -0,0 +1,13 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2008 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module foo(input logic i_clk); /* verilator public_module */ +endmodule + +// --flatten forces inlining of public module foo. +module top(input logic i_clk); + foo f(.*); +endmodule