diff --git a/Changes b/Changes index 5d5ab3be9a..db6a674f58 100644 --- a/Changes +++ b/Changes @@ -20,9 +20,15 @@ Verilator 5.033 devel * Support generated classes (#5665). [Shou-Li Hsu] * Support constraints on associative array user-defined keys (#5671) (#5729). [Yilou Wang] * Support `+incdir` with multiple directories. +* Support integer atom type ports in `--hierarchical` (#5748). [Bartłomiej Chmiel, Antmicro Ltd.] * Support side effects of form 'variable[index_function()]++'. +* Improve hierarchical DPI wrapper scheduling performance (#2583) (#5734). [Bartłomiej Chmiel, Antmicro Ltd.] * Improve VPI write errors (#5712). [Andrew Nolte] +* Improve `resetall support (#5728) (#5730). [Ethan Sifferman] +* Optimize labels as final `if` block statements (#5744). +* Optimize empty function definition bodies (#5750). * Fix error message when call task as a function (#3089). [Matthew Ballance] +* Fix VPI iteration over hierarchy (#5314) (#5731). [Natan Kreimer] * Fix constrained random for > 64-bit associative arrays (#5670) (#5682). [Yilou Wang] * Fix mis-optimizing away `$urandom` (#5703). [Parker Schless] * Fix packages with `--public-depth 1` (#5708). [Andrew Nolte] @@ -32,6 +38,10 @@ Verilator 5.033 devel * Fix %p format output for real inside struct (#5713). * Fix segfault when only enum value referenced in package (#5714). [Dan Katz] * Fix `BLKSEQ` on suspendable processes (#5722). [Krzysztof Bieganski, Antmicro Ltd.] +* Fix vpiDefName issues with non-inlined scopes and dpi conflicts (#5732). [Andrew Nolte] +* Fix inlined expression with assignment under LHS of NBA (#5736) (#5740). [Geza Lore] +* Fix duplicate-named class variable equivalence (#5737). +* Fix `-j` ignored after `-f` (#5749). [Luca Colagrande] * Fix matching language extension options including dots. diff --git a/bin/verilator b/bin/verilator index 756344a253..e2c3c343a9 100755 --- a/bin/verilator +++ b/bin/verilator @@ -374,8 +374,8 @@ detailed descriptions of these arguments. --gdb Run Verilator under GDB interactively --gdbbt Run Verilator under GDB for backtrace --generate-key Create random key for --protect-key - --getenv Get environment variable with defaults --get-supported Get if feature is supported + --getenv Get environment variable with defaults --help Show this help --hierarchical Enable hierarchical Verilation --hierarchical-params-file Internal option that specifies parameters file for hier blocks @@ -385,6 +385,11 @@ detailed descriptions of these arguments. --inline-mult Tune module inlining --instr-count-dpi Assumed dynamic instruction count of DPI imports -j Parallelism for --build-jobs/--verilate-jobs + --no-json-edit-nums Don't dump editNum in .tree.json files + --no-json-ids Don't use short identifiers instead of adresses/paths in .tree.json + --json-only Create JSON parser output (.tree.json and .meta.json) + --json-only-output .tree.json output filename + --json-only-meta-output .tree.meta.json output filename --l2-name Verilog scope name of the top module --language Default language standard to parse -LDFLAGS Linker pre-object arguments for makefile @@ -421,6 +426,7 @@ detailed descriptions of these arguments. --pipe-filter Filter all input through a script --pp-comments Show preprocessor comments with -E --prefix Name of top-level class + --preproc-token-limit Maximum tokens on a line allowed by preprocessor --private Debugging; see docs --prof-c Compile C++ code with profiling --prof-cfuncs Name functions for profiling @@ -506,11 +512,6 @@ detailed descriptions of these arguments. --x-assign Assign non-initial Xs to this value --x-initial Assign initial Xs to this value --x-initial-edge Enable initial X->0 and X->1 edge triggers - --no-json-edit-nums Don't dump editNum in .tree.json files - --no-json-ids Don't use short identifiers instead of adresses/paths in .tree.json - --json-only Create JSON parser output (.tree.json and .meta.json) - --json-only-output .tree.json output filename - --json-only-meta-output .tree.meta.json output filename --xml-only Create XML parser output --xml-output XML output filename -y Directory to search for modules diff --git a/configure.ac b/configure.ac index 4420a1f7f4..4d815fc0b4 100644 --- a/configure.ac +++ b/configure.ac @@ -478,6 +478,7 @@ m4_foreach([cflag],[ [-Wno-parentheses-equality], [-Wno-shadow], [-Wno-sign-compare], + [-Wno-subobject-linkage], [-Wno-tautological-bitwise-compare], [-Wno-tautological-compare], [-Wno-uninitialized], @@ -538,7 +539,7 @@ _MY_LDLIBS_CHECK_IFELSE( [if test "$CFG_WITH_TCMALLOC" != "no"; then CFG_LIBS="$LTCMALLOC $CFG_LIBS"; # If using tcmalloc, add some extra options to make the compiler not assume - # it is using it's own versions of the standard library functions + # it is using its own versions of the standard library functions _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-malloc) _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-calloc) _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-realloc) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index da6fa55899..9fd2ac91ea 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -41,6 +41,7 @@ David Metz David Stanford David Turner Dercury +Diego Roux Don Williamson Drew Ranck Drew Taussig @@ -99,6 +100,7 @@ Jesse Taube Jevin Sweval Jiacheng Qian Jiamin Zhu +Jitesh Nayak Jinyan Xu Jiuyang Liu Joey Liu @@ -155,6 +157,7 @@ Miodrag Milanović Mladen Slijepcevic Morten Borup Petersen Mostafa Gamal +Moubarak Jeje Nandu Raj Natan Kreimer Nathan Graybeal diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index d9b4075ab9..61a99b23e0 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -782,6 +782,38 @@ Summary: the number of threads in the current hardware. Otherwise, must be a positive integer specifying the maximum number of parallel build jobs. +.. option:: --json-only + + Create JSON output only, do not create any other output. + + The JSON format is intended to be used to leverage Verilator's parser and + elaboration to feed to other downstream tools. For details on the format, see + the Verilator Internals manual. Be aware that the JSON + format is still evolving; there will be some changes in future versions. + + This option disables some more aggressive transformations and dumps only + the final state of the AST. For more granular and unaltered dumps, meant + mainly for debugging see :vlopt:`--dump-tree-json`. + +.. option:: --json-only-meta-output + + Specifies the filename for the metadata output file (`.tree.meta.json`) of --json-only. + Using this option automatically sets :vlopt:`--json-only`. + +.. option:: --json-only-output + + Specifies the filename for the main output file (`.tree.json`) of --json-only. + Using this option automatically sets :vlopt:`--json-only`. + +.. option:: --no-json-edit-nums + + Don't dump edit number in .tree.json files. This may make the file more + run-to-run stable for easier comparison. + +.. option:: --no-json-ids + + Don't use short identifiers instead of addresses/paths in .tree.json. + .. option:: --l2-name Instead of using the module name when showing Verilog scope, use the @@ -1110,6 +1142,12 @@ Summary: prepended to the name of the :vlopt:`--top` option, or V prepended to the first Verilog filename passed on the command line. +.. option:: --preproc-token-limit + + Rarely needed. Configure the limit of the number of tokens Verilator + can process on a single line to prevent infinite loops and other hangs. + Defaults to 40000 tokens. + .. option:: --private Opposite of :vlopt:`--public`. This is the default; this option exists for @@ -1893,38 +1931,6 @@ Summary: iterations. This may be another indication of problems with the modeled design that should be addressed. -.. option:: --json-only - - Create JSON output only, do not create any other output. - - The JSON format is intended to be used to leverage Verilator's parser and - elaboration to feed to other downstream tools. For details on the format, see - the Verilator Internals manual. Be aware that the JSON - format is still evolving; there will be some changes in future versions. - - This option disables some more aggressive transformations and dumps only - the final state of the AST. For more granular and unaltered dumps, meant - mainly for debugging see :vlopt:`--dump-tree-json`. - -.. option:: --json-only-meta-output - - Specifies the filename for the metadata output file (`.tree.meta.json`) of --json-only. - Using this option automatically sets :vlopt:`--json-only`. - -.. option:: --json-only-output - - Specifies the filename for the main output file (`.tree.json`) of --json-only. - Using this option automatically sets :vlopt:`--json-only`. - -.. option:: --no-json-edit-nums - - Don't dump edit number in .tree.json files. This may make the file more - run-to-run stable for easier comparison. - -.. option:: --no-json-ids - - Don't use short identifiers instead of addresses/paths in .tree.json. - .. option:: --xml-only Create XML output only, do not create any other output. @@ -2154,6 +2160,13 @@ The grammar of configuration commands is as follows: :option:`/*verilator&32;public_flat*/`, etc., metacomments. See also :ref:`VPI Example`. +.. option:: profile_data -hier-dpi "" -cost + + Internal profiling data inserted during :vlopt:`--hierarchical`; specifies + execution cost of a hierarchical DPI wrappers for modules with + :option:`/*verilator&32;hier_block*/` metacomment. See + :ref:`Hierarchical Verilation`. + .. option:: profile_data -mtask "" -cost Feeds profile-guided optimization data into the Verilator algorithms in diff --git a/include/verilated.h b/include/verilated.h index baccd06105..d832eb7abc 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -175,6 +175,9 @@ enum class VerilatedAssertDirectiveType : uint8_t { using VerilatedAssertType_t = std::underlying_type::type; using VerilatedAssertDirectiveType_t = std::underlying_type::type; +// Type trait for custom struct +template +struct VlIsCustomStruct : public std::false_type {}; //============================================================================= // Utility functions diff --git a/include/verilated_random.cpp b/include/verilated_random.cpp index a525df5c4d..7e048e6c1f 100644 --- a/include/verilated_random.cpp +++ b/include/verilated_random.cpp @@ -508,7 +508,7 @@ void VlRandomizer::clear() { m_constraints.clear(); } #ifdef VL_DEBUG void VlRandomizer::dump() const { for (const auto& var : m_vars) { - VL_PRINTF("Variable (%d): %s\n", var.second->width(), var.second->name()); + VL_PRINTF("Variable (%d): %s\n", var.second->width(), var.second->name().c_str()); } for (const std::string& c : m_constraints) VL_PRINTF("Constraint: %s\n", c.c_str()); } diff --git a/include/verilated_random.h b/include/verilated_random.h index 24ba843852..7b1eaabbbd 100644 --- a/include/verilated_random.h +++ b/include/verilated_random.h @@ -54,21 +54,22 @@ class ArrayInfo final { using ArrayInfoMap = std::map>; class VlRandomVar VL_NOT_FINAL { - const char* const m_name; // Variable name + std::string m_name; // Variable name void* const m_datap; // Reference to variable data const int m_width; // Variable width in bits const int m_dimension; //Variable dimension, default is 0 const std::uint32_t m_randModeIdx; // rand_mode index public: - VlRandomVar(const char* name, int width, void* datap, int dimension, std::uint32_t randModeIdx) + VlRandomVar(const std::string& name, int width, void* datap, int dimension, + std::uint32_t randModeIdx) : m_name{name} , m_datap{datap} , m_width{width} , m_dimension{dimension} , m_randModeIdx{randModeIdx} {} virtual ~VlRandomVar() = default; - const char* name() const { return m_name; } + std::string name() const { return m_name; } int width() const { return m_width; } int dimension() const { return m_dimension; } virtual void* datap(int idx) const { return m_datap; } @@ -99,7 +100,7 @@ class VlRandomVar VL_NOT_FINAL { template class VlRandomArrayVarTemplate final : public VlRandomVar { public: - VlRandomArrayVarTemplate(const char* name, int width, void* datap, int dimension, + VlRandomArrayVarTemplate(const std::string& name, int width, void* datap, int dimension, std::uint32_t randModeIdx) : VlRandomVar{name, width, datap, dimension, randModeIdx} {} void* datap(int idx) const override { @@ -300,8 +301,9 @@ class VlRandomizer final { } template - void write_var(T& var, int width, const char* name, int dimension, - std::uint32_t randmodeIdx = std::numeric_limits::max()) { + typename std::enable_if::value, void>::type + write_var(T& var, int width, const char* name, int dimension, + std::uint32_t randmodeIdx = std::numeric_limits::max()) { if (m_vars.find(name) != m_vars.end()) return; // TODO: make_unique once VlRandomizer is per-instance not per-ref m_vars[name] @@ -341,6 +343,22 @@ class VlRandomizer final { record_arr_table(var, name, dimension, {}, {}); } } + template + void modifyMembers(T& obj, std::index_sequence, std::string baseName) { + // Use the indices to access each member via std::get + (void)std::initializer_list{ + (write_var(std::get(obj.getMembers(obj)), + sizeof(std::get(obj.getMembers(obj))) * 8, + (baseName + "." + obj.memberNames()[I]).c_str(), 0), + 0)...}; + } + + template + typename std::enable_if::value, void>::type + write_var(T& var, int width, const char* name, int dimension, + std::uint32_t randmodeIdx = std::numeric_limits::max()) { + modifyMembers(var, var.memberIndices(), name); + } int idx; std::string generateKey(const std::string& name, int idx) { @@ -355,8 +373,9 @@ class VlRandomizer final { } template - void record_arr_table(T& var, const std::string name, int dimension, - std::vector indices, std::vector idxWidths) { + typename std::enable_if::value, void>::type + record_arr_table(T& var, const std::string name, int dimension, std::vector indices, + std::vector idxWidths) { const std::string key = generateKey(name, idx); m_arr_vars[key] = std::make_shared(name, &var, idx, indices, idxWidths); ++idx; diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 53f953564d..ac885332dd 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -221,6 +221,34 @@ class VerilatedVpioVarBase VL_NOT_FINAL : public VerilatedVpio { const char* name() const override { return m_varp->name(); } const char* fullname() const override { return m_fullname.c_str(); } virtual void* varDatap() const { return m_varp->datap(); } + CData* varCDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT8);); + return reinterpret_cast(varDatap()); + } + SData* varSDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT16);); + return reinterpret_cast(varDatap()); + } + IData* varIDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT32);); + return reinterpret_cast(varDatap()); + } + QData* varQDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT64);); + return reinterpret_cast(varDatap()); + } + EData* varEDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_WDATA);); + return reinterpret_cast(varDatap()); + } + double* varRealDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_REAL);); + return reinterpret_cast(varDatap()); + } + std::string* varStringDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_STRING);); + return reinterpret_cast(varDatap()); + } virtual uint32_t bitOffset() const { return 0; } }; @@ -2584,7 +2612,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { } } else if (valuep->format == vpiBinStrVal) { t_outDynamicStr.resize(varBits); - const CData* datap = (reinterpret_cast(varDatap)); + const CData* datap = reinterpret_cast(varDatap); for (size_t i = 0; i < varBits; ++i) { const size_t pos = i + vop->bitOffset(); const char val = (datap[pos >> 3] >> (pos & 7)) & 1; @@ -2632,7 +2660,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { valuep->value.str = reinterpret_cast(varDatap); return; } else { - t_outDynamicStr = *(reinterpret_cast(varDatap)); + t_outDynamicStr = *(vop->varStringDatap()); valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; } @@ -2651,7 +2679,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { valuep->value.integer = vl_vpi_get_word(vop, 32, 0); return; } else if (valuep->format == vpiRealVal) { - valuep->value.real = *(reinterpret_cast(varDatap)); + valuep->value.real = *(vop->varRealDatap()); return; } else if (valuep->format == vpiSuppressVal) { return; @@ -2739,7 +2767,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ } } else if (valuep->format == vpiBinStrVal) { const int len = std::strlen(valuep->value.str); - CData* const datap = (reinterpret_cast(vop->varDatap())); + CData* const datap = reinterpret_cast(vop->varDatap()); for (int i = 0; i < varBits; ++i) { const bool set = (i < len) && (valuep->value.str[len - i - 1] == '1'); const size_t pos = vop->bitOffset() + i; @@ -2819,7 +2847,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ return object; } else if (valuep->format == vpiStringVal) { if (vop->varp()->vltype() == VLVT_STRING) { - *(reinterpret_cast(vop->varDatap())) = valuep->value.str; + *(vop->varStringDatap()) = valuep->value.str; return object; } else { const int chars = VL_BYTES_I(varBits); @@ -2836,7 +2864,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ return object; } else if (valuep->format == vpiRealVal) { if (vop->varp()->vltype() == VLVT_REAL) { - *(reinterpret_cast(vop->varDatap())) = valuep->value.real; + *(vop->varRealDatap()) = valuep->value.real; return object; } } @@ -2856,13 +2884,637 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ return nullptr; } -void vpi_get_value_array(vpiHandle /*object*/, p_vpi_arrayvalue /*arrayvalue_p*/, - PLI_INT32* /*index_p*/, PLI_UINT32 /*num*/) { - VL_VPI_UNIMP_(); +bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arrayvalue_p, + const char* fullname) { + if (arrayvalue_p->format == vpiVectorVal) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: + case VLVT_UINT64: + case VLVT_WDATA: return true; + default:; + } + } else if (arrayvalue_p->format == vpiIntVal) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: return true; + default:; + } + } else if ((arrayvalue_p->format == vpiRawTwoStateVal) + || (arrayvalue_p->format == vpiRawFourStateVal)) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: + case VLVT_UINT64: + case VLVT_WDATA: return true; + default:; + } + } else if (arrayvalue_p->format == vpiShortIntVal) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: return true; + default:; + } + } else if (arrayvalue_p->format == vpiLongIntVal) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: + case VLVT_UINT64: return true; + default:; + } + } + + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__, + VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), fullname); + + return false; } -void vpi_put_value_array(vpiHandle /*object*/, p_vpi_arrayvalue /*arrayvalue_p*/, - PLI_INT32* /*index_p*/, PLI_UINT32 /*num*/) { - VL_VPI_UNIMP_(); + +template +void vl_get_value_array_integrals(unsigned index, const unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, const T* src, + K* dst) { + static_assert(sizeof(K) >= sizeof(T), "size of type K is less than size of type T"); + for (int i = 0; i < num; i++) { + dst[i] = src[index]; + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } +} + +template +void vl_put_value_array_integrals(unsigned index, const unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, const T* src, + K* dst) { + static_assert(std::is_integral::value, "type T is not an integral type"); + static_assert(std::is_unsigned::value, "type T is not unsigned"); + static_assert(sizeof(T) >= sizeof(K), "size of type T is less than size of type K"); + const unsigned element_size_bytes = VL_BYTES_I(packedSize); + const T mask = element_size_bytes == sizeof(T) + ? static_cast(-1) + : ~(static_cast(-1) << (element_size_bytes * 8)); + for (unsigned i = 0; i < num; i++) { + dst[index] = src[i] & static_cast(mask); + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } +} + +template +void vl_get_value_array_vectors(unsigned index, const unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, const T* src, + p_vpi_vecval dst) { + static_assert(std::is_unsigned::value, + "type T is not unsigned"); // ensure logical right shift + const unsigned element_size_bytes = VL_BYTES_I(packedSize); + const unsigned element_size_words = VL_WORDS_I(packedSize); + const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T); + if (sizeof(T) == sizeof(QData)) { + for (unsigned i = 0; i < num; i++) { + dst[i * 2].aval = static_cast(src[index]); + dst[i * 2].bval = 0; + dst[(i * 2) + 1].aval = static_cast(src[index]) >> 32; + dst[(i * 2) + 1].bval = 0; + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } + } else { + for (unsigned i = 0; i < num; i++) { + const size_t dst_index = i * element_size_words; + const size_t src_index = index * element_size_words; + for (unsigned j = 0; j < element_size_words; j++) { + dst[dst_index + j].aval = src[src_index + j]; + dst[dst_index + j].bval = 0; + } + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } + } +} + +template +void vl_put_value_array_vectors(unsigned index, const unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, + const bool fourState, const p_vpi_vecval src, T* dst) { + static_assert(std::is_unsigned::value, "type T is not unsigned"); + static_assert(std::is_integral::value, "type T is not an integral type"); + const unsigned element_size_bytes VL_BYTES_I(packedSize); + const unsigned element_size_words VL_WORDS_I(packedSize); + if (sizeof(T) == sizeof(QData)) { //destination is QDATA + const QData mask = element_size_bytes == sizeof(T) + ? static_cast(-1) + : ~(static_cast(-1) << (element_size_bytes * 8)); + for (unsigned i = 0; i < num; i++) { + dst[index] = src[i * 2].aval; + dst[index] + |= (static_cast(src[(i * 2) + 1].aval) << (sizeof(PLI_UINT32) * 8)) & mask; + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } + } else { + for (unsigned i = 0; i < num; i++) { + unsigned bytes_stored = 0; + for (unsigned j = 0; j < element_size_words; j++) { + if (bytes_stored >= element_size_bytes) break; + const T mask + = (element_size_bytes - bytes_stored) >= sizeof(PLI_UINT32) + ? static_cast(-1) + : ~(static_cast(-1) << ((element_size_bytes - bytes_stored) * 8)); + dst[(index * element_size_words) + j] + = static_cast(src[(i * element_size_words) + j].aval) & mask; + bytes_stored += sizeof(PLI_UINT32); + } + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } + } +} + +template +void vl_get_value_array_rawvals(unsigned index, unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, + const bool fourState, const T* src, PLI_BYTE8* dst) { + static_assert(std::is_unsigned::value, + "type T is not unsigned"); //ensure loigcal right shift + const unsigned element_size_bytes VL_BYTES_I(packedSize); + const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T); + size_t dst_index = 0; + while (num-- > 0) { + const size_t src_offset = index * element_size_repr; + unsigned bytes_copied = 0; + for (unsigned j = 0; j < element_size_repr; j++) { + const T& src_data = src[src_offset + j]; + for (unsigned k = 0; k < sizeof(T); k++) { + if (bytes_copied++ == element_size_bytes) break; + dst[dst_index++] = src_data >> (k * 8); + } + } + if (fourState) { + std::fill(dst + dst_index, dst + dst_index + element_size_bytes, 0); + dst_index += element_size_bytes; + } + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } +} + +template +void vl_put_value_array_rawvals(unsigned index, const unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, + const bool fourState, const PLI_UBYTE8* src, T* dst) { + const unsigned element_size_bytes VL_BYTES_I(packedSize); + const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T); + for (unsigned i = 0; i < num; i++) { + unsigned bytes_copied = 0; + const size_t dst_offset = index * element_size_repr; + const size_t src_offset = i * element_size_bytes; + for (unsigned j = 0; j < element_size_repr; j++) { + T& dst_data = dst[dst_offset + j]; + for (unsigned k = 0; k < sizeof(T); k++) { + if (bytes_copied == element_size_bytes) break; + const unsigned src_index + = fourState ? (src_offset * 2) + bytes_copied : (src_offset) + bytes_copied; + dst_data &= ~((static_cast(0xFF) & 0xFF) << (k * 8)); + dst_data |= ((static_cast(src[src_index]) & 0xFF) << (k * 8)); + bytes_copied++; + } + } + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } +} + +void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p, + PLI_UINT32 num) { + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); + if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return; + + const VerilatedVar* const varp = vop->varp(); + + static thread_local EData out_data[VL_VALUE_STRING_MAX_WORDS * 2]; + + const unsigned size = vop->size(); + if (VL_UNCOVERABLE(num > size)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: requested elements (%u) exceed array size (%u)", + __func__, num, size); + return; + } + + const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low(); + int index + = leftIsLow ? index_p[0] - vop->rangep()->left() : vop->rangep()->left() - index_p[0]; + + if (arrayvalue_p->format == vpiShortIntVal) { + if (VL_UNCOVERABLE((sizeof(PLI_INT16) * num) >= VL_VALUE_STRING_MAX_CHARS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + PLI_INT16* shortintsp = (PLI_INT16*)out_data; + arrayvalue_p->value.shortints = shortintsp; + + if (varp->vltype() == VLVT_UINT8) { + const CData* ptr = reinterpret_cast(vop->varDatap()); + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varCDatap(), shortintsp); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varSDatap(), shortintsp); + } + + return; + } else if (arrayvalue_p->format == vpiIntVal) { + if (VL_UNCOVERABLE(num >= VL_VALUE_STRING_MAX_WORDS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + PLI_INT32* integersp = (PLI_INT32*)out_data; + arrayvalue_p->value.integers = integersp; + + if (varp->vltype() == VLVT_UINT8) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varCDatap(), integersp); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varSDatap(), integersp); + } else if (varp->vltype() == VLVT_UINT32) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varIDatap(), integersp); + } + + return; + } else if (arrayvalue_p->format == vpiLongIntVal) { + if (VL_UNCOVERABLE((sizeof(PLI_INT64) * num) >= VL_VALUE_STRING_MAX_CHARS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + PLI_INT64* longintsp = (PLI_INT64*)out_data; + arrayvalue_p->value.longints = longintsp; + + if (varp->vltype() == VLVT_UINT8) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varCDatap(), longintsp); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varSDatap(), longintsp); + } else if (varp->vltype() == VLVT_UINT32) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varIDatap(), longintsp); + } else if (varp->vltype() == VLVT_UINT64) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varQDatap(), longintsp); + } + + return; + } else if (arrayvalue_p->format == vpiVectorVal) { + if (VL_UNCOVERABLE((VL_WORDS_I(varp->entBits()) * 2 * num) >= VL_VALUE_STRING_MAX_WORDS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + p_vpi_vecval vectorsp = (p_vpi_vecval)out_data; + arrayvalue_p->value.vectors = vectorsp; + + if (varp->vltype() == VLVT_UINT8) { + vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, + vop->varCDatap(), vectorsp); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, + vop->varSDatap(), vectorsp); + } else if (varp->vltype() == VLVT_UINT32) { + vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, + vop->varIDatap(), vectorsp); + } else if (varp->vltype() == VLVT_UINT64) { + vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, + vop->varQDatap(), vectorsp); + } else if (varp->vltype() == VLVT_WDATA) { + vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, + vop->varEDatap(), vectorsp); + } + + return; + } else if (arrayvalue_p->format == vpiRawFourStateVal) { + if (VL_UNCOVERABLE((VL_BYTES_I(varp->entBits()) * 2 * num) >= VL_VALUE_STRING_MAX_CHARS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + PLI_BYTE8* valuep = (PLI_BYTE8*)out_data; + arrayvalue_p->value.rawvals = valuep; + + if (varp->vltype() == VLVT_UINT8) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, + vop->varCDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, + vop->varSDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT32) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, + vop->varIDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT64) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, + vop->varQDatap(), valuep); + } else if (varp->vltype() == VLVT_WDATA) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, + vop->varEDatap(), valuep); + } + + return; + } else if (arrayvalue_p->format == vpiRawTwoStateVal) { + if (VL_UNCOVERABLE((VL_BYTES_I(varp->entBits()) * num) >= VL_VALUE_STRING_MAX_CHARS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + PLI_BYTE8* valuep = (PLI_BYTE8*)out_data; + arrayvalue_p->value.rawvals = valuep; + + if (varp->vltype() == VLVT_UINT8) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, + vop->varCDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, + vop->varSDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT32) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, + vop->varIDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT64) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, + vop->varQDatap(), valuep); + } else if (varp->vltype() == VLVT_WDATA) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, + vop->varEDatap(), valuep); + } + + return; + } + + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__, + VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), vop->fullname()); +} + +void vpi_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p, + PLI_UINT32 num) { + VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_value_array %p\n", object);); + VerilatedVpiImp::assertOneCheck(); + + VL_VPI_ERROR_RESET_(); + if (VL_UNLIKELY(!object)) return; + + if (VL_UNLIKELY(!arrayvalue_p)) { + VL_VPI_WARNING_(__FILE__, __LINE__, + "Ignoring vpi_get_value_array with null value pointer"); + return; + } + + if (VL_UNLIKELY(!index_p)) { + VL_VPI_WARNING_(__FILE__, __LINE__, + "Ignoring vpi_get_value_array with null index pointer"); + return; + } + + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); + if (VL_UNLIKELY(!vop)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object); + return; + } + + if (vop->type() != vpiRegArray) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object, + VerilatedVpiError::strFromVpiObjType(vop->type())); + return; + } + + const VerilatedVar* const varp = vop->varp(); + + int lowRange = vop->rangep()->low(); + int highRange = vop->rangep()->high(); + + if ((index_p[0] > highRange) || (index_p[0] < lowRange)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: index %u for object %s is out of bounds [%u,%u]", + __func__, index_p[0], vop->fullname(), lowRange, highRange); + return; + } + + if (arrayvalue_p->flags & vpiUserAllocFlag) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiUserAllocFlag (%x)", __func__, + arrayvalue_p->flags); + return; + } + + vl_get_value_array(object, arrayvalue_p, index_p, num); +} + +void vl_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p, + PLI_UINT32 num) { + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); + if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return; + + const VerilatedVar* const varp = vop->varp(); + + int size = vop->size(); + if (VL_UNCOVERABLE(num > size)) { + VL_VPI_ERROR_(__FILE__, __LINE__, + "%s: requested elements to set (%u) exceed array size (%u)", __func__, num, + size); + return; + } + + const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low(); + int index + = leftIsLow ? index_p[0] - vop->rangep()->left() : vop->rangep()->left() - index_p[0]; + + if (arrayvalue_p->format == vpiShortIntVal) { + const PLI_UINT16* shortintsp + = reinterpret_cast(arrayvalue_p->value.shortints); + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp, + vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp, + vop->varSDatap()); + } + + return; + } else if (arrayvalue_p->format == vpiIntVal) { + const PLI_UINT32* integersp = reinterpret_cast(arrayvalue_p->value.integers); + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp, + vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp, + vop->varSDatap()); + } else if (varp->vltype() == VLVT_UINT32) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp, + vop->varIDatap()); + } + + return; + } else if (arrayvalue_p->format == vpiLongIntVal) { + const PLI_UINT64* longintsp = reinterpret_cast(arrayvalue_p->value.longints); + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, + vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, + vop->varSDatap()); + } else if (varp->vltype() == VLVT_UINT32) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, + vop->varIDatap()); + } else if (varp->vltype() == VLVT_UINT64) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, + vop->varQDatap()); + } + + return; + } else if (arrayvalue_p->format == vpiVectorVal) { + const p_vpi_vecval vectorsp = arrayvalue_p->value.vectors; + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, + vectorsp, vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, + vectorsp, vop->varSDatap()); + } else if (varp->vltype() == VLVT_UINT32) { + vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, + vectorsp, vop->varIDatap()); + } else if (varp->vltype() == VLVT_UINT64) { + vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, + vectorsp, vop->varQDatap()); + } else if (varp->vltype() == VLVT_WDATA) { + vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, + vectorsp, vop->varEDatap()); + } + + return; + } else if (arrayvalue_p->format == vpiRawFourStateVal) { + const PLI_UBYTE8* valuep = reinterpret_cast(arrayvalue_p->value.rawvals); + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, + vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, + vop->varSDatap()); + } else if (varp->vltype() == VLVT_UINT32) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, + vop->varIDatap()); + } else if (varp->vltype() == VLVT_UINT64) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, + vop->varQDatap()); + } else if (varp->vltype() == VLVT_WDATA) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, + vop->varEDatap()); + } + + return; + } else if (arrayvalue_p->format == vpiRawTwoStateVal) { + const PLI_UBYTE8* valuep = reinterpret_cast(arrayvalue_p->value.rawvals); + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, + vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, + vop->varSDatap()); + } else if (varp->vltype() == VLVT_UINT32) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, + vop->varIDatap()); + } else if (varp->vltype() == VLVT_UINT64) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, + vop->varQDatap()); + } else if (varp->vltype() == VLVT_WDATA) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, + vop->varEDatap()); + } + + return; + } + + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__, + VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), vop->fullname()); +} + +void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p, + PLI_UINT32 num) { + VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value_array %p\n", object);); + VerilatedVpiImp::assertOneCheck(); + VL_VPI_ERROR_RESET_(); + + if (VL_UNLIKELY(!arrayvalue_p)) { + VL_VPI_WARNING_(__FILE__, __LINE__, + "Ignoring vpi_put_value_array with null value pointer"); + return; + } + + if (VL_UNLIKELY(!index_p)) { + VL_VPI_WARNING_(__FILE__, __LINE__, + "Ignoring vpi_put_value_array with null index pointer"); + return; + } + + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); + if (VL_UNLIKELY(!vop)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object); + return; + } + + if (vop->type() != vpiRegArray) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object, + VerilatedVpiError::strFromVpiObjType(vop->type())); + return; + } + + const VerilatedVar* const varp = vop->varp(); + + int lowRange = vop->rangep()->low(); + int highRange = vop->rangep()->high(); + + if ((index_p[0] > highRange) || (index_p[0] < lowRange)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: index %u for object %s is out of bounds [%u,%u]", + __func__, index_p[0], vop->fullname(), lowRange, highRange); + return; + } + + if (VL_UNLIKELY(!vop->varp()->isPublicRW())) { + VL_VPI_ERROR_(__FILE__, __LINE__, + "Ignoring vpi_put_value_array to signal marked read-only," + " use public_flat_rw instead: %s", + vop->fullname()); + return; + } + + if (arrayvalue_p->flags & (vpiPropagateOff | vpiOneValue)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported flags (%x)", __func__, + arrayvalue_p->flags); + return; + } + + vl_put_value_array(object, arrayvalue_p, index_p, num); } // time processing diff --git a/include/verilatedos.h b/include/verilatedos.h index 4022ac2b09..be0cd59a2f 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -474,6 +474,7 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read() #ifndef VL_VALUE_STRING_MAX_WORDS #define VL_VALUE_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation #endif +#define VL_VALUE_STRING_MAX_CHARS (VL_VALUE_STRING_MAX_WORDS) * 4 //========================================================================= // Base macros diff --git a/src/V3AstInlines.h b/src/V3AstInlines.h index 90ebccfd82..3f6b06c7e6 100644 --- a/src/V3AstInlines.h +++ b/src/V3AstInlines.h @@ -188,8 +188,9 @@ bool AstVarRef::sameNode(const AstVarRef* samep) const { if (varScopep()) { return (varScopep() == samep->varScopep() && access() == samep->access()); } else { - return (selfPointer() == samep->selfPointer() && varp()->name() == samep->varp()->name() - && access() == samep->access()); + return (selfPointer() == samep->selfPointer() + && classOrPackagep() == samep->classOrPackagep() + && varp()->name() == samep->varp()->name() && access() == samep->access()); } } bool AstVarRef::sameNoLvalue(AstVarRef* samep) const { @@ -197,6 +198,7 @@ bool AstVarRef::sameNoLvalue(AstVarRef* samep) const { return (varScopep() == samep->varScopep()); } else { return (selfPointer() == samep->selfPointer() + && classOrPackagep() == samep->classOrPackagep() && (!selfPointer().isEmpty() || !samep->selfPointer().isEmpty()) && varp()->name() == samep->varp()->name()); } diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index 83b80717f5..1cc96fdbe7 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -230,6 +230,7 @@ class AstNodeUOrStructDType VL_NOT_FINAL : public AstNodeDType { const int m_uniqueNum; bool m_packed; bool m_isFourstate = false; // V3Width computes + bool m_constrainedRand = false; // True if struct has constraint expression protected: AstNodeUOrStructDType(VNType t, FileLine* fl, VSigning numericUnpack) @@ -244,7 +245,8 @@ class AstNodeUOrStructDType VL_NOT_FINAL : public AstNodeDType { , m_name(other.m_name) , m_uniqueNum(uniqueNumInc()) , m_packed(other.m_packed) - , m_isFourstate(other.m_isFourstate) {} + , m_isFourstate(other.m_isFourstate) + , m_constrainedRand(false) {} public: ASTGEN_MEMBERS_AstNodeUOrStructDType; @@ -284,6 +286,8 @@ class AstNodeUOrStructDType VL_NOT_FINAL : public AstNodeDType { VNumRange declRange() const VL_MT_STABLE { return VNumRange{hi(), lo()}; } AstNodeModule* classOrPackagep() const { return m_classOrPackagep; } void classOrPackagep(AstNodeModule* classpackagep) { m_classOrPackagep = classpackagep; } + bool isConstrainedRand() { return m_constrainedRand; } + void markConstrainedRand(bool flag) { m_constrainedRand = flag; } }; // === Concrete node types ===================================================== @@ -905,12 +909,14 @@ class AstMemberDType final : public AstNodeDType { string m_name; // Name of variable string m_tag; // Holds the string of the verilator tag -- used in XML output. int m_lsb = -1; // Within this level's packed struct, the LSB of the first bit of the member + bool m_constrainedRand = false; // UNSUP: int m_randType; // Randomization type (IEEE) public: AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp, AstNode* valuep) : ASTGEN_SUPER_MemberDType(fl) - , m_name{name} { + , m_name{name} + , m_constrainedRand(false) { childDTypep(dtp); // Only for parser this->valuep(valuep); dtypep(nullptr); // V3Width will resolve @@ -918,13 +924,16 @@ class AstMemberDType final : public AstNodeDType { } AstMemberDType(FileLine* fl, const string& name, AstNodeDType* dtp) : ASTGEN_SUPER_MemberDType(fl) - , m_name{name} { + , m_name{name} + , m_constrainedRand(false) { UASSERT(dtp, "AstMember created with no dtype"); refDTypep(dtp); dtypep(this); widthFromSub(subDTypep()); } ASTGEN_MEMBERS_AstMemberDType; + void dump(std::ostream& str = std::cout) const override; + void dumpJson(std::ostream& str = std::cout) const override; void dumpSmall(std::ostream& str) const override; string name() const override VL_MT_STABLE { return m_name; } // * = Var name bool hasDType() const override VL_MT_SAFE { return true; } @@ -958,6 +967,8 @@ class AstMemberDType final : public AstNodeDType { v3fatalSrc("call isCompound on subdata type, not reference"); return false; } + bool isConstrainedRand() const { return m_constrainedRand; } + void markConstrainedRand(bool flag) { m_constrainedRand = flag; } }; class AstNBACommitQueueDType final : public AstNodeDType { // @astgen ptr := m_subDTypep : AstNodeDType // Type of the corresponding variable diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index db32a6de99..f3e418a37b 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -4151,7 +4151,7 @@ class AstArraySel final : public AstNodeSel { return new AstArraySel{fileline(), lhsp, rhsp}; } void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override { - V3ERROR_NA; /* How can from be a const? */ + V3ERROR_NA; // How can from be a const? } string emitVerilog() override { return "%k(%l%f[%r])"; } string emitC() override { return "%li%k[%ri]"; } diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 811850d712..91a1ab405b 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -625,6 +625,7 @@ class AstCFunc final : public AstNode { bool m_isTrace : 1; // Function is related to tracing bool m_dontCombine : 1; // V3Combine shouldn't compare this func tree, it's special bool m_declPrivate : 1; // Declare it private + bool m_keepIfEmpty : 1; // Keep declaration and definition separate, even if empty bool m_slow : 1; // Slow routine, called once or just at init time bool m_funcPublic : 1; // From user public task/function bool m_isConstructor : 1; // Is C class constructor @@ -643,6 +644,7 @@ class AstCFunc final : public AstNode { bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code bool m_needProcess : 1; // Needs access to VlProcess of the caller bool m_recursive : 1; // Recursive or part of recursion + int m_cost; // Function call cost public: AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "") : ASTGEN_SUPER_CFunc(fl) { @@ -654,6 +656,7 @@ class AstCFunc final : public AstNode { m_isTrace = false; m_dontCombine = false; m_declPrivate = false; + m_keepIfEmpty = false; m_slow = false; m_funcPublic = false; m_isConstructor = false; @@ -671,6 +674,7 @@ class AstCFunc final : public AstNode { m_dpiImportPrototype = false; m_dpiImportWrapper = false; m_recursive = false; + m_cost = v3Global.opt.instrCountDpi(); // As proxy for unknown general DPI cost } ASTGEN_MEMBERS_AstCFunc; string name() const override VL_MT_STABLE { return m_name; } @@ -685,9 +689,7 @@ class AstCFunc final : public AstNode { } // void name(const string& name) override { m_name = name; } - int instrCount() const override { - return dpiImportPrototype() ? v3Global.opt.instrCountDpi() : 0; - } + int instrCount() const override { return m_cost; } VBoolOrUnknown isConst() const { return m_isConst; } void isConst(bool flag) { m_isConst.setTrueOrFalse(flag); } void isConst(VBoolOrUnknown flag) { m_isConst = flag; } @@ -706,6 +708,8 @@ class AstCFunc final : public AstNode { bool dontInline() const { return dontCombine() || slow() || funcPublic(); } bool declPrivate() const { return m_declPrivate; } void declPrivate(bool flag) { m_declPrivate = flag; } + bool keepIfEmpty() const VL_MT_SAFE { return m_keepIfEmpty; } + void keepIfEmpty(bool flag) { m_keepIfEmpty = flag; } bool slow() const VL_MT_SAFE { return m_slow; } void slow(bool flag) { m_slow = flag; } bool funcPublic() const { return m_funcPublic; } @@ -746,10 +750,10 @@ class AstCFunc final : public AstNode { bool isCoroutine() const { return m_rtnType == "VlCoroutine"; } void recursive(bool flag) { m_recursive = flag; } bool recursive() const { return m_recursive; } + void cost(int cost) { m_cost = cost; } // Special methods bool emptyBody() const { - return argsp() == nullptr && initsp() == nullptr && stmtsp() == nullptr - && finalsp() == nullptr; + return !keepIfEmpty() && !argsp() && !initsp() && !stmtsp() && !finalsp(); } }; class AstCLocalScope final : public AstNode { diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index f8b4a6f9b3..f0ee72c844 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1950,6 +1950,20 @@ void AstJumpLabel::dump(std::ostream& str) const { } void AstJumpLabel::dumpJson(std::ostream& str) const { dumpJsonGen(str); } +void AstMemberDType::dump(std::ostream& str) const { + this->AstNodeDType::dump(str); + if (isConstrainedRand()) str << " [CONSTRAINEDRAND]"; + if (name() != "") str << " name=" << name(); + if (tag() != "") str << " tag=" << tag(); +} + +void AstMemberDType::dumpJson(std::ostream& str) const { + dumpJsonBoolFunc(str, isConstrainedRand); + dumpJsonStrFunc(str, name); + dumpJsonStrFunc(str, tag); + dumpJsonGen(str); +} + void AstMemberDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str << "member"; @@ -2865,15 +2879,17 @@ void AstCFile::dumpJson(std::ostream& str) const { void AstCFunc::dump(std::ostream& str) const { this->AstNode::dump(str); if (slow()) str << " [SLOW]"; - if (dpiPure()) str << " [DPIPURE]"; if (isStatic()) str << " [STATIC]"; + if (dpiContext()) str << " [DPICTX]"; if (dpiExportDispatcher()) str << " [DPIED]"; if (dpiExportImpl()) str << " [DPIEI]"; if (dpiImportPrototype()) str << " [DPIIP]"; if (dpiImportWrapper()) str << " [DPIIW]"; - if (dpiContext()) str << " [DPICTX]"; + if (dpiPure()) str << " [DPIPURE]"; if (isConstructor()) str << " [CTOR]"; if (isDestructor()) str << " [DTOR]"; + if (isMethod()) str << " [METHOD]"; + if (isLoose()) str << " [LOOSE]"; if (isVirtual()) str << " [VIRT]"; if (isCoroutine()) str << " [CORO]"; if (needProcess()) str << " [NPRC]"; diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index 045628e6a6..30b5d5f694 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -62,6 +62,7 @@ class V3CCtorsBuilder final { AstCFunc* const funcp = new AstCFunc{m_modp->fileline(), funcName, nullptr, "void"}; funcp->isStatic(false); funcp->isLoose(!m_type.isClass()); + funcp->keepIfEmpty(true); // TODO relax funcp->declPrivate(true); funcp->slow(!m_type.isClass()); // Only classes construct on fast path string preventUnusedStmt; @@ -210,6 +211,7 @@ void V3CCtors::evalAsserts() { funcp->declPrivate(true); funcp->isStatic(false); funcp->isLoose(true); + funcp->keepIfEmpty(true); funcp->slow(false); funcp->ifdef("VL_DEBUG"); modp->addStmtsp(funcp); diff --git a/src/V3Config.cpp b/src/V3Config.cpp index 9b8d38a615..f637cacd35 100644 --- a/src/V3Config.cpp +++ b/src/V3Config.cpp @@ -533,11 +533,13 @@ class V3ConfigScopeTraceResolver final { // Resolve modules and files in the design class V3ConfigResolver final { + enum ProfileDataMode : uint8_t { NONE = 0, MTASK = 1, HIER_DPI = 2 }; V3ConfigModuleResolver m_modules; // Access to module names (with wildcards) V3ConfigFileResolver m_files; // Access to file names (with wildcards) V3ConfigScopeTraceResolver m_scopeTraces; // Regexp to trace enables std::unordered_map> m_profileData; // Access to profile_data records + uint8_t m_mode = NONE; FileLine* m_profileFileLine = nullptr; V3ConfigResolver() = default; @@ -552,10 +554,21 @@ class V3ConfigResolver final { V3ConfigFileResolver& files() { return m_files; } V3ConfigScopeTraceResolver& scopeTraces() { return m_scopeTraces; } - void addProfileData(FileLine* fl, const string& model, const string& key, uint64_t cost) { + void addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost) { + // Empty key for hierarchical DPI wrapper costs. + addProfileData(fl, hierDpi, "", cost, HIER_DPI); + } + void addProfileData(FileLine* fl, const string& model, const string& key, uint64_t cost, + ProfileDataMode mode = MTASK) { if (!m_profileFileLine) m_profileFileLine = fl; if (cost == 0) cost = 1; // Cost 0 means delete (or no data) m_profileData[model][key] += cost; + m_mode |= mode; + } + bool containsMTaskProfileData() const { return m_mode & MTASK; } + uint64_t getProfileData(const string& hierDpi) const { + // Empty key for hierarchical DPI wrapper costs. + return getProfileData(hierDpi, ""); } uint64_t getProfileData(const string& model, const string& key) const { const auto mit = m_profileData.find(model); @@ -619,6 +632,10 @@ void V3Config::addModulePragma(const string& module, VPragmaType pragma) { V3ConfigResolver::s().modules().at(module).addModulePragma(pragma); } +void V3Config::addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost) { + V3ConfigResolver::s().addProfileData(fl, hierDpi, cost); +} + void V3Config::addProfileData(FileLine* fl, const string& model, const string& key, uint64_t cost) { V3ConfigResolver::s().addProfileData(fl, model, key, cost); @@ -724,6 +741,9 @@ void V3Config::applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar if (vp) vp->apply(varp); } +uint64_t V3Config::getProfileData(const string& hierDpi) { + return V3ConfigResolver::s().getProfileData(hierDpi); +} uint64_t V3Config::getProfileData(const string& model, const string& key) { return V3ConfigResolver::s().getProfileData(model, key); } @@ -736,6 +756,10 @@ bool V3Config::getScopeTraceOn(const string& scope) { void V3Config::contentsPushText(const string& text) { return WildcardContents::pushText(text); } +bool V3Config::containsMTaskProfileData() { + return V3ConfigResolver::s().containsMTaskProfileData(); +} + bool V3Config::waive(FileLine* filelinep, V3ErrorCode code, const string& message) { V3ConfigFile* filep = V3ConfigResolver::s().files().resolve(filelinep->filename()); if (!filep) return false; diff --git a/src/V3Config.h b/src/V3Config.h index 32ee079c0f..df7287c07b 100644 --- a/src/V3Config.h +++ b/src/V3Config.h @@ -38,6 +38,7 @@ class V3Config final { const string& match); static void addInline(FileLine* fl, const string& module, const string& ftask, bool on); static void addModulePragma(const string& module, VPragmaType pragma); + static void addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost); static void addProfileData(FileLine* fl, const string& model, const string& key, uint64_t cost); static void addScopeTraceOn(bool on, const string& scope, int levels); @@ -51,12 +52,15 @@ class V3Config final { static void applyModule(AstNodeModule* modulep); static void applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar* varp); + static uint64_t getProfileData(const string& hierDpi); static uint64_t getProfileData(const string& model, const string& key); static FileLine* getProfileDataFileLine(); static bool getScopeTraceOn(const string& scope); static void contentsPushText(const string& text); + static bool containsMTaskProfileData(); + static bool waive(FileLine* filelinep, V3ErrorCode code, const string& message); }; diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 406f4c8154..782b7a00b2 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2266,6 +2266,19 @@ class ConstVisitor final : public VNVisitor { } return false; } + bool replaceJumpGoNext(AstJumpGo* nodep, AstNode* abovep) { + // If JumpGo has an upper JumpBlock that is to same label, then + // code will by normal sequential operation do the JUMPGO and it + // can be removed. + if (nodep->nextp()) return false; // Label jumps other statements + AstJumpBlock* const aboveBlockp = VN_CAST(abovep, JumpBlock); + if (!aboveBlockp) return false; + if (aboveBlockp != nodep->labelp()->blockp()) return false; + if (aboveBlockp->endStmtsp() != nodep->labelp()) return false; + UINFO(4, "JUMPGO => last remove " << nodep << endl); + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return true; + } // Boolean replacements bool operandBoolShift(const AstNode* nodep) { @@ -3158,8 +3171,9 @@ class ConstVisitor final : public VNVisitor { nodep->condp(new AstLogAnd{lowerIfp->fileline(), condp, lowerCondp}); lowerIfp->replaceWith(lowerThensp); VL_DO_DANGLING(pushDeletep(lowerIfp), lowerIfp); - } else if (operandBoolShift(nodep->condp())) { - replaceBoolShift(nodep->condp()); + } else { + // Optimizations that don't reform the IF itself + if (operandBoolShift(nodep->condp())) replaceBoolShift(nodep->condp()); } } } @@ -3377,7 +3391,7 @@ class ConstVisitor final : public VNVisitor { void visit(AstJumpGo* nodep) override { iterateChildren(nodep); - // Jump to label where label immediately follows this go is not useful + // Jump to label where label immediately follows this JumpGo is not useful if (nodep->labelp() == VN_CAST(nodep->nextp(), JumpLabel)) { VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); // Keep the label, might be other jumps pointing to it, gets cleaned later @@ -3390,14 +3404,14 @@ class ConstVisitor final : public VNVisitor { } // If last statement in a jump label we have JumpLabel(...., JumpGo) // Often caused by "return" in a Verilog function. The Go is pointless, remove. + if (replaceJumpGoNext(nodep, nodep->abovep())) return; + // Also optimize If with a then or else's final statement being this JumpGo + // We only do single ifs... Ideally we'd look at control flow and delete any + // Jumps where any following control flow point is the label if (!nodep->nextp()) { - if (AstJumpBlock* const aboveBlockp = VN_CAST(nodep->abovep(), JumpBlock)) { - if (aboveBlockp == nodep->labelp()->blockp()) { - if (aboveBlockp->endStmtsp() == nodep->labelp()) { - UINFO(4, "JUMPGO => last remove " << nodep << endl); - VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); - return; - } + if (AstNodeIf* const aboveIfp = VN_CAST(nodep->abovep(), NodeIf)) { + if (!aboveIfp->nextp()) { + if (replaceJumpGoNext(nodep, aboveIfp->abovep())) return; } } } diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index 6b885b1d9d..92b3836424 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -208,6 +208,7 @@ class DelayedVisitor final : public VNVisitor { // NODE STATE // AstVar::user1() -> bool. Set true if already issued MULTIDRIVEN warning // AstVarRef::user1() -> bool. Set true if target of NBA + // AstAssignDly::user1() -> bool. Set true if already visited // AstNodeModule::user1p() -> std::unorded_map temp map via m_varMap // AstVarScope::user1p() -> VarScopeInfo via m_vscpInfo // AstVarScope::user2p() -> AstVarRef*: First write reference to the Variable @@ -909,6 +910,9 @@ class DelayedVisitor final : public VNVisitor { VL_DO_DANGLING(nodep->deleteTree(), nodep); } void visit(AstAssignDly* nodep) override { + // Prevent double processing due to AstExprStmt being moved before this node + if (nodep->user1SetOnce()) return; + if (m_cfuncp) { if (!v3Global.rootp()->nbaEventp()) { nodep->v3warn( @@ -922,21 +926,31 @@ class DelayedVisitor final : public VNVisitor { UASSERT_OBJ(m_inSuspendableOrFork || m_activep->hasClocked(), nodep, "<= assignment in non-clocked block, should have been converted in V3Active"); - // Grab the reference to the target of the NBA + // Grab the reference to the target of the NBA, also lift ExprStmt statements on the LHS VL_RESTORER(m_currNbaLhsRefp); UASSERT_OBJ(!m_currNbaLhsRefp, nodep, "NBAs should not nest"); - nodep->lhsp()->foreach([&](AstVarRef* nodep) { - // Ignore reads (e.g.: '_[*here*] <= _') - if (nodep->access().isReadOnly()) return; - // A RW ref on the LHS (e.g.: '_[preInc(*here*)] <= _') is asking for trouble at this - // point. These should be lowered in an earlier pass into sequenced temporaries. - UASSERT_OBJ(!nodep->access().isRW(), nodep, "RW ref on LHS of NBA"); - // Multiple target variables - // (e.g.: '{*here*, *and here*} <= _',or '*here*[*and here* = _] <= _'). - // These should be lowered in an earlier pass into sequenced statements. - UASSERT_OBJ(!m_currNbaLhsRefp, nodep, "Multiple Write refs on LHS of NBA"); - // Hold on to it - m_currNbaLhsRefp = nodep; + nodep->lhsp()->foreach([&](AstNode* currp) { + if (AstExprStmt* const exprp = VN_CAST(currp, ExprStmt)) { + // Move statements before the NBA + nodep->addHereThisAsNext(exprp->stmtsp()->unlinkFrBackWithNext()); + // Replace with result + currp->replaceWith(exprp->resultp()->unlinkFrBack()); + // Get rid of the AstExprStmt + VL_DO_DANGLING(pushDeletep(currp), currp); + } else if (AstVarRef* const refp = VN_CAST(currp, VarRef)) { + // Ignore reads (e.g.: '_[*here*] <= _') + if (refp->access().isReadOnly()) return; + // A RW ref on the LHS (e.g.: '_[preInc(*here*)] <= _') is asking for trouble at + // this point. These should be lowered in an earlier pass into sequenced + // temporaries. + UASSERT_OBJ(!refp->access().isRW(), refp, "RW ref on LHS of NBA"); + // Multiple target variables + // (e.g.: '{*here*, *and here*} <= _',or '*here*[*and here* = _] <= _'). + // These should be lowered in an earlier pass into sequenced statements. + UASSERT_OBJ(!m_currNbaLhsRefp, refp, "Multiple Write refs on LHS of NBA"); + // Hold on to it + m_currNbaLhsRefp = refp; + } }); // The target variable of the NBA (there can only be one per NBA at this point) AstVarScope* const vscp = m_currNbaLhsRefp->varScopep(); diff --git a/src/V3EmitCBase.cpp b/src/V3EmitCBase.cpp index 547a178841..2cb45c20b0 100644 --- a/src/V3EmitCBase.cpp +++ b/src/V3EmitCBase.cpp @@ -171,7 +171,11 @@ void EmitCBaseVisitorConst::emitCFuncDecl(const AstCFunc* funcp, const AstNodeMo putns(funcp, "virtual "); } emitCFuncHeader(funcp, modp, /* withScope: */ false); - putns(funcp, ";\n"); + if (funcp->emptyBody() && !funcp->isLoose() && !cLinkage) { + putns(funcp, " {}\n"); + } else { + putns(funcp, ";\n"); + } if (!funcp->ifdef().empty()) putns(funcp, "#endif // " + funcp->ifdef() + "\n"); } diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index d39c8c4b09..97335c62e5 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -456,9 +456,7 @@ void EmitCFunc::emitDereference(AstNode* nodep, const string& pointer) { void EmitCFunc::emitCvtPackStr(AstNode* nodep) { if (const AstConst* const constp = VN_CAST(nodep, Const)) { - putnbs(nodep, "std::string{"); - putsQuoted(constp->num().toString()); - puts("}"); + emitConstantString(constp); } else if (VN_IS(nodep->dtypep(), StreamDType)) { putnbs(nodep, "VL_CVT_PACK_STR_ND("); iterateAndNextConstNull(nodep); @@ -494,9 +492,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string } else if (nodep->num().isFourState()) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: 4-state numbers in this context"); } else if (nodep->num().isString()) { - putnbs(nodep, "std::string{"); - putsQuoted(nodep->num().toString()); - puts("}"); + emitConstantString(nodep); } else if (nodep->isWide()) { int upWidth = nodep->num().widthMin(); int chunks = 0; @@ -594,6 +590,13 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string } } +void EmitCFunc::emitConstantString(const AstConst* nodep) { + putnbs(nodep, "std::string{"); + const string str = nodep->num().toString(); + if (!str.empty()) putsQuoted(str); + puts("}"); +} + void EmitCFunc::emitSetVarConstant(const string& assignString, AstConst* constp) { if (!constp->isWide()) { puts(assignString); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index e79109502a..00374ae1f7 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -208,6 +208,7 @@ class EmitCFunc VL_NOT_FINAL : public EmitCConstInit { void emitCvtPackStr(AstNode* nodep); void emitCvtWideArray(AstNode* nodep, AstNode* fromp); void emitConstant(AstConst* nodep, AstVarRef* assigntop, const string& assignString); + void emitConstantString(const AstConst* nodep); void emitSetVarConstant(const string& assignString, AstConst* constp); void emitVarReset(AstVar* varp); string emitVarResetRecurse(const AstVar* varp, const string& varNameProtected, @@ -279,6 +280,7 @@ class EmitCFunc VL_NOT_FINAL : public EmitCConstInit { // VISITORS using EmitCConstInit::visit; void visit(AstCFunc* nodep) override { + if (nodep->emptyBody() && !nodep->isLoose()) return; VL_RESTORER(m_useSelfForThis); VL_RESTORER(m_cfuncp); VL_RESTORER(m_instantiatesOwnProcess) @@ -303,15 +305,6 @@ class EmitCFunc VL_NOT_FINAL : public EmitCConstInit { } puts(" {\n"); - if (nodep->isLoose()) { - m_lazyDecls.declared(nodep); // Defined here, so no longer needs declaration - if (!nodep->isStatic()) { // Standard prologue - m_useSelfForThis = true; - puts("(void)vlSelf; // Prevent unused variable warning\n"); - if (!VN_IS(m_modp, Class)) puts(symClassAssign()); - } - } - // "+" in the debug indicates a print from the model puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+ "); for (int i = 0; i < m_modp->level(); ++i) puts(" "); @@ -319,6 +312,18 @@ class EmitCFunc VL_NOT_FINAL : public EmitCConstInit { puts(nodep->isLoose() ? "__" : "::"); puts(nodep->nameProtect() + "\\n\"); );\n"); + if (nodep->isLoose()) { + m_lazyDecls.declared(nodep); // Defined here, so no longer needs declaration + if (!nodep->isStatic()) { // Standard prologue + m_useSelfForThis = true; + if (!VN_IS(m_modp, Class)) { + puts(symClassAssign()); // Uses vlSelf + } else { + puts("(void)vlSelf; // Prevent unused variable warning\n"); + } + } + } + // Instantiate a process class if it's going to be needed somewhere later nodep->forall([&](const AstNodeCCall* ccallp) -> bool { if (ccallp->funcp()->needProcess() @@ -344,16 +349,14 @@ class EmitCFunc VL_NOT_FINAL : public EmitCConstInit { if (m_useSelfForThis) { m_usevlSelfRef = true; - /* - * Using reference to the vlSelf pointer will help the C++ - * compiler to have dereferenceable hints, which can help to - * reduce the need for branch instructions in the generated - * code to allow the compiler to generate load store after the - * if condition (including short-circuit evaluation) - * speculatively and also reduce the data cache pollution when - * executing in the wrong path to make verilator-generated code - * run faster. - */ + // Using reference to the vlSelf pointer will help the C++ + // compiler to have dereferenceable hints, which can help to + // reduce the need for branch instructions in the generated + // code to allow the compiler to generate load store after the + // if condition (including short-circuit evaluation) + // speculatively and also reduce the data cache pollution when + // executing in the wrong path to make verilator-generated code + // run faster. puts("auto& vlSelfRef = std::ref(*vlSelf).get();\n"); } diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 9af1c604d5..41c471f0a3 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -248,7 +248,43 @@ class EmitCHeader final : public EmitCConstInit { putns(itemp, itemp->dtypep()->cType(itemp->nameProtect(), false, false)); puts(";\n"); } - + // Three helper functions for struct constrained randomization: + // - memberNames: Get member names + // - getMembers: Access member references + // - memberIndices: Retrieve member indices + if (sdtypep->isConstrainedRand()) { + putns(sdtypep, "\nstd::vector memberNames(void) const {\n"); + puts("return {"); + for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + if (itemp->isConstrainedRand()) putns(itemp, "\"" + itemp->shortName() + "\""); + if (itemp->nextp() && VN_AS(itemp->nextp(), MemberDType)->isConstrainedRand()) + puts(",\n"); + } + puts("};\n}\n"); + + putns(sdtypep, "\nauto memberIndices(void) const {\n"); + puts("return std::index_sequence_for<"); + for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + if (itemp->isConstrainedRand()) + putns(itemp, itemp->dtypep()->cType("", false, false)); + if (itemp->nextp() && VN_AS(itemp->nextp(), MemberDType)->isConstrainedRand()) + puts(",\n"); + } + puts(">{};\n}\n"); + + putns(sdtypep, "\ntemplate "); + putns(sdtypep, "\nauto getMembers(T& obj) {\n"); + puts("return std::tie("); + for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + if (itemp->isConstrainedRand()) putns(itemp, "obj." + itemp->nameProtect()); + if (itemp->nextp() && VN_AS(itemp->nextp(), MemberDType)->isConstrainedRand()) + puts(", "); + } + puts(");\n}\n"); + } putns(sdtypep, "\nbool operator==(const " + EmitCBase::prefixNameProtect(sdtypep) + "& rhs) const {\n"); puts("return "); @@ -280,6 +316,9 @@ class EmitCHeader final : public EmitCConstInit { puts(");\n"); puts("}\n"); puts("};\n"); + puts("template <>\n"); + putns(sdtypep, "struct VlIsCustomStruct<" + EmitCBase::prefixNameProtect(sdtypep) + + "> : public std::true_type {};\n"); } // getfunc: VL_ASSIGNSEL_XX(rbits, obits, off, lhsdata, rhsdata); diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index f87463f342..71d1979a6a 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -96,7 +96,7 @@ class EmitGroup final { std::sort(sortedScores.begin(), sortedScores.end()); const int64_t topScore = sortedScores.back(); - os << "Top score: " << topScore << endl; + os << "Top score: " << topScore << '\n'; const int maxScoreWidth = std::to_string(topScore).length(); const int64_t intervalsNum = std::min(topScore + 1, MAX_INTERVALS_NUM); @@ -123,7 +123,7 @@ class EmitGroup final { for (const Interval& iv : intervals) topIntervalSize = std::max(topIntervalSize, iv.m_size); - os << "Input files' scores histogram:" << endl; + os << "Input files' scores histogram:\n"; for (const Interval& iv : intervals) { const int scaledSize = iv.m_size * (MAX_BAR_LENGTH + 1) / topIntervalSize; @@ -132,14 +132,14 @@ class EmitGroup final { os << std::setw(maxScoreWidth) << iv.m_lowerBound << line << " " << iv.m_size << '\n'; } os << std::setw(maxScoreWidth) << (topScore + 1) << '\n'; - os << endl; + os << '\n'; } // PRIVATE METHODS // Debug logging: dumps Work Lists and their lists of files void dumpWorkLists(std::ostream& os) { - os << "Initial Work Lists with their concatenation eligibility status:" << endl; + os << "Initial Work Lists with their concatenation eligibility status:\n"; for (const WorkList& list : m_workLists) { os << "+ [" << (list.m_isConcatenable ? 'x' : ' ') << "] Work List #" << list.m_dbgId << " (num of files: " << list.m_files.size() @@ -157,13 +157,13 @@ class EmitGroup final { os << "| + " << last.m_filename << " (score: " << last.m_score << ")\n"; } } - os << endl; + os << '\n'; } // Debug logging: dumps list of output files. List of grouped files is additionally printed // for each concatenating file. void dumpOutputList(std::ostream& os) const { - os << "List of output files after execution of concatenation:" << endl; + os << "List of output files after execution of concatenation:\n"; for (const FileOrConcatenatedFilesList& entry : m_outputFiles) { if (entry.isConcatenatingFile()) { @@ -182,15 +182,16 @@ class EmitGroup final { const int totalBucketsNum = v3Global.opt.outputGroups(); // Return early if there's nothing to do. bool groupingRedundant = false; - if (inputFilesCount < MIN_FILES_COUNT) { - UINFO(4, "File concatenation skipped: Too few files (" - << m_inputFiles.size() << " < " << MIN_FILES_COUNT << ")" << endl); + if (inputFilesCount < MIN_FILES_COUNT + && inputFilesCount <= static_cast(totalBucketsNum)) { + UINFO(4, "File concatenation skipped: Too few files (" << m_inputFiles.size() << " < " + << MIN_FILES_COUNT << ")\n"); groupingRedundant = true; } if (inputFilesCount < (MIN_FILES_PER_BUCKET * totalBucketsNum)) { UINFO(4, "File concatenation skipped: Too few files per bucket (" << m_inputFiles.size() << " < " << MIN_FILES_PER_BUCKET << " - " - << totalBucketsNum << ")" << endl); + << totalBucketsNum << ")\n"); groupingRedundant = true; } if (!groupingRedundant) return false; @@ -212,13 +213,12 @@ class EmitGroup final { V3Stats::addStat("Concatenation max score", concatenableFileMaxScore); int nextWorkListId = 0; - if (m_logp) *m_logp << "Input files with their concatenation eligibility status:" << endl; + if (m_logp) *m_logp << "Input files with their concatenation eligibility status:\n"; for (const FilenameWithScore& inputFile : m_inputFiles) { const bool fileIsConcatenable = (inputFile.m_score <= concatenableFileMaxScore); if (m_logp) *m_logp << " + [" << (fileIsConcatenable ? 'x' : ' ') << "] " - << inputFile.m_filename << " (score: " << inputFile.m_score << ")" - << endl; + << inputFile.m_filename << " (score: " << inputFile.m_score << ")\n"; V3Stats::addStatSum(fileIsConcatenable ? "Concatenation total grouped score" : "Concatenation total non-grouped score", inputFile.m_score); @@ -233,7 +233,7 @@ class EmitGroup final { list.m_files.push_back({inputFile.m_filename, inputFile.m_score}); list.m_totalScore += inputFile.m_score; } - if (m_logp) *m_logp << endl; + if (m_logp) *m_logp << '\n'; } void assignBuckets(uint64_t concatenableFilesTotalScore) { @@ -248,8 +248,7 @@ class EmitGroup final { // Debugging: Log which work lists will be kept if (m_logp) { *m_logp << "More Work Lists than buckets; " - "Work Lists with statuses indicating whether the list will be kept:" - << endl; + "Work Lists with statuses indicating whether the list will be kept:\n"; // Only lists that will be kept. List that will be removed are logged below. std::for_each(m_concatenableListsByDescSize.begin(), m_concatenableListsByDescSize.begin() + totalBucketsNum, @@ -269,7 +268,7 @@ class EmitGroup final { << " (num of files: " << listp->m_files.size() << "; total score: " << listp->m_totalScore << ")\n"; }); - if (m_logp) *m_logp << endl; + if (m_logp) *m_logp << '\n'; m_concatenableListsByDescSize.resize(totalBucketsNum); // Recalculate stats @@ -283,7 +282,7 @@ class EmitGroup final { V3Stats::addStat("Concatenation ideal bucket score", idealBucketScore); - if (m_logp) *m_logp << "Buckets assigned to Work Lists:" << endl; + if (m_logp) *m_logp << "Buckets assigned to Work Lists:\n"; int availableBuckets = v3Global.opt.outputGroups(); for (WorkList* listp : m_concatenableListsByDescSize) { if (availableBuckets > 0) { @@ -301,7 +300,7 @@ class EmitGroup final { << std::right << " (excluding from concatenation)\n"; } } - if (m_logp) *m_logp << endl; + if (m_logp) *m_logp << '\n'; } void buildOutputList() { @@ -395,19 +394,19 @@ class EmitGroup final { } void process() { - UINFO(4, __FUNCTION__ << ":" << endl); - UINFO(5, "Number of input files: " << m_inputFiles.size() << endl); - UINFO(5, "Total score: " << m_totalScore << endl); - UINFO(5, "Group file prefix: " << m_groupFilePrefix << endl); + UINFO(4, __FUNCTION__ << " group file prefix: " << m_groupFilePrefix << '\n'); + UINFO(5, "Number of input files: " << m_inputFiles.size() << '\n'); + UINFO(5, "Total score: " << m_totalScore << '\n'); const int totalBucketsNum = v3Global.opt.outputGroups(); - UINFO(5, "Number of buckets: " << totalBucketsNum << endl); + UINFO(5, "Number of buckets: " << totalBucketsNum << '\n'); UASSERT(totalBucketsNum > 0, "More than 0 buckets required"); if (fallbackNoGrouping(m_inputFiles.size())) return; - if (dumpLevel() >= 6) { + if (debug() >= 6 || dumpLevel() >= 6) { const string filename = v3Global.debugFilename("outputgroup") + ".txt"; + UINFO(5, "Dumping " << filename << endl); m_logp = std::unique_ptr{V3File::new_ofstream(filename)}; if (m_logp->fail()) v3fatal("Can't write " << filename); } @@ -417,7 +416,6 @@ class EmitGroup final { createWorkLists(); // Collect stats and mark lists with only one file as non-concatenable - size_t concatenableFilesCount = 0; int64_t concatenableFilesTotalScore = 0; @@ -479,6 +477,9 @@ class EmitMk final { using FileOrConcatenatedFilesList = EmitGroup::FileOrConcatenatedFilesList; using FilenameWithScore = EmitGroup::FilenameWithScore; + // MEMBERS + double m_putClassCount = 0; // Number of classEntries printed + public: // METHODS @@ -494,6 +495,7 @@ class EmitMk final { void putMakeClassEntry(V3OutMkFile& of, const string& name) { of.puts("\t" + V3Os::filenameNonDirExt(name) + " \\\n"); + ++m_putClassCount; } void emitClassMake() { @@ -577,9 +579,12 @@ class EmitMk final { } else { of.puts(", fast-path, compile with highest optimization\n"); } - of.puts(support == 2 ? "VM_GLOBAL" : support == 1 ? "VM_SUPPORT" : "VM_CLASSES"); - of.puts(slow ? "_SLOW" : "_FAST"); - of.puts(" += \\\n"); + const string targetVar = (support == 2 ? "VM_GLOBAL"s + : support == 1 ? "VM_SUPPORT"s + : "VM_CLASSES"s) + + (slow ? "_SLOW" : "_FAST"); + m_putClassCount = 0; + of.puts(targetVar + " += \\\n"); if (support == 2 && v3Global.opt.hierChild()) { // Do nothing because VM_GLOBAL is necessary per executable. Top module will // have them. @@ -605,7 +610,7 @@ class EmitMk final { const std::vector& list = slow ? vmClassesSlowList : vmClassesFastList; for (const FileOrConcatenatedFilesList& entry : list) { - if (entry.isConcatenatingFile()) { emitConcatenatingFile(entry); } + if (entry.isConcatenatingFile()) emitConcatenatingFile(entry); putMakeClassEntry(of, entry.m_filename); } } else { @@ -619,6 +624,7 @@ class EmitMk final { } } of.puts("\n"); + V3Stats::addStat("Makefile targets, " + targetVar, m_putClassCount); } } @@ -808,6 +814,7 @@ class EmitMk final { }; //###################################################################### + class EmitMkHierVerilation final { const V3HierBlockPlan* const m_planp; const string m_makefile; // path of this makefile diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index b5da1b091a..8aab516882 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -28,13 +28,17 @@ VL_DEFINE_DEBUG_FUNCTIONS; // Emit statements and expressions class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { - // MEMBERS - bool m_suppressSemi = false; + // STATE - across all visitors const bool m_suppressUnknown = false; + + // STATE - for current visit position (use VL_RESTORER) AstSenTree* m_sensesp; // Domain for printing one a ALWAYS under a ACTIVE + bool m_suppressSemi = false; // Non-statement, don't print ; + bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars + bool m_arrayPost = false; // Print array information that goes after identifier (vs after) + std::deque m_packedps; // Packed arrays to print with BasicDType // METHODS - virtual void puts(const string& str) = 0; virtual void putbs(const string& str) = 0; virtual void putfs(AstNode* nodep, const string& str) = 0; // Fileline and node %% mark @@ -49,6 +53,20 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { putsNoTracking("\""); } + void iterateAndCommaConstNull(AstNode* nodep) { + for (; nodep; nodep = nodep->nextp()) { + iterateConst(nodep); + if (nodep->nextp()) puts(", "); + } + } + void emitPacked() { + for (AstNodeArrayDType* packedp : m_packedps) { + puts(" "); + iterateConstNull(packedp->rangep()); + } + m_packedps.clear(); + } + // VISITORS void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); } void visit(AstNodeModule* nodep) override { @@ -58,13 +76,14 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } void visit(AstPort* nodep) override {} void visit(AstNodeFTask* nodep) override { - putfs(nodep, nodep->isFunction() ? "function" : "task"); + const bool func = nodep->isFunction() || nodep->name() == "new"; + putfs(nodep, func ? "function" : "task"); puts(" "); puts(nodep->prettyName()); puts(";\n"); // Only putfs the first time for each visitor; later for same node is putqs iterateAndNextConstNull(nodep->stmtsp()); - putfs(nodep, nodep->isFunction() ? "endfunction\n" : "endtask\n"); + putfs(nodep, func ? "endfunction\n" : "endtask\n"); } void visit(AstBegin* nodep) override { @@ -169,7 +188,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } void visit(AstSenItem* nodep) override { putfs(nodep, ""); - puts(nodep->edgeType().verilogKwd()); + if (nodep->edgeType() != VEdgeType::ET_CHANGED) puts(nodep->edgeType().verilogKwd()); if (nodep->sensp()) puts(" "); iterateChildrenConst(nodep); } @@ -221,13 +240,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { putfs(nodep, nodep->verilogKwd()); putbs("("); if (fileOrStrgp) { - iterateAndNextConstNull(fileOrStrgp); + iterateConstNull(fileOrStrgp); putbs(", "); } putsQuoted(text); for (AstNode* expp = exprsp; expp; expp = expp->nextp()) { puts(", "); - iterateAndNextConstNull(expp); + iterateConstNull(expp); } puts(");\n"); } @@ -393,7 +412,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } void visit(AstTextBlock* nodep) override { visit(static_cast(nodep)); - VL_RESTORER(m_suppressSemi); + VL_RESTORER(m_suppressVarSemi); m_suppressVarSemi = nodep->commas(); for (AstNode* childp = nodep->nodesp(); childp; childp = childp->nextp()) { iterateConst(childp); @@ -403,17 +422,17 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { void visit(AstScopeName* nodep) override {} void visit(AstCStmt* nodep) override { putfs(nodep, "$_CSTMT("); - iterateAndNextConstNull(nodep->exprsp()); + iterateAndCommaConstNull(nodep->exprsp()); puts(");\n"); } void visit(AstCExpr* nodep) override { putfs(nodep, "$_CEXPR("); - iterateAndNextConstNull(nodep->exprsp()); + iterateAndCommaConstNull(nodep->exprsp()); puts(")"); } void visit(AstUCStmt* nodep) override { putfs(nodep, "$c("); - iterateAndNextConstNull(nodep->exprsp()); + iterateAndCommaConstNull(nodep->exprsp()); puts(");\n"); } void visit(AstUCFunc* nodep) override { @@ -431,19 +450,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { void visit(AstCMethodHard* nodep) override { iterateConst(nodep->fromp()); puts("." + nodep->name() + "("); - for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) { - if (pinp != nodep->pinsp()) puts(", "); - iterateConst(pinp); - } + iterateAndCommaConstNull(nodep->pinsp()); puts(")"); } void visit(AstCMethodCall* nodep) override { iterateConst(nodep->fromp()); puts("." + nodep->name() + "("); - for (AstNode* pinp = nodep->argsp(); pinp; pinp = pinp->nextp()) { - if (pinp != nodep->argsp()) puts(", "); - iterateConst(pinp); - } + iterateAndCommaConstNull(nodep->argsp()); puts(")"); } @@ -565,13 +578,15 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { puts(cvtToStr(nodep->leftConst())); puts(":"); puts(cvtToStr(nodep->rightConst())); - puts("]"); } else { iterateAndNextConstNull(nodep->leftp()); puts(":"); iterateAndNextConstNull(nodep->rightp()); - puts("]"); } + puts("]"); + } + void visit(AstRand* nodep) override { + emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->seedp()); } void visit(AstSel* nodep) override { iterateAndNextConstNull(nodep->fromp()); @@ -603,14 +618,31 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } void visit(AstTypedef* nodep) override { putfs(nodep, "typedef "); - iterateAndNextConstNull(nodep->subDTypep()); + iterateConstNull(nodep->subDTypep()); puts(" "); puts(nodep->prettyName()); puts(";\n"); } + void visit(AstAssocArrayDType* nodep) override { + if (!m_arrayPost) { + iterateConst(nodep->subDTypep()); + } else { + VL_RESTORER(m_arrayPost); + m_arrayPost = false; + puts("["); + iterateConst(nodep->keyDTypep()); + puts("]"); + m_arrayPost = true; + iterateConst(nodep->subDTypep()); // For post's key + } + } void visit(AstBasicDType* nodep) override { + if (m_arrayPost) return; putfs(nodep, nodep->prettyName()); - if (nodep->isSigned()) putfs(nodep, " signed"); + if (nodep->isSigned() && !nodep->keyword().isDouble()) putfs(nodep, " signed"); + // Do not emit ranges for integer atoms. + if (nodep->keyword().isIntNumeric() && !nodep->keyword().isBitLogic()) return; + emitPacked(); if (nodep->rangep()) { puts(" "); iterateAndNextConstNull(nodep->rangep()); @@ -624,12 +656,50 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } } void visit(AstConstDType* nodep) override { + if (m_arrayPost) return; putfs(nodep, "const "); iterateConst(nodep->subDTypep()); } - void visit(AstNodeArrayDType* nodep) override { + void visit(AstDynArrayDType* nodep) override { + if (!m_arrayPost) { + iterateConst(nodep->subDTypep()); + } else { + puts("[]"); + iterateConst(nodep->subDTypep()); // For post's key + } + } + void visit(AstEnumDType* nodep) override { + if (m_arrayPost) return; + putfs(nodep, "enum "); iterateConst(nodep->subDTypep()); - iterateAndNextConstNull(nodep->rangep()); + puts("{\n"); + iterateAndNextConstNull(nodep->itemsp()); + puts("}"); + } + void visit(AstEnumItem* nodep) override { + putfs(nodep, nodep->name()); + iterateConstNull(nodep->rangep()); + puts(" = "); + iterateConstNull(nodep->valuep()); + if (nodep->nextp()) puts(","); + puts("\n"); + } + void visit(AstNodeArrayDType* nodep) override { + if (!m_arrayPost) { + if (VN_IS(nodep, PackArrayDType)) { + // Unpacked ranges handled in BasicDType, as they print "backwards" + m_packedps.push_back(nodep); + } + iterateConst(nodep->subDTypep()); + } else { + if (VN_IS(nodep, UnpackArrayDType)) { + VL_RESTORER(m_arrayPost); + m_arrayPost = false; + iterateAndNextConstNull(nodep->rangep()); + m_arrayPost = true; + } + iterateConst(nodep->subDTypep()); // For post's key + } } void visit(AstRefDType* nodep) override { if (nodep->subDTypep()) { @@ -639,21 +709,43 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } } void visit(AstNodeUOrStructDType* nodep) override { + if (m_arrayPost) return; puts(nodep->verilogKwd() + " "); if (nodep->packed()) puts("packed "); - puts("\n"); - puts("{"); - for (AstMemberDType* itemp = nodep->membersp(); itemp; - itemp = VN_AS(itemp->nextp(), MemberDType)) { - iterateConst(itemp); - puts(";"); + { + puts("{\n"); + VL_RESTORER(m_packedps); + m_packedps.clear(); + for (AstMemberDType* itemp = nodep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + iterateConst(itemp); + } + puts("}"); } - puts("}"); + emitPacked(); } void visit(AstMemberDType* nodep) override { + if (m_arrayPost) return; iterateConst(nodep->subDTypep()); puts(" "); puts(nodep->name()); + puts(";\n"); + } + void visit(AstQueueDType* nodep) override { + if (!m_arrayPost) { + iterateConst(nodep->subDTypep()); + } else { + VL_RESTORER(m_arrayPost); + m_arrayPost = false; + puts("[$"); + if (nodep->boundp()) { + puts(":"); + iterateConst(nodep->boundp()); + } + puts("]"); + m_arrayPost = true; + iterateConst(nodep->subDTypep()); // For post's key + } } void visit(AstNodeFTaskRef* nodep) override { if (nodep->dotted() != "") { @@ -716,37 +808,23 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { putfs(nodep, nodep->verilogKwd()); puts(" "); } - std::vector unpackps; - for (AstNodeDType* dtypep = nodep->dtypep(); dtypep;) { - dtypep = dtypep->skipRefp(); - if (const AstUnpackArrayDType* const unpackp = VN_CAST(dtypep, UnpackArrayDType)) { - unpackps.push_back(unpackp); - dtypep = unpackp->subDTypep(); - } else { - iterateConst(dtypep); - puts(" "); - puts(nodep->prettyName()); - dtypep = nullptr; - } - } - // If nodep is an unpacked array, append unpacked dimensions - for (const auto& unpackp : unpackps) { - puts("["); - puts(cvtToStr(unpackp->rangep()->leftConst())); - puts(":"); - puts(cvtToStr(unpackp->rangep()->rightConst())); - puts("]"); - } + VL_RESTORER(m_arrayPost); + m_arrayPost = false; + iterateConstNull(nodep->dtypep()); // Dtype part before identifier + puts(" "); + puts(nodep->prettyName()); + m_arrayPost = true; + iterateConstNull(nodep->dtypep()); // Dtype part after identifier puts(m_suppressVarSemi ? "\n" : ";\n"); } void visit(AstActive* nodep) override { + VL_RESTORER(m_sensesp); m_sensesp = nodep->sensesp(); iterateAndNextConstNull(nodep->stmtsp()); - m_sensesp = nullptr; } void visit(AstParseRef* nodep) override { puts(nodep->prettyName()); } - void visit(AstVarScope*) override {} void visit(AstNodeText*) override {} + void visit(AstVarScope*) override {} void visit(AstTraceDecl*) override {} void visit(AstTraceInc*) override {} // NOPs @@ -764,7 +842,6 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } public: - bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars explicit EmitVBaseVisitorConst(bool suppressUnknown, AstSenTree* domainp) : m_suppressUnknown{suppressUnknown} , m_sensesp{domainp} {} diff --git a/src/V3Error.cpp b/src/V3Error.cpp index 23c70a0131..e9ad855cbf 100644 --- a/src/V3Error.cpp +++ b/src/V3Error.cpp @@ -154,7 +154,7 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra) } // Suppress duplicate messages if (!m_messages.insert(msg).second) return; - if (!extra.empty()) { + if (!extra.empty() && !m_errorSuppressed) { const string extraMsg = warnMore() + extra + "\n"; const size_t pos = msg.find('\n'); msg.insert(pos + 1, extraMsg); diff --git a/src/V3ExecGraph.cpp b/src/V3ExecGraph.cpp index 5ccc71da52..49c8900348 100644 --- a/src/V3ExecGraph.cpp +++ b/src/V3ExecGraph.cpp @@ -528,9 +528,11 @@ void fillinCosts(V3Graph* execMTaskGraphp) { if (missingProfiles) { if (FileLine* const fl = V3Config::getProfileDataFileLine()) { - fl->v3warn(PROFOUTOFDATE, "Profile data for mtasks may be out of date. " - << missingProfiles << " of " << totalEstimates - << " mtasks had no data"); + if (V3Config::containsMTaskProfileData()) { + fl->v3warn(PROFOUTOFDATE, "Profile data for mtasks may be out of date. " + << missingProfiles << " of " << totalEstimates + << " mtasks had no data"); + } } } } diff --git a/src/V3LanguageWords.h b/src/V3LanguageWords.h index ba0f302de3..c04603efac 100644 --- a/src/V3LanguageWords.h +++ b/src/V3LanguageWords.h @@ -56,7 +56,6 @@ class V3LanguageWords final { inline void V3LanguageWords::Singleton::init() { // C++ keywords // clang-format off - addKwd("nullptr", "C++ common word"); addKwd("abort", "C++ common word"); addKwd("alignas", "C++11 keyword"); addKwd("alignof", "C++11 keyword"); @@ -91,7 +90,6 @@ inline void V3LanguageWords::Singleton::init() { addKwd("decltype", "C++11 keyword"); addKwd("default", "C++ keyword"); addKwd("delete", "C++ keyword"); - addKwd("deque", "C++ common word"); addKwd("do", "C++ keyword"); addKwd("double", "C++ keyword"); addKwd("dynamic_cast", "C++ keyword"); @@ -113,12 +111,8 @@ inline void V3LanguageWords::Singleton::init() { addKwd("int", "C++ keyword"); addKwd("interrupt", "C++ common word"); addKwd("iterator", "C++ common word"); - addKwd("list", "C++ common word"); addKwd("long", "C++ keyword"); - addKwd("map", "C++ common word"); addKwd("module", "C++ modules TS keyword"); - addKwd("std::multimap", "C++ common word"); - addKwd("std::multiset", "C++ common word"); addKwd("mutable", "C++ keyword"); addKwd("namespace", "C++ keyword"); addKwd("near", "C++ common word"); @@ -126,6 +120,7 @@ inline void V3LanguageWords::Singleton::init() { addKwd("noexcept", "C++11 keyword"); addKwd("not", "C++ keyword"); addKwd("not_eq", "C++ keyword"); + addKwd("nullptr", "C++ common word"); addKwd("nullptr", "C++11 keyword"); addKwd("operator", "C++ keyword"); addKwd("or", "C++ keyword"); @@ -142,7 +137,6 @@ inline void V3LanguageWords::Singleton::init() { addKwd("requires", "C++20 keyword"); addKwd("restrict", "C++ keyword"); addKwd("return", "C++ keyword"); - addKwd("set", "C++ common word"); addKwd("short", "C++ keyword"); addKwd("signed", "C++ keyword"); addKwd("sizeof", "C++ keyword"); @@ -150,6 +144,13 @@ inline void V3LanguageWords::Singleton::init() { addKwd("static", "C++ keyword"); addKwd("static_assert", "C++11 keyword"); addKwd("static_cast", "C++ keyword"); + addKwd("std::deque", "C++ common word"); + addKwd("std::list", "C++ common word"); + addKwd("std::map", "C++ common word"); + addKwd("std::multimap", "C++ common word"); + addKwd("std::multiset", "C++ common word"); + addKwd("std::set", "C++ common word"); + addKwd("std::vector", "C++ common word"); addKwd("struct", "C++ keyword"); addKwd("switch", "C++ keyword"); addKwd("synchronized", "C++ TM TS keyword"); @@ -171,7 +172,6 @@ inline void V3LanguageWords::Singleton::init() { addKwd("union", "C++ keyword"); addKwd("unsigned", "C++ keyword"); addKwd("using", "C++ keyword"); - addKwd("vector", "C++ common word"); addKwd("virtual", "C++ keyword"); addKwd("void", "C++ keyword"); addKwd("volatile", "C++ keyword"); diff --git a/src/V3Life.cpp b/src/V3Life.cpp index 47a7468b86..556fc23c03 100644 --- a/src/V3Life.cpp +++ b/src/V3Life.cpp @@ -287,8 +287,8 @@ class LifeVisitor final : public VNVisitor { } } void visit(AstNodeAssign* nodep) override { - if (nodep->isTimingControl()) { - // V3Life doesn't understand time sense - don't optimize + if (nodep->isTimingControl() || VN_IS(nodep, AssignForce)) { + // V3Life doesn't understand time sense nor force assigns - don't optimize setNoopt(); iterateChildren(nodep); return; diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 8c54b29848..9f77c8aad4 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -986,8 +986,8 @@ class LinkDotFindVisitor final : public VNVisitor { if (nodep->hierParams()) { UINFO(1, "Found module with hier type parameters" << endl); m_hierParamsName = nodep->name(); - for (const AstNode* node = nodep->op2p(); node; node = node->nextp()) { - if (const AstTypedef* const tdef = VN_CAST(node, Typedef)) { + for (const AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + if (const AstTypedef* const tdef = VN_CAST(stmtp, Typedef)) { UINFO(1, "Inserting hier type parameter typedef: " << tdef << endl); VSymEnt* const upperSymp = m_curSymp ? m_curSymp : m_statep->rootEntp(); m_curSymp = m_modSymp = m_statep->insertBlock(upperSymp, nodep->name(), @@ -1501,9 +1501,9 @@ class LinkDotFindVisitor final : public VNVisitor { if (const VSymEnt* const typedefEntp = m_curSymp->findIdFallback(m_hierParamsName)) { const AstModule* modp = VN_CAST(typedefEntp->nodep(), Module); - for (const AstNode* node = modp ? modp->stmtsp() : nullptr; node; - node = node->nextp()) { - const AstTypedef* tdefp = VN_CAST(node, Typedef); + for (const AstNode* stmtp = modp ? modp->stmtsp() : nullptr; stmtp; + stmtp = stmtp->nextp()) { + const AstTypedef* tdefp = VN_CAST(stmtp, Typedef); if (tdefp && tdefp->name() == nodep->name() && m_statep->forPrimary()) { UINFO(8, "Replacing type of" << nodep << endl diff --git a/src/V3Options.cpp b/src/V3Options.cpp index f4dc0f5730..1e2024d377 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -955,9 +955,6 @@ void V3Options::notify() VL_MT_DISABLED { cmdfl->v3error("Unsupported: --timing and --savable not supported together"); } - // Mark options as available - m_available = true; - // --dump-tree-dot will turn on tree dumping. if (!m_dumpLevel.count("tree") && m_dumpLevel.count("tree-dot")) { m_dumpLevel["tree"] = m_dumpLevel["tree-dot"]; @@ -965,9 +962,15 @@ void V3Options::notify() VL_MT_DISABLED { // Sanity check of expected configuration UASSERT(threads() >= 1, "'threads()' must return a value >= 1"); + if (m_buildJobs == -1) m_buildJobs = 1; + if (m_verilateJobs == -1) m_verilateJobs = 1; // Preprocessor defines based on options used if (timing().isSetTrue()) V3PreShell::defineCmdLine("VERILATOR_TIMING", "1"); + + // === Leave last + // Mark options as available + m_available = true; } //###################################################################### @@ -1233,8 +1236,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, } }); DECL_OPTION("-compiler-include", CbVal, callStrSetter(&V3Options::addCompilerIncludes)); - DECL_OPTION("-coverage", CbOnOff, [this](bool flag) { coverage(flag); }); DECL_OPTION("-converge-limit", Set, &m_convergeLimit); + DECL_OPTION("-coverage", CbOnOff, [this](bool flag) { coverage(flag); }); DECL_OPTION("-coverage-line", OnOff, &m_coverageLine); DECL_OPTION("-coverage-max-width", Set, &m_coverageMaxWidth); DECL_OPTION("-coverage-toggle", OnOff, &m_coverageToggle); @@ -1277,8 +1280,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-dumpi-", CbPartialMatchVal, [this](const char* optp, const char* valp) { m_dumpLevel[optp] = std::atoi(valp); }); - DECL_OPTION("-json-edit-nums", OnOff, &m_jsonEditNums); - DECL_OPTION("-json-ids", OnOff, &m_jsonIds); + DECL_OPTION("-E", CbOnOff, [this](bool flag) { if (flag) { m_stdPackage = false; @@ -1311,6 +1313,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-fconst", FOnOff, &m_fConst); DECL_OPTION("-fconst-before-dfg", FOnOff, &m_fConstBeforeDfg); DECL_OPTION("-fconst-bit-op-tree", FOnOff, &m_fConstBitOpTree); + DECL_OPTION("-fdead-assigns", FOnOff, &m_fDeadAssigns); + DECL_OPTION("-fdead-cells", FOnOff, &m_fDeadCells); DECL_OPTION("-fdedup", FOnOff, &m_fDedupe); DECL_OPTION("-fdfg", CbFOnOff, [this](bool flag) { m_fDfgPreInline = flag; @@ -1325,8 +1329,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, }); DECL_OPTION("-fdfg-pre-inline", FOnOff, &m_fDfgPreInline); DECL_OPTION("-fdfg-post-inline", FOnOff, &m_fDfgPostInline); - DECL_OPTION("-fdead-assigns", FOnOff, &m_fDeadAssigns); - DECL_OPTION("-fdead-cells", FOnOff, &m_fDeadCells); DECL_OPTION("-fexpand", FOnOff, &m_fExpand); DECL_OPTION("-ffunc-opt", CbFOnOff, [this](bool flag) { // m_fFuncSplitCat = flag; @@ -1388,6 +1390,18 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, if (m_instrCountDpi < 0) fl->v3fatal("--instr-count-dpi must be non-negative: " << val); }); + DECL_OPTION("-json-edit-nums", OnOff, &m_jsonEditNums); + DECL_OPTION("-json-ids", OnOff, &m_jsonIds); + DECL_OPTION("-json-only", OnOff, &m_jsonOnly); + DECL_OPTION("-json-only-meta-output", CbVal, [this](const char* valp) { + m_jsonOnlyMetaOutput = valp; + m_jsonOnly = true; + }); + DECL_OPTION("-json-only-output", CbVal, [this](const char* valp) { + m_jsonOnlyOutput = valp; + m_jsonOnly = true; + }); + DECL_OPTION("-LDFLAGS", CbVal, callStrSetter(&V3Options::addLdLibs)); DECL_OPTION("-l2-name", Set, &m_l2Name); DECL_OPTION("-no-l2name", CbCall, [this]() { m_l2Name = ""; }).undocumented(); // Historical @@ -1412,7 +1426,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, }); DECL_OPTION("-lint-only", OnOff, &m_lintOnly); DECL_OPTION("-localize-max-size", Set, &m_localizeMaxSize); - DECL_OPTION("-main-top-name", Set, &m_mainTopName); DECL_OPTION("-MAKEFLAGS", CbVal, callStrSetter(&V3Options::addMakeFlags)); DECL_OPTION("-MMD", OnOff, &m_makeDepend); @@ -1422,6 +1435,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, addIncDirFallback(m_makeDir); // Need to find generated files there too }); DECL_OPTION("-main", OnOff, &m_main); + DECL_OPTION("-main-top-name", Set, &m_mainTopName); DECL_OPTION("-make", CbVal, [this, fl](const char* valp) { if (!std::strcmp(valp, "cmake")) { m_cmake = true; @@ -1446,6 +1460,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-order-clock-delay", CbOnOff, [fl](bool /*flag*/) { fl->v3warn(DEPRECATED, "Option order-clock-delay is deprecated and has no effect."); }); + DECL_OPTION("-output-groups", CbVal, [this, fl](const char* valp) { + m_outputGroups = std::atoi(valp); + if (m_outputGroups < 0) { fl->v3error("--output-groups must be >= 0: " << valp); } + }); DECL_OPTION("-output-split", Set, &m_outputSplit); DECL_OPTION("-output-split-cfuncs", CbVal, [this, fl](const char* valp) { m_outputSplitCFuncs = std::atoi(valp); @@ -1459,20 +1477,15 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, fl->v3error("--output-split-ctrace must be >= 0: " << valp); } }); - DECL_OPTION("-output-groups", CbVal, [this, fl](const char* valp) { - m_outputGroups = std::atoi(valp); - if (m_outputGroups < 0) { fl->v3error("--output-groups must be >= 0: " << valp); } - }); DECL_OPTION("-P", Set, &m_preprocNoLine); - DECL_OPTION("-pvalue+", CbPartialMatch, - [this](const char* varp) { addParameter(varp, false); }); DECL_OPTION("-pins64", CbCall, [this]() { m_pinsBv = 65; }); DECL_OPTION("-no-pins64", CbCall, [this]() { m_pinsBv = 33; }); DECL_OPTION("-pins-bv", CbVal, [this, fl](const char* valp) { m_pinsBv = std::atoi(valp); if (m_pinsBv > 65) fl->v3fatal("--pins-bv maximum is 65: " << valp); }); + DECL_OPTION("-pins-inout-enables", OnOff, &m_pinsInoutEnables); DECL_OPTION("-pins-sc-uint", CbOnOff, [this](bool flag) { m_pinsScUint = flag; if (!m_pinsScBigUint) m_pinsBv = 65; @@ -1482,7 +1495,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, m_pinsScBigUint = flag; m_pinsBv = 513; }); - DECL_OPTION("-pins-inout-enables", OnOff, &m_pinsInoutEnables); DECL_OPTION("-pins-uint8", OnOff, &m_pinsUint8); DECL_OPTION("-pipe-filter", Set, &m_pipeFilter); DECL_OPTION("-pp-comments", OnOff, &m_ppComments); @@ -1490,13 +1502,17 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, validateIdentifier(fl, valp, "--prefix"); m_prefix = valp; }); + DECL_OPTION("-preproc-token-limit", CbVal, [this, fl](const char* valp) { + m_preprocTokenLimit = std::atoi(valp); + if (m_preprocTokenLimit <= 0) fl->v3error("--preproc-token-limit must be > 0: " << valp); + }); DECL_OPTION("-private", CbCall, [this]() { m_public = false; }); DECL_OPTION("-prof-c", OnOff, &m_profC); DECL_OPTION("-prof-cfuncs", CbCall, [this]() { m_profC = m_profCFuncs = true; }); - DECL_OPTION("-profile-cfuncs", CbCall, - [this]() { m_profC = m_profCFuncs = true; }); // Renamed DECL_OPTION("-prof-exec", OnOff, &m_profExec); DECL_OPTION("-prof-pgo", OnOff, &m_profPgo); + DECL_OPTION("-profile-cfuncs", CbCall, + [this]() { m_profC = m_profCFuncs = true; }); // Renamed DECL_OPTION("-protect-ids", OnOff, &m_protectIds); DECL_OPTION("-protect-key", Set, &m_protectKey); DECL_OPTION("-protect-lib", CbVal, [this, fl](const char* valp) { @@ -1515,6 +1531,9 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, m_publicParams = flag; v3Global.dpi(true); }); + DECL_OPTION("-pvalue+", CbPartialMatch, + [this](const char* varp) { addParameter(varp, false); }); + DECL_OPTION("-quiet", CbOnOff, [this](bool flag) { m_quietExit = flag; m_quietStats = flag; @@ -1560,7 +1579,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-structs-packed", OnOff, &m_structsPacked); DECL_OPTION("-sv", CbCall, [this]() { m_defaultLanguage = V3LangCode::L1800_2023; }); - DECL_OPTION("-threads-coarsen", OnOff, &m_threadsCoarsen).undocumented(); // Debug DECL_OPTION("-no-threads", CbCall, [this, fl]() { fl->v3warn(DEPRECATED, "Option --no-threads is deprecated, use '--threads 1' instead"); m_threads = 1; @@ -1573,6 +1591,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, m_threads = 1; } }); + DECL_OPTION("-threads-coarsen", OnOff, &m_threadsCoarsen).undocumented(); // Debug DECL_OPTION("-threads-dpi", CbVal, [this, fl](const char* valp) { if (!std::strcmp(valp, "all")) { m_threadsDpiPure = true; @@ -1614,9 +1633,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, } }); DECL_OPTION("-timing", OnOff, &m_timing); - DECL_OPTION("-top-module", Set, &m_topModule); DECL_OPTION("-top", Set, &m_topModule); - DECL_OPTION("-no-trace-top", Set, &m_noTraceTop); + DECL_OPTION("-top-module", Set, &m_topModule); DECL_OPTION("-trace", OnOff, &m_trace); DECL_OPTION("-trace-coverage", OnOff, &m_traceCoverage); DECL_OPTION("-trace-depth", Set, &m_traceDepth); @@ -1642,6 +1660,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, m_traceThreads = std::atoi(valp); if (m_traceThreads < 1) fl->v3fatal("--trace-threads must be >= 1: " << valp); }); + DECL_OPTION("-no-trace-top", Set, &m_noTraceTop); DECL_OPTION("-trace-underscore", OnOff, &m_traceUnderscore); DECL_OPTION("-U", CbPartialMatch, &V3PreShell::undef); @@ -1659,6 +1678,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, V3Options::addLibraryFile(parseFileArg(optdir, valp)); }); DECL_OPTION("-valgrind", CbCall, []() {}); // Processed only in bin/verilator shell + DECL_OPTION("-verilate", OnOff, &m_verilate); DECL_OPTION("-verilate-jobs", CbVal, [this, fl](const char* valp) { int val = std::atoi(valp); if (val < 0) { @@ -1670,17 +1690,12 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, } m_verilateJobs = val; }); - DECL_OPTION("-verilate", OnOff, &m_verilate); DECL_OPTION("-version", CbCall, [this]() { showVersion(false); std::exit(0); }); DECL_OPTION("-vpi", OnOff, &m_vpi); - DECL_OPTION("-Wpedantic", CbCall, [this]() { - m_pedantic = true; - V3Error::pretendError(V3ErrorCode::ASSIGNIN, false); - }); DECL_OPTION("-Wall", CbCall, []() { FileLine::globalWarnLintOff(false); FileLine::globalWarnStyleOff(false); @@ -1723,6 +1738,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-Wno-style", CbCall, []() { FileLine::globalWarnStyleOff(true); }); DECL_OPTION("-Wno-UNUSED", CbCall, []() { FileLine::globalWarnUnusedOff(true); }); DECL_OPTION("-Wno-WIDTH", CbCall, []() { FileLine::globalWarnOff(V3ErrorCode::WIDTH, true); }); + DECL_OPTION("-Wpedantic", CbCall, [this]() { + m_pedantic = true; + V3Error::pretendError(V3ErrorCode::ASSIGNIN, false); + }); DECL_OPTION("-Wwarn-", CbPartialMatch, [this, fl, &parser](const char* optp) VL_MT_DISABLED { const V3ErrorCode code{optp}; if (code == V3ErrorCode::EC_ERROR) { @@ -1792,19 +1811,11 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, m_xmlOutput = valp; m_xmlOnly = true; }); - DECL_OPTION("-json-only", OnOff, &m_jsonOnly); - DECL_OPTION("-json-only-output", CbVal, [this](const char* valp) { - m_jsonOnlyOutput = valp; - m_jsonOnly = true; - }); - DECL_OPTION("-json-only-meta-output", CbVal, [this](const char* valp) { - m_jsonOnlyMetaOutput = valp; - m_jsonOnly = true; - }); DECL_OPTION("-y", CbVal, [this, &optdir](const char* valp) { addIncDirUser(parseFileArg(optdir, string{valp})); }); + parser.finalize(); for (int i = 0; i < argc;) { @@ -1853,8 +1864,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, ++i; } } - if (m_buildJobs == -1) m_buildJobs = 1; - if (m_verilateJobs == -1) m_verilateJobs = 1; } //====================================================================== diff --git a/src/V3Options.h b/src/V3Options.h index b19c6dfad3..9ec3918de0 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -255,6 +255,7 @@ class V3Options final { bool m_flatten = false; // main switch: --flatten bool m_hierarchical = false; // main switch: --hierarchical bool m_ignc = false; // main switch: --ignc + bool m_jsonOnly = false; // main switch: --json-only bool m_lintOnly = false; // main switch: --lint-only bool m_gmake = false; // main switch: --make gmake bool m_main = false; // main switch: --main @@ -302,7 +303,6 @@ class V3Options final { bool m_waiverMultiline = false; // main switch: --waiver-multiline bool m_xInitialEdge = false; // main switch: --x-initial-edge bool m_xmlOnly = false; // main switch: --xml-only - bool m_jsonOnly = false; // main switch: --json-only int m_buildJobs = -1; // main switch: --build-jobs, -j int m_convergeLimit = 100; // main switch: --converge-limit @@ -324,6 +324,7 @@ class V3Options final { int m_outputSplitCFuncs = -1; // main switch: --output-split-cfuncs int m_outputSplitCTrace = -1; // main switch: --output-split-ctrace int m_pinsBv = 65; // main switch: --pins-bv + int m_preprocTokenLimit = 40000; // main switch: --preproc-token-limit int m_publicDepth = 0; // main switch: --public-depth int m_reloopLimit = 40; // main switch: --reloop-limit VOptionBool m_skipIdentical; // main switch: --skip-identical @@ -351,6 +352,8 @@ class V3Options final { string m_exeName; // main switch: -o {name} string m_flags; // main switch: -f {name} string m_hierParamsFile; // main switch: --hierarchical-params-file + string m_jsonOnlyOutput; // main switch: --json-only-output + string m_jsonOnlyMetaOutput; // main switch: --json-only-meta-output string m_l2Name; // main switch: --l2name; "" for top-module's name string m_libCreate; // main switch: --lib-create {lib_name} string m_mainTopName; // main switch: --main-top-name @@ -365,8 +368,6 @@ class V3Options final { string m_xAssign; // main switch: --x-assign string m_xInitial; // main switch: --x-initial string m_xmlOutput; // main switch: --xml-output - string m_jsonOnlyOutput; // main switch: --json-only-output - string m_jsonOnlyMetaOutput; // main switch: --json-only-meta-output // Language is now held in FileLine, on a per-node basis. However we still // have a concept of the default language at a global level. @@ -465,6 +466,7 @@ class V3Options final { bool preprocOnly() const { return m_preprocOnly; } bool makePhony() const { return m_makePhony; } bool preprocNoLine() const { return m_preprocNoLine; } + int preprocTokenLimit() const { return m_preprocTokenLimit; } bool underlineZero() const { return m_underlineZero; } string flags() const { return m_flags; } bool systemC() const VL_MT_SAFE { return m_systemC; } @@ -527,6 +529,7 @@ class V3Options final { bool traceUnderscore() const { return m_traceUnderscore; } bool main() const { return m_main; } bool outFormatOk() const { return m_outFormatOk; } + bool jsonOnly() const { return m_jsonOnly; } bool keepTempFiles() const { return (V3Error::debugDefault() != 0); } bool pedantic() const { return m_pedantic; } bool pinsInoutEnables() const { return m_pinsInoutEnables; } @@ -557,7 +560,6 @@ class V3Options final { bool waiverMultiline() const { return m_waiverMultiline; } bool xInitialEdge() const { return m_xInitialEdge; } bool xmlOnly() const { return m_xmlOnly; } - bool jsonOnly() const { return m_jsonOnly; } bool serializeOnly() const { return m_xmlOnly || m_jsonOnly; } bool topIfacesSupported() const { return lintOnly() && !hierarchical(); } @@ -617,6 +619,8 @@ class V3Options final { string exeName() const { return m_exeName != "" ? m_exeName : prefix(); } string hierParamFile() const { return m_hierParamsFile; } + string jsonOnlyOutput() const { return m_jsonOnlyOutput; } + string jsonOnlyMetaOutput() const { return m_jsonOnlyMetaOutput; } string l2Name() const { return m_l2Name; } string libCreate() const { return m_libCreate; } string libCreateName(bool shared) { @@ -644,8 +648,6 @@ class V3Options final { string xAssign() const { return m_xAssign; } string xInitial() const { return m_xInitial; } string xmlOutput() const { return m_xmlOutput; } - string jsonOnlyOutput() const { return m_jsonOnlyOutput; } - string jsonOnlyMetaOutput() const { return m_jsonOnlyMetaOutput; } const V3StringSet& cppFiles() const { return m_cppFiles; } const V3StringList& cFlags() const { return m_cFlags; } diff --git a/src/V3OrderParallel.cpp b/src/V3OrderParallel.cpp index 67fe62bd3c..3c410d1bb6 100644 --- a/src/V3OrderParallel.cpp +++ b/src/V3OrderParallel.cpp @@ -1710,7 +1710,11 @@ class DpiImportCallVisitor final : public VNVisitor { if (nodep->dpiImportWrapper()) { if (nodep->dpiPure() ? !v3Global.opt.threadsDpiPure() : !v3Global.opt.threadsDpiUnpure()) { - m_hasDpiHazard = true; + // If hierarchical DPI wrapper cost is not found or is of a 0 cost, + // we have a normal DPI which induces DPI hazard by default. + m_hasDpiHazard = V3Config::getProfileData(nodep->cname()) == 0; + UINFO(9, "DPI wrapper '" << nodep->cname() + << "' has dpi hazard = " << m_hasDpiHazard << endl); } } iterateChildren(nodep); diff --git a/src/V3Os.cpp b/src/V3Os.cpp index cd38daf44a..342c01f85d 100644 --- a/src/V3Os.cpp +++ b/src/V3Os.cpp @@ -91,9 +91,9 @@ string V3Os::getenvStr(const string& envvar, const string& defaultValue) { void V3Os::setenvStr(const string& envvar, const string& value, const string& why) { if (why != "") { - UINFO(1, "export " << envvar << "=" << value << " # " << why << endl); + UINFO(1, "export " << envvar << "='" << value << "' # " << why << endl); } else { - UINFO(1, "export " << envvar << "=" << value << endl); + UINFO(1, "export " << envvar << "='" << value << "'" << endl); } #if defined(_WIN32) || defined(__MINGW32__) _putenv_s(envvar.c_str(), value.c_str()); diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 17ecb785b1..5cea41c8b5 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -543,18 +543,18 @@ class ParamProcessor final { for (auto&& defaultValue : paramsIt->second) { const auto pinIt = pins.find(defaultValue.first); // If the pin does not have a value assigned, use the default one. - const AstNode* const node = pinIt == pins.end() ? defaultValue.second : pinIt->second; + const AstNode* const nodep = pinIt == pins.end() ? defaultValue.second : pinIt->second; // This longname is not valid as verilog symbol, but ok, because it will be hashed longname += "_" + defaultValue.first + "="; // constp can be nullptr - if (const AstConst* const p = VN_CAST(node, Const)) { + if (const AstConst* const p = VN_CAST(nodep, Const)) { // Treat modules parametrized with the same values but with different type as the // same. longname += p->num().ascii(false); - } else if (node) { + } else if (nodep) { std::stringstream type; - V3EmitV::verilogForTree(node, type); + V3EmitV::verilogForTree(nodep, type); longname += type.str(); } } diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp index 203faf5bf1..fa41f55825 100644 --- a/src/V3PreProc.cpp +++ b/src/V3PreProc.cpp @@ -963,9 +963,9 @@ int V3PreProcImp::getRawToken() { if (m_lastLineno != m_lexp->m_tokFilelinep->lineno()) { m_lastLineno = m_lexp->m_tokFilelinep->lineno(); m_tokensOnLine = 0; - } else if (++m_tokensOnLine > LINE_TOKEN_MAX) { - error("Too many preprocessor tokens on a line (>" + cvtToStr(LINE_TOKEN_MAX) - + "); perhaps recursive `define"); + } else if (++m_tokensOnLine > v3Global.opt.preprocTokenLimit()) { + error("Too many preprocessor tokens on a line (>" + + cvtToStr(v3Global.opt.preprocTokenLimit()) + "); perhaps recursive `define"); tok = VP_EOF_ERROR; } diff --git a/src/V3PreProc.h b/src/V3PreProc.h index da6c0d8d80..608447d4b3 100644 --- a/src/V3PreProc.h +++ b/src/V3PreProc.h @@ -42,7 +42,6 @@ class V3PreProc VL_NOT_FINAL { // CONSTANTS enum MiscConsts { DEFINE_RECURSION_LEVEL_MAX = 1000, // How many `def substitutions 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 ec66dba2d6..7ca0881133 100644 --- a/src/V3ProtectLib.cpp +++ b/src/V3ProtectLib.cpp @@ -19,6 +19,7 @@ #include "V3ProtectLib.h" #include "V3Hasher.h" +#include "V3InstrCount.h" #include "V3String.h" #include "V3Task.h" @@ -98,6 +99,32 @@ class ProtectVisitor final : public VNVisitor { txtp->addNodesp(new AstComment{fl, comment}); } + void configSection(AstNodeModule* modp, AstTextBlock* txtp, FileLine* fl) { + txtp->addText(fl, "\n`ifdef VERILATOR\n"); + txtp->addText(fl, "`verilator_config\n"); + + // The `eval` function is called inside both update functions. As those functions + // are created by text bashing, we need to find cost of `_eval` which is the first function + // with a real cost in AST. + uint32_t cost = 0; + modp->foreach([&cost](AstCFunc* cfuncp) { + if (cfuncp->name() == "_eval") cost = V3InstrCount::count(cfuncp, false); + }); + txtp->addText(fl, "profile_data -hier-dpi \"" + m_libName + + "_protectlib_combo_update\" -cost 64'd" + std::to_string(cost) + + "\n"); + txtp->addText(fl, "profile_data -hier-dpi \"" + m_libName + + "_protectlib_seq_update\" -cost 64'd" + std::to_string(cost) + + "\n"); + + // Mark remaining NDA protectlib wrapper DPIs as non-hazardous by deliberately forwarding + // them with non-zero cost. + txtp->addText(fl, "profile_data -hier-dpi \"" + m_libName + + "_protectlib_combo_ignore\" -cost 64'd1\n"); + txtp->addText(fl, "`verilog\n"); + txtp->addText(fl, "`endif\n"); + } + void hashComment(AstTextBlock* txtp, FileLine* fl) { addComment(txtp, fl, "Checks to make sure the .sv wrapper and library agree"); } @@ -283,6 +310,9 @@ class ProtectVisitor final : public VNVisitor { txtp->addText(fl, "final " + m_libName + "_protectlib_final(handle__V);\n\n"); txtp->addText(fl, "endmodule\n"); + + configSection(modp, txtp, fl); + m_vfilep->tblockp(txtp); } diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 6baa90919b..d6b8b67ef5 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -629,6 +629,11 @@ class ConstraintExprVisitor final : public VNVisitor { const uint32_t unpackedDimensions = dims.second; dimension = unpackedDimensions; } + if (VN_IS(varp->dtypeSkipRefp(), StructDType) + && !VN_AS(varp->dtypeSkipRefp(), StructDType)->packed()) { + VN_AS(varp->dtypeSkipRefp(), StructDType)->markConstrainedRand(true); + dimension = 1; + } methodp->dtypeSetVoid(); AstClass* const classp = VN_AS(varp->user2p(), Class); AstVarRef* const varRefp @@ -706,6 +711,26 @@ class ConstraintExprVisitor final : public VNVisitor { editSMT(nodep, nodep->fromp(), lsbp, msbp); } + void visit(AstStructSel* nodep) override { + if (VN_IS(nodep->fromp()->dtypep()->skipRefp(), StructDType)) { + AstMemberDType* memberp + = VN_AS(nodep->fromp()->dtypep()->skipRefp(), StructDType)->membersp(); + while (memberp->nextp()) { + if (memberp->name() == nodep->name()) { + memberp->markConstrainedRand(true); + break; + } else + memberp = VN_CAST(memberp->nextp(), MemberDType); + } + } + iterateChildren(nodep); + if (editFormat(nodep)) return; + FileLine* const fl = nodep->fileline(); + AstSFormatF* const newp + = new AstSFormatF{fl, nodep->fromp()->name() + "." + nodep->name(), false, nullptr}; + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } void visit(AstAssocSel* nodep) override { if (editFormat(nodep)) return; FileLine* const fl = nodep->fileline(); diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index 61a7f21d11..76f6cfbdcf 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -70,6 +70,7 @@ AstCFunc* makeSubFunction(AstNetlist* netlistp, const string& name, bool slow) { AstCFunc* makeTopFunction(AstNetlist* netlistp, const string& name, bool slow) { AstCFunc* const funcp = makeSubFunction(netlistp, name, slow); funcp->entryPoint(true); + funcp->keepIfEmpty(true); return funcp; } diff --git a/src/V3SenExprBuilder.h b/src/V3SenExprBuilder.h index f837a960c1..0149a2a568 100644 --- a/src/V3SenExprBuilder.h +++ b/src/V3SenExprBuilder.h @@ -182,16 +182,11 @@ class SenExprBuilder final { case VEdgeType::ET_EVENT: { UASSERT_OBJ(v3Global.hasEvents(), senItemp, "Inconsistent"); { - // If the event is fired, set up the clearing process - AstCMethodHard* const callp = new AstCMethodHard{flp, currp(), "isFired"}; - callp->dtypeSetBit(); - AstIf* const ifp = new AstIf{flp, callp}; - m_postUpdates.push_back(ifp); - // Clear 'fired' state when done + // No need to check if the event was fired, we need the flag clear regardless AstCMethodHard* const clearp = new AstCMethodHard{flp, currp(), "clearFired"}; clearp->dtypeSetVoid(); - ifp->addThensp(clearp->makeStmt()); + m_postUpdates.push_back(clearp->makeStmt()); } // Get 'fired' state diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 0fe3624d38..eb87161336 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -27,6 +27,7 @@ #include "V3Task.h" +#include "V3Config.h" #include "V3Const.h" #include "V3EmitCBase.h" #include "V3Graph.h" @@ -372,6 +373,7 @@ class TaskVisitor final : public VNVisitor { // STATE - across all visitors DpiCFuncs m_dpiNames; // Map of all created DPI functions VDouble0 m_statInlines; // Statistic tracking + VDouble0 m_statHierDpisWithCosts; // Statistic tracking // METHODS @@ -980,6 +982,11 @@ class TaskVisitor final : public VNVisitor { funcp->isMethod(false); funcp->protect(false); funcp->dpiPure(nodep->dpiPure()); + + const int cost = static_cast(V3Config::getProfileData(funcp->name())); + m_statHierDpisWithCosts += (cost != 0); + funcp->cost(cost); + // Add DPI Import to top, since it's a global function m_topScopep->scopep()->addBlocksp(funcp); makePortList(nodep, funcp); @@ -1259,6 +1266,8 @@ class TaskVisitor final : public VNVisitor { if (nodep->name() == "new") cfuncp->isConstructor(true); if (cfuncp->dpiExportImpl()) cfuncp->cname(nodep->cname()); + if (cfuncp->dpiImportWrapper()) cfuncp->cname(nodep->cname()); + if (!nodep->dpiImport() && !nodep->taskPublic()) { // Need symbol table cfuncp->argTypes(EmitCBase::symClassVar()); @@ -1613,7 +1622,11 @@ class TaskVisitor final : public VNVisitor { : m_statep{statep} { iterate(nodep); } - ~TaskVisitor() { V3Stats::addStat("Optimizations, Functions inlined", m_statInlines); } + ~TaskVisitor() { + V3Stats::addStat("Optimizations, Functions inlined", m_statInlines); + V3Stats::addStat("Optimizations, Hierarchical DPI wrappers with costs", + m_statHierDpisWithCosts); + } }; //###################################################################### diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index ac707874b2..d36083c5a7 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -57,7 +57,7 @@ class UnrollVisitor final : public VNVisitor { bool cantUnroll(AstNode* nodep, const char* reason) const { if (m_generate) nodep->v3warn(E_UNSUPPORTED, "Unsupported: Can't unroll generate for; " << reason); - UINFO(3, " Can't Unroll: " << reason << " :" << nodep << endl); + UINFO(4, " Can't Unroll: " << reason << " :" << nodep << endl); // if (debug() >= 9) nodep->dumpTree("- cant: "); V3Stats::addStatSum("Unrolling gave up, "s + reason, 1); return false; @@ -210,7 +210,7 @@ class UnrollVisitor final : public VNVisitor { SimulateVisitor simvis; simvis.mainParamEmulate(clonep); if (!simvis.optimizable()) { - UINFO(3, "Unable to simulate" << endl); + UINFO(4, "Unable to simulate" << endl); if (debug() >= 9) nodep->dumpTree("- _simtree: "); VL_DO_DANGLING(clonep->deleteTree(), clonep); return false; diff --git a/src/verilog.l b/src/verilog.l index 5aa52e4ed1..7e96bdf448 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -141,6 +141,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} -?"-cost" { FL; return yVLT_D_COST; } -?"-file" { FL; return yVLT_D_FILE; } -?"-function" { FL; return yVLT_D_FUNCTION; } + -?"-hier-dpi" { FL; return yVLT_D_HIER_DPI; } -?"-levels" { FL; return yVLT_D_LEVELS; } -?"-lines" { FL; return yVLT_D_LINES; } -?"-match" { FL; return yVLT_D_MATCH; } diff --git a/src/verilog.y b/src/verilog.y index 8e6a48ea76..3d66945f02 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -492,6 +492,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yVLT_D_COST "--cost" %token yVLT_D_FILE "--file" %token yVLT_D_FUNCTION "--function" +%token yVLT_D_HIER_DPI "--hier-dpi" %token yVLT_D_LEVELS "--levels" %token yVLT_D_LINES "--lines" %token yVLT_D_MATCH "--match" @@ -7655,6 +7656,8 @@ vltItem: { V3Config::addCaseParallel(*$2, 0); } | yVLT_PARALLEL_CASE vltDFile yVLT_D_LINES yaINTNUM { V3Config::addCaseParallel(*$2, $4->toUInt()); } + | yVLT_PROFILE_DATA vltDHierDpi vltDCost + { V3Config::addProfileData($1, *$2, $3->toUQuad()); } | yVLT_PROFILE_DATA vltDModel vltDMtask vltDCost { V3Config::addProfileData($1, *$2, *$3, $4->toUQuad()); } ; @@ -7699,6 +7702,10 @@ vltDFile: // --file yVLT_D_FILE str { $$ = $2; } ; +vltDHierDpi: // --hier-dpi + yVLT_D_HIER_DPI str { $$ = $2; } + ; + vltDLevels: // --levels yVLT_D_LEVELS yaINTNUM { $$ = $2; } ; diff --git a/test_regress/t/TestVpi.h b/test_regress/t/TestVpi.h index 4a87b8e514..d2428ba4ae 100644 --- a/test_regress/t/TestVpi.h +++ b/test_regress/t/TestVpi.h @@ -12,6 +12,8 @@ #include "sv_vpi_user.h" #include "vpi_user.h" +#include + // Avoid C++11 in this file as not all simulators allow it //====================================================================== diff --git a/test_regress/t/t_class_eq.py b/test_regress/t/t_class_eq.py new file mode 100755 index 0000000000..bd059b0f2f --- /dev/null +++ b/test_regress/t/t_class_eq.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=['--binary']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_eq.v b/test_regress/t/t_class_eq.v new file mode 100644 index 0000000000..d6738ae570 --- /dev/null +++ b/test_regress/t/t_class_eq.v @@ -0,0 +1,32 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t(); + +class A; + int num; + function new(int num); + this.num = num; + endfunction +endclass + +class B; + static A obj = new(2); +endclass + +class C; + static A obj = new(5); +endclass + + initial begin + #1; + $display("Bobj=%p Cobj=%p eq=%p", B::obj, C::obj, (B::obj == C::obj)); + if (B::obj == C::obj) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_constraint_struct.py b/test_regress/t/t_constraint_struct.py new file mode 100755 index 0000000000..a2b131082a --- /dev/null +++ b/test_regress/t/t_constraint_struct.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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 + +import vltest_bootstrap + +test.scenarios('simulator') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_constraint_struct.v b/test_regress/t/t_constraint_struct.v new file mode 100755 index 0000000000..4462017bb0 --- /dev/null +++ b/test_regress/t/t_constraint_struct.v @@ -0,0 +1,87 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +typedef struct packed { + bit [7:0] byte_value; + int int_value; +} PackedStruct; + +typedef struct { + rand bit [7:0] byte_value; + rand int int_value; + int non_rand_value; // Non-randomized member +} UnpackedStruct; + +class PackedStructTest; + rand PackedStruct packed_struct; + + function new(); + packed_struct.byte_value = 8'hA0; + packed_struct.int_value = 0; + endfunction + + // Constraint block for packed struct + constraint packed_struct_constraint { + packed_struct.byte_value == 8'hA0; + packed_struct.int_value inside {[0:100]}; + } + + // Self-check function for packed struct + function void check(); + if (packed_struct.byte_value != 8'hA0) $stop; + if (!(packed_struct.int_value inside {[0:100]})) $stop; + endfunction +endclass + +class UnpackedStructTest; + rand UnpackedStruct unpacked_struct; + + function new(); + unpacked_struct.byte_value = 8'h00; + unpacked_struct.int_value = 0; + unpacked_struct.non_rand_value = 42; + endfunction + + // Constraint block for unpacked struct + constraint unpacked_struct_constraint { + unpacked_struct.byte_value inside {8'hA0, 8'hB0, 8'hC0}; + unpacked_struct.int_value inside {[50:150]}; + } + + // Self-check function for unpacked struct + function void check(); + if (!(unpacked_struct.byte_value inside {8'hA0, 8'hB0, 8'hC0})) $stop; + if (!(unpacked_struct.int_value inside {[50:150]})) $stop; + if (unpacked_struct.non_rand_value != 42) $stop; // Check non-randomized member + endfunction +endclass + +module t_constraint_struct; + PackedStructTest packed_struct_test; + UnpackedStructTest unpacked_struct_test; + int success; + + initial begin + // Test packed struct + packed_struct_test = new(); + repeat(10) begin + success = packed_struct_test.randomize(); + if (success == 0) $stop; + packed_struct_test.check(); // Self-check for packed struct + end + + // Test unpacked struct + unpacked_struct_test = new(); + repeat(10) begin + success = unpacked_struct_test.randomize(); + if (success == 0) $stop; + unpacked_struct_test.check(); // Self-check for unpacked struct + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index 20e344b0f0..d7337c9317 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -1,99 +1,70 @@ module Vt_debug_emitv_t; input logic clk; input logic in; - typedef - ???? // ENUMDTYPE 't.e_t' - - ???? // ENUMITEM 'ZERO' - 32'h0 - ???? // ENUMITEM 'ONE' - 32'h1int signed [31:0] struct packed - {int signed [31:0] a;}logic signed [2:0] struct - {logic signed [2:0] a;}logicunion - {logic a;}struct packed - {int signed [31:0] a;}bit [31:0] const struct packed - {int signed [31:0] a;}const struct packed - {int signed [31:0] a;}[0:2]struct - {logic signed [2:0] a;}union - {logic a;}int signed [31:0] int signed [31:0] [0:2]logic [15:0] logic [15:0] logic [15:0] int signed [31:0] int signed [31:0] int signed [31:0] - ???? // QUEUEDTYPE - int signed [31:0] string - ???? // ASSOCARRAYDTYPE - int signed [31:0] - ???? // DYNARRAYDTYPE - int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] real signedstringIData [31:0] logic signed [31:0] int signed [31:0] e_t; - typedef struct packed - {int signed [31:0] a;}logic signed [2:0] struct - {logic signed [2:0] a;}logicunion - {logic a;}struct packed - {int signed [31:0] a;}bit [31:0] const struct packed - {int signed [31:0] a;}const struct packed - {int signed [31:0] a;}[0:2]struct - {logic signed [2:0] a;}union - {logic a;}int signed [31:0] int signed [31:0] [0:2]logic [15:0] logic [15:0] logic [15:0] int signed [31:0] int signed [31:0] int signed [31:0] - ???? // QUEUEDTYPE - int signed [31:0] string - ???? // ASSOCARRAYDTYPE - int signed [31:0] - ???? // DYNARRAYDTYPE - int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] real signedstringIData [31:0] logic signed [31:0] int signed [31:0] ps_t; - typedef struct - {logic signed [2:0] a;}logicunion - {logic a;}struct packed - {int signed [31:0] a;}bit [31:0] const struct packed - {int signed [31:0] a;}const struct packed - {int signed [31:0] a;}[0:2]struct - {logic signed [2:0] a;}union - {logic a;}int signed [31:0] int signed [31:0] [0:2]logic [15:0] logic [15:0] logic [15:0] int signed [31:0] int signed [31:0] int signed [31:0] - ???? // QUEUEDTYPE - int signed [31:0] string - ???? // ASSOCARRAYDTYPE - int signed [31:0] - ???? // DYNARRAYDTYPE - int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] real signedstringIData [31:0] logic signed [31:0] int signed [31:0] us_t; - typedef union - {logic a;}struct packed - {int signed [31:0] a;}bit [31:0] const struct packed - {int signed [31:0] a;}const struct packed - {int signed [31:0] a;}[0:2]struct - {logic signed [2:0] a;}union - {logic a;}int signed [31:0] int signed [31:0] [0:2]logic [15:0] logic [15:0] logic [15:0] int signed [31:0] int signed [31:0] int signed [31:0] - ???? // QUEUEDTYPE - int signed [31:0] string - ???? // ASSOCARRAYDTYPE - int signed [31:0] - ???? // DYNARRAYDTYPE - int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] real signedstringIData [31:0] logic signed [31:0] int signed [31:0] union_t; - struct packed - {int signed [31:0] a;} ps[0:2]; - struct - {logic signed [2:0] a;} us; - union - {logic a;} unu; - int signed [31:0] array[0:2]; + typedef enum logic [2:0] { + ZERO = 3'h0, + ONE = 3'h1 + } e_t; + typedef struct packed { + logic [2:0] a; + } ps_t; + typedef struct { + logic signed [2:0] a; + } us_t; + typedef union { + logic a; + } union_t; + const struct packed { + logic [2:0] a; + } ps[0:2]; + struct { + logic signed [2:0] a; + } us; + union { + logic a; + } unu; + int signed array[0:2]; initial begin array = '{0:32'sh1, 1:32'sh2, 2:32'sh3}; end + bit [6:5] [4:3] [2:1] arraymanyd[10:11][12:13][14:15]; logic [15:0] pubflat; logic [15:0] pubflat_r; logic [15:0] pubflat_w; assign pubflat_w = pubflat; - int signed [31:0] fd; - int signed [31:0] i; - - ???? // QUEUEDTYPE - q; - - ???? // ASSOCARRAYDTYPE - assoc; - - ???? // DYNARRAYDTYPE - dyn; + int signed fd; + int signed i; + int signed q[$]; + int signed qb[$:'sh3]; + int signed assoc[string]; + int signed assocassoc[string][real]; + int signed dyn[]; + typedef struct packed { + logic nn1; + } nested_named_t; + typedef struct packed { + struct packed { + logic nn2; + } nested_anonymous; + struct packed { + logic nn1; + } nested_named; + logic [11:10] nn3; + } nibble_t; + struct packed { + struct packed { + logic nn2; + } nested_anonymous; + struct packed { + logic nn1; + } nested_named; + logic [11:10] nn3; + } [5:4] nibblearray[3:2]; task t; $display("stmt"); endtask function f; - input int signed [31:0] v; + input int signed v; begin : label0 $display("stmt"); f = ((v == 'sh0) ? 'sh63 : ((~ v) + 'sh1)); @@ -102,16 +73,16 @@ module Vt_debug_emitv_t; endfunction initial begin begin : unnamedblk1 - int signed [31:0] other; + int signed other; begin begin : unnamedblk2 - int signed [31:0] i; + int signed i; i = 'sh0; while ((i < 'sh3)) begin begin other = f(i); $display("stmt %~ %~", - iother, other); + i, other); t(); end i = (i + 'h1); @@ -128,7 +99,7 @@ module Vt_debug_emitv_t; $display("stmt"); end end - always @([changed] in) begin + always @( in) begin begin $display("stmt"); end @@ -144,10 +115,10 @@ module Vt_debug_emitv_t; $display("negedge clk, pfr = %x", pubflat_r); end end - int signed [31:0] cyc; - int signed [31:0] fo; - int signed [31:0] sum; - real signed r; + int signed cyc; + int signed fo; + int signed sum; + real r; string str; always @(posedge clk) begin begin @@ -156,7 +127,7 @@ module Vt_debug_emitv_t; fo = cyc; sub.inc(fosum); sum = sub.f(sum); - $display("[%0t] sum = %~", $timesum, sum); + $display("[%0t] sum = %~", $time, sum); $display("a?= %d", ($c('sh1) ? $c('sh14) : $c('sh1e))); $c(;); @@ -177,7 +148,7 @@ module Vt_debug_emitv_t; $readmemh(fd, array, 'sh0, 'sh0); sum = 'sh0; begin : unnamedblk3 - int signed [31:0] i; + int signed i; i = 'sh0; begin : label0 while ((i < cyc)) begin @@ -248,32 +219,23 @@ module Vt_debug_emitv_t; else begin $display("0"); end - $display("%~%~", $past(cyc)$past(cyc, 'sh1), - $past(cyc, 'sh1)); + $display("%~%~", $past(cyc), $past(cyc, + 'sh1)); str = $sformatf("cyc=%~", cyc); ; $display("str = %@", str); - $display("%% [%t] [%^] to=%o td=%d", $time - $realtime$time$time, $realtime - $time$time, $time$time, $time); + $display("%% [%t] [%^] to=%o td=%d", $time, + $realtime, $time, $time); $sscanf(40'h666f6f3d35, "foo=%d", i); ; $printtimescale; if ((i != 'sh5)) begin $stop; end - sum = - ???? // RAND - ; - sum = - ???? // RAND - 'sha; - sum = - ???? // RAND - ; - sum = - ???? // RAND - 'sha; + sum = $random(); + sum = $random('sha); + sum = $urandom(); + sum = $urandom('sha); if ((PKG_PARAM != 'sh1)) begin $stop; end @@ -298,7 +260,7 @@ module Vt_debug_emitv_t; $display("%g", $atanh(r)); force sum = 'sha; begin : unnamedblk1_1 - integer signed [31:0] __Vrepeat0; + integer signed __Vrepeat0; __Vrepeat0 = 'sh2; while ((__Vrepeat0 > 32'h0)) begin if ((sum != 'sha)) begin @@ -314,22 +276,22 @@ module Vt_debug_emitv_t; endmodule package Vt_debug_emitv___024unit; class Vt_debug_emitv_Cls; - int signed [31:0] member; + int signed member; member = 'sh1; task method; endtask - task new; - endtask + function new; + endfunction endclass endpackage module Vt_debug_emitv_sub; task inc; - input int signed [31:0] i; - output int signed [31:0] o; + input int signed i; + output int signed o; o = ({32'h1{{1'h0, i[31:1]}}} + 32'h1); endtask function f; - input int signed [31:0] v; + input int signed v; begin : label0 if ((v == 'sh0)) begin f = 'sh21; @@ -339,7 +301,7 @@ module Vt_debug_emitv_sub; disable label0; end endfunction - real signed r; + real r; endmodule package Vt_debug_emitv_p; logic pkgvar; diff --git a/test_regress/t/t_debug_emitv.py b/test_regress/t/t_debug_emitv.py index 6063ac2c8e..227968047e 100755 --- a/test_regress/t/t_debug_emitv.py +++ b/test_regress/t/t_debug_emitv.py @@ -16,7 +16,16 @@ # Likewise XML v_flags=["--lint-only --dumpi-tree 9 --dumpi-V3EmitV 9 --debug-emitv"]) -test.files_identical(test.glob_one(test.obj_dir + "/" + test.vm_prefix + "_*_width.tree.v"), - test.golden_filename) +output_v = test.glob_one(test.obj_dir + "/" + test.vm_prefix + "_*_width.tree.v") + +test.files_identical(output_v, test.golden_filename) + +if test.verbose: + # Print if that the output Verilog is clean + # TODO not yet round-trip clean + test.run(cmd=[os.environ["VERILATOR_ROOT"] + "/bin/verilator", "--lint-only", output_v], + logfile=test.obj_dir + "/sim_roundtrip.log", + fails=True, + verilator_run=True) test.passes() diff --git a/test_regress/t/t_debug_emitv.v b/test_regress/t/t_debug_emitv.v index 989888d8c7..8c7ba061a4 100644 --- a/test_regress/t/t_debug_emitv.v +++ b/test_regress/t/t_debug_emitv.v @@ -33,7 +33,7 @@ module t (/*AUTOARG*/ // verilator lint_off UNPACKED - typedef enum { + typedef enum [2:0] { ZERO, ONE = 1 } e_t; @@ -52,19 +52,35 @@ module t (/*AUTOARG*/ us_t us; union_t unu; - int array[3]; + int array[3]; initial array = '{1,2,3}; - reg [15:0] pubflat /*verilator public_flat_rw @(posedge clk) */; + bit [6:5][4:3][2:1] arraymanyd[10:11][12:13][14:15]; - reg [15:0] pubflat_r; - wire [15:0] pubflat_w = pubflat; - int fd; - int i; + reg [15:0] pubflat /*verilator public_flat_rw @(posedge clk) */; - int q[$]; - int assoc[string]; - int dyn[]; + reg [15:0] pubflat_r; + wire [15:0] pubflat_w = pubflat; + int fd; + int i; + + int q[$]; + int qb[$ : 3]; + int assoc[string]; + int assocassoc[string][real]; + int dyn[]; + + typedef struct packed { + logic nn1; + } nested_named_t; + typedef struct packed { + struct packed { + logic nn2; + } nested_anonymous; + nested_named_t nested_named; + logic [11:10] nn3; + } nibble_t; + nibble_t [5:4] nibblearray[3:2]; task t; $display("stmt"); diff --git a/test_regress/t/t_exprstmt_on_lhs_of_nba.py b/test_regress/t/t_exprstmt_on_lhs_of_nba.py new file mode 100755 index 0000000000..319c0ff4a9 --- /dev/null +++ b/test_regress/t/t_exprstmt_on_lhs_of_nba.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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 + +import vltest_bootstrap + +test.scenarios('vlt') + +test.compile() + +test.passes() diff --git a/test_regress/t/t_exprstmt_on_lhs_of_nba.v b/test_regress/t/t_exprstmt_on_lhs_of_nba.v new file mode 100644 index 0000000000..e9c7efb4e7 --- /dev/null +++ b/test_regress/t/t_exprstmt_on_lhs_of_nba.v @@ -0,0 +1,104 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t(/*AUTOARG*/ + // Outputs + data_o, + // Inputs + clk, rst_i, write_valid_i, write_front_i, read_valid_i, data_i + ); + + localparam NR_ELEMENTS = 16; + localparam DATAW = 32; + + input clk; + input rst_i; + input write_valid_i; + input write_front_i; + input read_valid_i; + input [31:0] data_i; + output [31:0] data_o; + + reg [31:0] FIFOContent [NR_ELEMENTS-1:0]; + + typedef logic [$clog2(NR_ELEMENTS)-1:0] FIFOPointer_t; + + // verilator lint_off WIDTH + localparam FIFOPointer_t MAX_PTR_VAL = NR_ELEMENTS-1; + // verilator lint_on WIDTH + localparam FIFOPointer_t MIN_PTR_VAL = 0; + localparam FIFOPointer_t PTR_INC = 1; + FIFOPointer_t write_pointer; + FIFOPointer_t read_pointer; + + function FIFOPointer_t nextPointer(input FIFOPointer_t val); + if ($clog2(NR_ELEMENTS) == $clog2(NR_ELEMENTS+1) + && val == MAX_PTR_VAL) + nextPointer = MIN_PTR_VAL; // explicit wrap if NR_ELEMENTS is not a power of 2 + else + nextPointer = val + PTR_INC; + endfunction + + function FIFOPointer_t prevPointer(input FIFOPointer_t val); + if ($clog2(NR_ELEMENTS) == $clog2(NR_ELEMENTS+1) + && val == MIN_PTR_VAL) + prevPointer = MAX_PTR_VAL; // explicit wrap if NR_ELEMENTS is not a power of 2 + else + prevPointer = val - PTR_INC; + endfunction + + reg [$clog2(NR_ELEMENTS)-1:0] level; + reg is_empty; + + always @(posedge clk) begin + if (write_valid_i) + FIFOContent[write_front_i ? (read_valid_i ? read_pointer : prevPointer(read_pointer)) : write_pointer] <= data_i; + end + + assign data_o = FIFOContent[read_pointer]; + + always @(posedge clk) begin + if (rst_i) begin + is_empty <= 1; + end + else if (write_valid_i) begin + is_empty <= 0; + end + else if (read_valid_i && write_pointer == nextPointer(read_pointer)) begin + is_empty <= 1; + end + end + + always @(posedge clk) begin + if (rst_i) begin + level <= 0; + end + else begin + level <= level + (write_valid_i ? 1 : 0) - (read_valid_i ? 1 : 0); + end + end + + always @(posedge clk) begin + if (rst_i) begin + write_pointer <= 0; + end + else if (write_valid_i && !write_front_i) begin + write_pointer <= nextPointer(write_pointer); + end + end + + always @(posedge clk) begin + if (rst_i) begin + read_pointer <= 0; + end + else if (read_valid_i) begin + if (!(write_valid_i && write_front_i))read_pointer <= nextPointer(read_pointer); + end + else if (write_valid_i && write_front_i) begin + read_pointer <= prevPointer(read_pointer); + end + end +endmodule diff --git a/test_regress/t/t_flag_csplit_groups.py b/test_regress/t/t_flag_csplit_groups.py index 8fe9707bad..467fe6ad41 100755 --- a/test_regress/t/t_flag_csplit_groups.py +++ b/test_regress/t/t_flag_csplit_groups.py @@ -87,6 +87,7 @@ def check_gcc_flags(filename): "--output-groups 2", "--output-split-cfuncs 1", "--exe", + "--stats", "../" + test.main_filename], verilator_make_gmake=False) # yapf:disable @@ -122,4 +123,9 @@ def check_gcc_flags(filename): test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_Slow_2") test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_2") +# Check combine count +test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (174 if test.vltmt else 156)) +test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_FAST + (\d+)', (2 if test.vltmt else 3)) +test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_SLOW + (\d+)', 2) + test.passes() diff --git a/test_regress/t/t_flag_values_bad.out b/test_regress/t/t_flag_values_bad.out index 8febf43ce6..19b5964887 100644 --- a/test_regress/t/t_flag_values_bad.out +++ b/test_regress/t/t_flag_values_bad.out @@ -1,4 +1,5 @@ %Error: --output-split-cfuncs must be >= 0: -1 %Error: --output-split-ctrace must be >= 0: -1 +%Error: --preproc-token-limit must be > 0: 0 %Error: --reloop-limit must be >= 2: -1 %Error: Exiting due to diff --git a/test_regress/t/t_flag_values_bad.py b/test_regress/t/t_flag_values_bad.py index ab8c8bb793..0bebd83799 100755 --- a/test_regress/t/t_flag_values_bad.py +++ b/test_regress/t/t_flag_values_bad.py @@ -11,9 +11,11 @@ test.scenarios('vlt') -test.lint( - verilator_flags2=["--output-split-cfuncs -1", "--output-split-ctrace -1", "--reloop-limit -1"], - fails=True, - expect_filename=test.golden_filename) +test.lint(verilator_flags2=[ + "--output-split-cfuncs -1", "--output-split-ctrace -1", "--preproc-token-limit 0", + "--reloop-limit -1" +], + fails=True, + expect_filename=test.golden_filename) test.passes() diff --git a/test_regress/t/t_force_assign.py b/test_regress/t/t_force_assign.py new file mode 100755 index 0000000000..f989a35fba --- /dev/null +++ b/test_regress/t/t_force_assign.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_force_assign.v b/test_regress/t/t_force_assign.v new file mode 100644 index 0000000000..7b3724dd53 --- /dev/null +++ b/test_regress/t/t_force_assign.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, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module t; + reg [2:0] a = 0; + + initial begin + a = 1; + if (a != 1) $stop; + + force a = 2; + if (a != 2) $stop; + + a = 3; + if (a != 2) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_hier_block_int.py b/test_regress/t/t_hier_block_int.py new file mode 100755 index 0000000000..0384eccd50 --- /dev/null +++ b/test_regress/t/t_hier_block_int.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=['--stats', '--hierarchical']) + +test.execute() + +test.file_grep(test.obj_dir + "/Vsub/sub.sv", r'^module\s+(\S+)\s+', "sub") +test.file_grep(test.stats, r'HierBlock,\s+Hierarchical blocks\s+(\d+)', 1) + +test.passes() diff --git a/test_regress/t/t_hier_block_int.v b/test_regress/t/t_hier_block_int.v new file mode 100644 index 0000000000..8ff804d888 --- /dev/null +++ b/test_regress/t/t_hier_block_int.v @@ -0,0 +1,50 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2025 by Antmicro. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +module t(/*AUTOARG*/ + // inputs + clk +); + input clk; + + byte out1; + shortint out2; + int out3; + longint out4; + integer out5; + time out6; + + sub sub(out1, out2, out3, out4, out5, out6); + + always_ff @(posedge clk) begin + if (out1 == 1 && out2 == 2 && out3 == 3 && out4 == 4 && out5 == 5 && out6 == 6) begin + $write("*-* All Finished *-*\n"); + $finish; + end + else begin + $write("Mismatch\n"); + $stop; + end + end +endmodule + +module sub( + output byte out1, + output shortint out2, + output int out3, + output longint out4, + output integer out5, + output time out6 +); /*verilator hier_block*/ + assign out1 = 1; + assign out2 = 2; + assign out3 = 3; + assign out4 = 4; + assign out5 = 5; + assign out6 = 6; +endmodule diff --git a/test_regress/t/t_hier_block_perf.py b/test_regress/t/t_hier_block_perf.py new file mode 100755 index 0000000000..f73b5db4c5 --- /dev/null +++ b/test_regress/t/t_hier_block_perf.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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 + +import vltest_bootstrap + +test.scenarios('vlt_all') +test.init_benchmarksim() +test.cycles = (int(test.benchmark) if test.benchmark else 1000000) +test.sim_time = test.cycles * 10 + 1000 +THREADS = int(os.environ["SIM_THREADS"]) if "SIM_THREADS" in os.environ else 2 + +test.compile(benchmarksim=1, + v_flags2=[ + "+define+SIM_CYCLES=" + str(test.cycles), "--prof-exec", "--hierarchical", + "--stats" + ], + threads=(THREADS if test.vltmt else 1)) + +test.file_grep(test.obj_dir + "/V" + test.name + "__hier.dir/V" + test.name + "__stats.txt", + r'Optimizations, Hierarchical DPI wrappers with costs\s+(\d+)', 3) + +test.execute(all_run_flags=[ + "+verilator+prof+exec+start+2", + " +verilator+prof+exec+window+2", + " +verilator+prof+exec+file+" + test.obj_dir + "/profile_exec.dat", + " +verilator+prof+vlt+file+" + test.obj_dir + "/profile.vlt"]) # yapf:disable + +test.run(cmd=[ + os.environ["VERILATOR_ROOT"] + "/bin/verilator_gantt", test.obj_dir + + "/profile_exec.dat", "--vcd " + test.obj_dir + "/profile_exec.vcd", "| tee " + test.obj_dir + + "/gantt.log" +]) + +test.passes() diff --git a/test_regress/t/t_hier_block_perf.v b/test_regress/t/t_hier_block_perf.v new file mode 100644 index 0000000000..59470bb038 --- /dev/null +++ b/test_regress/t/t_hier_block_perf.v @@ -0,0 +1,314 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +// based on t_gate_ormux + +`ifndef HIER_CORES + `define HIER_CORES 3 +`endif + +`ifndef MAIN_CORES + `define MAIN_CORES 1 +`endif + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + generate + for (genvar i = 0; i < `MAIN_CORES; ++i) NonHierCore mainCore(clk); + endgenerate + + generate + for (genvar i = 0; i < `HIER_CORES; ++i) Core hierCore(clk); + endgenerate +endmodule + +module Core(input clk); /* verilator hier_block */ + reg [63:0] crc; + logic [31:0] rdata; + logic [31:0] rdata2; + wire [31:0] wdata = crc[31:0]; + wire [15:0] sel = {11'h0, crc[36:32]}; + wire we = crc[48]; + + Test test ( + // Outputs + .rdata (rdata[31:0]), + .rdata2 (rdata2[31:0]), + // Inputs + .clk (clk), + .we (we), + .sel (sel[15:0]), + .wdata (wdata[31:0])); + wire [63:0] result = {rdata2, rdata}; + + Check check(.clk(clk), .crc(crc), .result(result), .rdata(rdata), .rdata2(rdata2)); +endmodule + +module NonHierCore(input clk); + reg [63:0] crc; + logic [31:0] rdata; + logic [31:0] rdata2; + wire [31:0] wdata = crc[31:0]; + wire [15:0] sel = {11'h0, crc[36:32]}; + wire we = crc[48]; + + Test test ( + // Outputs + .rdata (rdata[31:0]), + .rdata2 (rdata2[31:0]), + // Inputs + .clk (clk), + .we (we), + .sel (sel[15:0]), + .wdata (wdata[31:0])); + wire [63:0] result = {rdata2, rdata}; + + Check check(.clk(clk), .crc(crc), .result(result), .rdata(rdata), .rdata2(rdata2)); +endmodule + +module Check( + input clk, + output reg [63:0] crc, + input wire [63:0] result, + input logic [31:0] rdata, + input logic [31:0] rdata2 + ); + integer cyc = 0; + reg [63:0] sum; + + always @ (posedge clk) begin +`ifdef TEST_VERBOSE + $write("[%0t] cyc==%0d crc=%x result=%x\n", $time, cyc, crc, result); +`endif + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; + sum <= result ^ {sum[62:0], sum[63] ^ sum[2] ^ sum[0]}; + if (rdata2 != rdata) $stop; + if (cyc==0) begin + // Setup + crc <= 64'h5aef0c8d_d70a4497; + sum <= '0; + end + else if (cyc<10) begin + sum <= '0; + end + else if (cyc == 99) begin + $write("[%0t] cyc==%0d crc=%x sum=%x\n", $time, cyc, crc, sum); + if (crc !== 64'hc77bb9b3784ea091) $stop; +`define EXPECTED_SUM 64'h8977713eb467bc86 + if (sum !== `EXPECTED_SUM) $stop; + end + else if (cyc == `SIM_CYCLES) begin + $write("[%0t] cyc==%0d crc=%x sum=%x\n", $time, cyc, crc, sum); + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule + +module Test(/*AUTOARG*/ + // Outputs + rdata, rdata2, + // Inputs + clk, we, sel, wdata + ); + input clk; + input we; + input [15:0] sel; + input [31:0] wdata; + output logic [31:0] rdata; + output logic [31:0] rdata2; + + logic we_d1r; + logic [15:0] sel_d1r; + logic [31:0] wdata_d1r; + always_ff @ (posedge clk) begin + we_d1r <= we; + sel_d1r <= sel; + wdata_d1r <= wdata; + end + + reg [31:0] csr0000; + reg [31:0] csr0001; + reg [31:0] csr0002; + reg [31:0] csr0003; + reg [31:0] csr0004; + reg [31:0] csr0005; + reg [31:0] csr0006; + reg [31:0] csr0007; + reg [31:0] csr0008; + reg [31:0] csr0009; + reg [31:0] csr000a; + reg [31:0] csr000b; + reg [31:0] csr000c; + reg [31:0] csr000d; + reg [31:0] csr000e; + reg [31:0] csr000f; + wire [31:0] csr0010 = 32'h33675230; + wire [31:0] csr0011 = 32'h00fa2144; + wire [31:0] csr0012 = 32'h6a5e8e10; + wire [31:0] csr0013 = 32'h000a5b5e; + wire [31:0] csr0014 = 32'h002fe51b; + wire [31:0] csr0015 = 32'h00027e00; + wire [31:0] csr0016 = 32'h0000e3c0; + wire [31:0] csr0017 = 32'h00efcf16; + wire [31:0] csr0018 = 32'h007a2600; + wire [31:0] csr0019 = 32'h0a4a9f10; + wire [31:0] csr001a = 32'h7d789de3; + wire [31:0] csr001b = 32'h40f655f9; + wire [31:0] csr001c = 32'hadad01f4; + wire [31:0] csr001d = 32'h02e7b33c; + wire [31:0] csr001e = 32'h12101533; + wire [31:0] csr001f = 32'h2cc1cce5; + initial begin + csr0000 = 32'he172d365; + csr0001 = 32'h35cc25e2; + csr0002 = 32'haf48436e; + csr0003 = 32'h135e55e4; + csr0004 = 32'h5fd6e48a; + csr0005 = 32'hb07d34ad; + csr0006 = 32'h2aa05deb; + csr0007 = 32'hfe97b680; + csr0008 = 32'h960f20bb; + csr0009 = 32'h251129f0; + csr000a = 32'hef3d2f93; + csr000b = 32'hef4bc127; + csr000c = 32'h3dfecb10; + csr000d = 32'h1b4690f5; + csr000e = 32'ha07822ab; + csr000f = 32'hf817cbf6; + end + + always_ff @ (posedge clk) begin + if (we_d1r && sel_d1r == 16'h0000) csr0000 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0001) csr0001 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0002) csr0002 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0003) csr0003 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0004) csr0004 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0005) csr0005 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0006) csr0006 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0007) csr0007 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0008) csr0008 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0009) csr0009 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000a) csr000a <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000b) csr000b <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000c) csr000c <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000d) csr000d <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000e) csr000e <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000f) csr000f <= wdata_d1r; + end + + wire dec0000 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0001 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec0002 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec0003 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec0004 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0005 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec0006 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec0007 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec0008 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0009 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec000a = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec000b = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec000c = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec000d = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec000e = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec000f = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec0010 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0011 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec0012 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec0013 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec0014 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0015 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec0016 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec0017 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec0018 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0019 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec001a = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec001b = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec001c = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec001d = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec001e = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec001f = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + + assign rdata = (32'h0 + | {32{dec0000}} & csr0000 + | {32{dec0001}} & csr0001 + | {32{dec0002}} & csr0002 + | {32{dec0003}} & csr0003 + | {32{dec0004}} & csr0004 + | {32{dec0005}} & csr0005 + | {32{dec0006}} & csr0006 + | {32{dec0007}} & csr0007 + | {32{dec0008}} & csr0008 + | {32{dec0009}} & csr0009 + | {32{dec000a}} & csr000a + | {32{dec000b}} & csr000b + | {32{dec000c}} & csr000c + | {32{dec000d}} & csr000d + | {32{dec000e}} & csr000e + | {32{dec000f}} & csr000f + | {32{dec0010}} & csr0010 + | {32{dec0011}} & csr0011 + | {32{dec0012}} & csr0012 + | {32{dec0013}} & csr0013 + | {32{dec0014}} & csr0014 + | {32{dec0015}} & csr0015 + | {32{dec0016}} & csr0016 + | {32{dec0017}} & csr0017 + | {32{dec0018}} & csr0018 + | {32{dec0019}} & csr0019 + | {32{dec001a}} & csr001a + | {32{dec001b}} & csr001b + | {32{dec001c}} & csr001c + | {32{dec001d}} & csr001d + | {32{dec001e}} & csr001e + | {32{dec001f}} & csr001f + ); + + always_comb begin + case (sel_d1r) + 16'h0000: rdata2 = csr0000; + 16'h0001: rdata2 = csr0001; + 16'h0002: rdata2 = csr0002; + 16'h0003: rdata2 = csr0003; + 16'h0004: rdata2 = csr0004; + 16'h0005: rdata2 = csr0005; + 16'h0006: rdata2 = csr0006; + 16'h0007: rdata2 = csr0007; + 16'h0008: rdata2 = csr0008; + 16'h0009: rdata2 = csr0009; + 16'h000a: rdata2 = csr000a; + 16'h000b: rdata2 = csr000b; + 16'h000c: rdata2 = csr000c; + 16'h000d: rdata2 = csr000d; + 16'h000e: rdata2 = csr000e; + 16'h000f: rdata2 = csr000f; + 16'h0010: rdata2 = csr0010; + 16'h0011: rdata2 = csr0011; + 16'h0012: rdata2 = csr0012; + 16'h0013: rdata2 = csr0013; + 16'h0014: rdata2 = csr0014; + 16'h0015: rdata2 = csr0015; + 16'h0016: rdata2 = csr0016; + 16'h0017: rdata2 = csr0017; + 16'h0018: rdata2 = csr0018; + 16'h0019: rdata2 = csr0019; + 16'h001a: rdata2 = csr001a; + 16'h001b: rdata2 = csr001b; + 16'h001c: rdata2 = csr001c; + 16'h001d: rdata2 = csr001d; + 16'h001e: rdata2 = csr001e; + 16'h001f: rdata2 = csr001f; + default: rdata2 = 0; + endcase + end + +endmodule diff --git a/test_regress/t/t_json_only_tag.out b/test_regress/t/t_json_only_tag.out index fe4fb9b92c..d0864dfe4e 100644 --- a/test_regress/t/t_json_only_tag.out +++ b/test_regress/t/t_json_only_tag.out @@ -69,10 +69,10 @@ {"type":"BASICDTYPE","name":"logic","addr":"(RB)","loc":"d,24:7,24:12","dtypep":"(RB)","keyword":"logic","generic":false,"rangep": []}, {"type":"STRUCTDTYPE","name":"m.my_struct","addr":"(K)","loc":"d,20:12,20:18","dtypep":"(K)","packed":true,"isFourstate":true,"generic":false,"classOrPackagep":"UNLINKED", "membersp": [ - {"type":"MEMBERDTYPE","name":"clk","addr":"(SB)","loc":"d,21:19,21:22","dtypep":"(OB)","generic":false,"childDTypep": [],"valuep": []}, - {"type":"MEMBERDTYPE","name":"k","addr":"(TB)","loc":"d,22:19,22:20","dtypep":"(PB)","generic":false,"childDTypep": [],"valuep": []}, - {"type":"MEMBERDTYPE","name":"enable","addr":"(UB)","loc":"d,23:19,23:25","dtypep":"(QB)","generic":false,"childDTypep": [],"valuep": []}, - {"type":"MEMBERDTYPE","name":"data","addr":"(VB)","loc":"d,24:19,24:23","dtypep":"(RB)","generic":false,"childDTypep": [],"valuep": []} + {"type":"MEMBERDTYPE","name":"clk","addr":"(SB)","loc":"d,21:19,21:22","dtypep":"(OB)","isConstrainedRand":false,"name":"clk","tag":"this is clk","generic":false,"refDTypep":"(OB)","childDTypep": [],"valuep": []}, + {"type":"MEMBERDTYPE","name":"k","addr":"(TB)","loc":"d,22:19,22:20","dtypep":"(PB)","isConstrainedRand":false,"name":"k","tag":"","generic":false,"refDTypep":"(PB)","childDTypep": [],"valuep": []}, + {"type":"MEMBERDTYPE","name":"enable","addr":"(UB)","loc":"d,23:19,23:25","dtypep":"(QB)","isConstrainedRand":false,"name":"enable","tag":"enable","generic":false,"refDTypep":"(QB)","childDTypep": [],"valuep": []}, + {"type":"MEMBERDTYPE","name":"data","addr":"(VB)","loc":"d,24:19,24:23","dtypep":"(RB)","isConstrainedRand":false,"name":"data","tag":"data","generic":false,"refDTypep":"(RB)","childDTypep": [],"valuep": []} ]}, {"type":"IFACEREFDTYPE","name":"","addr":"(O)","loc":"d,29:8,29:12","dtypep":"(O)","isPortDecl":false,"isVirtual":false,"cellName":"itop","ifaceName":"ifc","modportName":"","generic":false,"ifacep":"UNLINKED","cellp":"(L)","modportp":"UNLINKED","paramsp": []}, {"type":"BASICDTYPE","name":"logic","addr":"(S)","loc":"d,31:27,31:28","dtypep":"(S)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, diff --git a/test_regress/t/t_optm_if_array.py b/test_regress/t/t_opt_if_array.py similarity index 100% rename from test_regress/t/t_optm_if_array.py rename to test_regress/t/t_opt_if_array.py diff --git a/test_regress/t/t_optm_if_array.v b/test_regress/t/t_opt_if_array.v similarity index 100% rename from test_regress/t/t_optm_if_array.v rename to test_regress/t/t_opt_if_array.v diff --git a/test_regress/t/t_opt_ifjumpgo.py b/test_regress/t/t_opt_ifjumpgo.py new file mode 100755 index 0000000000..2c0e4f4f69 --- /dev/null +++ b/test_regress/t/t_opt_ifjumpgo.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=['--stats']) + +test.file_grep(test.stats, r'Node count, JUMPGO + 3 *$') + +test.execute() + +test.passes() diff --git a/test_regress/t/t_opt_ifjumpgo.v b/test_regress/t/t_opt_ifjumpgo.v new file mode 100644 index 0000000000..e0fabacaa9 --- /dev/null +++ b/test_regress/t/t_opt_ifjumpgo.v @@ -0,0 +1,67 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class uvm_object; +endclass + +class uvm_callback; +endclass + +class uvm_callbacks #(type T=uvm_object, type CB=uvm_callback); + bit m_registered = 1; + virtual function bit m_is_registered(uvm_object obj, uvm_callback cb); + if (m_is_for_me(cb) && m_am_i_a(obj)) begin + return m_registered; + end + endfunction + + virtual function bit m_is_for_me(uvm_callback cb); + CB this_cb; + // verilator lint_off WIDTHTRUNC + return ($cast(this_cb, cb)); + // verilator lint_on WIDTHTRUNC + endfunction + + virtual function bit m_am_i_a(uvm_object obj); + T this_t; + // verilator lint_off WIDTHTRUNC + return ($cast(this_t, obj)); + // verilator lint_on WIDTHTRUNC + endfunction +endclass + +class my_object extends uvm_object; +endclass + +class my_callback extends uvm_callback; +endclass + +class other_object extends uvm_object; +endclass + +module t; + + initial begin + my_object obj; + other_object oobj; + my_callback cb; + uvm_callbacks#(my_object, my_callback) ucs; + bit i; + + obj = new; + oobj = new; + cb = new; + ucs = new; + + i = ucs.m_is_registered(obj, cb); + if (i !== 1) $stop; + i = ucs.m_is_registered(oobj, cb); + if (i !== 0) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_optm_redor.py b/test_regress/t/t_opt_redor.py similarity index 100% rename from test_regress/t/t_optm_redor.py rename to test_regress/t/t_opt_redor.py diff --git a/test_regress/t/t_optm_redor.v b/test_regress/t/t_opt_redor.v similarity index 100% rename from test_regress/t/t_optm_redor.v rename to test_regress/t/t_opt_redor.v diff --git a/test_regress/t/t_pp_circ_subst_bad2.out b/test_regress/t/t_pp_circ_subst_bad2.out new file mode 100644 index 0000000000..657d44e8ae --- /dev/null +++ b/test_regress/t/t_pp_circ_subst_bad2.out @@ -0,0 +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:1: syntax error, unexpected IDENTIFIER +%Error: Exiting due to diff --git a/test_regress/t/t_pp_circ_subst_bad2.py b/test_regress/t/t_pp_circ_subst_bad2.py new file mode 100755 index 0000000000..a4770180d2 --- /dev/null +++ b/test_regress/t/t_pp_circ_subst_bad2.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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 + +import vltest_bootstrap + +test.scenarios('linter') +test.top_filename = "t/t_pp_circ_subst_bad.v" + +test.lint(verilator_flags2=["--preproc-token-limit 20000"], + fails=True, + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_sc_names.cpp b/test_regress/t/t_sc_names.cpp index 354d27ce2a..3d7b6cd7f6 100644 --- a/test_regress/t/t_sc_names.cpp +++ b/test_regress/t/t_sc_names.cpp @@ -14,7 +14,7 @@ int sc_main(int argc, char* argv[]) { std::vector ch = tb->get_child_objects(); bool found = false; - /* We expect to find clk in here. */ + // We expect to find clk in here for (int i = 0; i < ch.size(); ++i) { if (!std::strcmp(ch[i]->basename(), "clk")) found = true; } diff --git a/test_regress/t/t_timing_debug2.out b/test_regress/t/t_timing_debug2.out index ac4dd1943b..a00833c4e3 100644 --- a/test_regress/t/t_timing_debug2.out +++ b/test_regress/t/t_timing_debug2.out @@ -185,7 +185,6 @@ -V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:274 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:173 --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::__VnoInFunc_do_delay -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:247 -V{t#,#} Process forked at t/t_timing_class.v:246 finished @@ -499,7 +498,6 @@ -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:257 -V{t#,#}+ Vt_timing_debug2_t__03a__03aForkDelayClass::__VnoInFunc_do_delay -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:174 --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::__VnoInFunc_do_delay -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:131 -V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass::__VnoInFunc_flip @@ -1038,9 +1036,7 @@ -V{t#,#} Process forked at t/t_timing_class.v:250 finished -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:245 -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:175 --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::__VnoInFunc_do_delay --V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t___eval_initial__TOP__t__Vtiming__6____Vfork_1__0 -V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::__VnoInFunc_do_assign -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:76 @@ -1082,8 +1078,6 @@ -V{t#,#}End-of-eval cleanup -V{t#,#}+++++TOP Evaluate Vt_timing_debug2::eval_step -V{t#,#}+ Vt_timing_debug2___024root___eval_debug_assertions --V{t#,#}+ Vt_timing_debug2_t__03a__03aForkDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aForkClass::~ -V{t#,#}+ Eval -V{t#,#}+ Vt_timing_debug2___024root___eval -V{t#,#}+ Vt_timing_debug2___024root___eval_phase__act @@ -1236,7 +1230,6 @@ -V{t#,#}End-of-eval cleanup -V{t#,#}+++++TOP Evaluate Vt_timing_debug2::eval_step -V{t#,#}+ Vt_timing_debug2___024root___eval_debug_assertions --V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::~ -V{t#,#}+ Eval -V{t#,#}+ Vt_timing_debug2___024root___eval -V{t#,#}+ Vt_timing_debug2___024root___eval_phase__act @@ -1308,18 +1301,3 @@ -V{t#,#}+ Vt_timing_debug2___024root___eval_phase__nba -V{t#,#}End-of-eval cleanup -V{t#,#}+ Vt_timing_debug2___024root___eval_final --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aLocalWaitClass::~ --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aWaitClass::~ --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass::~ --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ diff --git a/test_regress/t/t_vpi_get_value_array.cpp b/test_regress/t/t_vpi_get_value_array.cpp new file mode 100644 index 0000000000..86796eb046 --- /dev/null +++ b/test_regress/t/t_vpi_get_value_array.cpp @@ -0,0 +1,695 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2024 by Diego Roux. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#ifndef IS_VPI + +#include "verilated.h" +#include "verilated_vpi.h" + +#include "Vt_vpi_get_value_array.h" + +#endif + +// These require the above. Comment prevents clang-format moving them +#include "TestSimulator.h" +#include "TestVpi.h" + +#include + +//====================================================================== + +int test_vpiRawFourStateVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRawFourStateVal; + arrayvalue.flags = 0; + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size; j++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", (i * 2 * elem_size) + j, (offset * elem_size) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(i * 2 * elem_size) + j], + test_data[(offset * elem_size) + j]); + } + for (unsigned j = 0; j < elem_size; j++) { + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(((i * 2) + 1) * elem_size) + j], 0); + } + } + + return 0; +} + +int test_vpiRawTwoStateVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw two state + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRawTwoStateVal; + arrayvalue.flags = 0; + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size; j++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", (i * elem_size) + j, (offset * elem_size) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(i * elem_size) + j], + test_data[(offset * elem_size) + j]); + } + } + + return 0; +} + +int test_vpiVectorVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + const unsigned elem_size_words = (elem_size + 3) / sizeof(PLI_UINT32); + const unsigned vec_size = elem_size_words * size; + std::vector test_data_vectors; + test_data_vectors.reserve(vec_size); + unsigned test_data_index = 0; + for (unsigned i = 0; i < size; i++) { + unsigned count = 0; + for (unsigned j = 0; j < elem_size_words; j++) { + PLI_UINT32& aval = test_data_vectors[(i * elem_size_words) + j].aval; + test_data_vectors[(i * elem_size_words) + j].bval = UINT32_MAX; + aval = 0; + for (unsigned k = 0; k < sizeof(PLI_UINT32); k++) { + if (count++ == elem_size) break; + aval |= static_cast(test_data[test_data_index++] & 0xFF) << (k * 8); + } + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test vector + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiVectorVal; + arrayvalue.flags = 0; + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < vec_size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.vectors[i].aval, i, + test_data_vectors[i].aval); + } +#endif + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size_words; j++) { +#ifdef TEST_VERBOSE + printf("array[%u] == test[%u]\n", (i * elem_size_words) + j, + (offset * elem_size_words) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.vectors[(i * elem_size_words) + j].aval, + test_data_vectors[(offset * elem_size_words) + j].aval); + CHECK_RESULT_HEX(arrayvalue.value.vectors[(i * elem_size_words) + j].bval, 0); + } + } + + return 0; +} + +int test_vpiIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_integers; + test_data_integers.reserve(size); + for (unsigned i = 0; i < size; i++) { + PLI_INT32& integer = test_data_integers[i]; + integer = 0; + for (unsigned j = 0; j < elem_size; j++) { + integer |= (static_cast(test_data[(i * elem_size) + j]) & 0xFF) << (j * 8); + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.integers[i], i, + test_data_integers[i]); + } +#endif + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; +#ifdef TEST_VERBOSE + printf("array[%u] == test[%u]\n", i, offset); +#endif + CHECK_RESULT_HEX(arrayvalue.value.integers[i], test_data_integers[offset]); + } + + return 0; +} + +int test_vpiShortIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_shortints; + test_data_shortints.reserve(size); + for (unsigned i = 0; i < size; i++) { + if (elem_size == 2) { + test_data_shortints[i] = test_data[i * 2] & 0xFF; + test_data_shortints[i] |= test_data[(i * 2) + 1] << 8; + } else { + test_data_shortints[i] = test_data[i] & 0xFF; + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiShortIntVal; + arrayvalue.flags = 0; + arrayvalue.value.shortints = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.shortints[i], i, + test_data_shortints[i]); + } +#endif + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; +#ifdef TEST_VERBOSE + printf("array[%u] == test[%u]\n", i, offset); +#endif + CHECK_RESULT_HEX(arrayvalue.value.shortints[i], test_data_shortints[offset]); + } + + return 0; +} + +int test_vpiLongIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_longints; + test_data_longints.reserve(size); + for (unsigned i = 0; i < size; i++) { + PLI_INT64& longint = test_data_longints[i]; + longint = 0; + for (unsigned j = 0; j < elem_size; j++) { + longint |= (static_cast(test_data[(i * elem_size) + j]) & 0xFF) << (j * 8); + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiLongIntVal; + arrayvalue.flags = 0; + arrayvalue.value.longints = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; +#ifdef TEST_VERBOSE + printf("array[%u] == test[%u]\n", i, offset); +#endif + CHECK_RESULT_HEX(arrayvalue.value.longints[i], test_data_longints[offset]); + } + + return 0; +} + +int mon_check_props() { + // skip test if not verilator (value_array accessors unimplemented in other sims) + if (!TestSimulator::is_verilator()) { +#ifdef VERILATOR + printf("TestSimulator indicating not verilator, but VERILATOR macro is defined\n"); + return 1; +#endif + return 0; + } + + const unsigned NUM_ELEMENTS = 4; + + PLI_BYTE8 read_bytes[NUM_ELEMENTS] + = {static_cast(0xad), static_cast(0xde), + static_cast(0xef), static_cast(0xbe)}; + + PLI_BYTE8 read_shorts[NUM_ELEMENTS * 2] = { + static_cast(0xad), static_cast(0xde), static_cast(0xef), + static_cast(0xbe), static_cast(0xfe), static_cast(0xca), + static_cast(0x0d), static_cast(0xf0)}; + + PLI_BYTE8 read_words[NUM_ELEMENTS * 4] = { + static_cast(0xef), static_cast(0xbe), static_cast(0xad), + static_cast(0xde), static_cast(0x0d), static_cast(0xf0), + static_cast(0xfe), static_cast(0xca), static_cast(0x03), + static_cast(0x02), static_cast(0x01), static_cast(0x00), + static_cast(0x07), static_cast(0x06), static_cast(0x05), + static_cast(0x04)}; + + PLI_BYTE8 read_longs[NUM_ELEMENTS * 8] = { + static_cast(0x0d), static_cast(0xf0), static_cast(0xfe), + static_cast(0xca), static_cast(0xef), static_cast(0xbe), + static_cast(0xad), static_cast(0xde), static_cast(0x07), + static_cast(0x06), static_cast(0x05), static_cast(0x04), + static_cast(0x03), static_cast(0x02), static_cast(0x01), + static_cast(0x00), static_cast(0x0F), static_cast(0x0E), + static_cast(0x0D), static_cast(0x0C), static_cast(0x0B), + static_cast(0x0A), static_cast(0x09), static_cast(0x08), + static_cast(0x17), static_cast(0x16), static_cast(0x15), + static_cast(0x14), static_cast(0x13), static_cast(0x12), + static_cast(0x11), static_cast(0x10)}; + + PLI_BYTE8 read_customs[NUM_ELEMENTS * 9] = { + static_cast(0x0d), static_cast(0xf0), static_cast(0xfe), + static_cast(0xca), static_cast(0xef), static_cast(0xbe), + static_cast(0xad), static_cast(0xde), static_cast(0x1A), + static_cast(0x07), static_cast(0x06), static_cast(0x05), + static_cast(0x04), static_cast(0x03), static_cast(0x02), + static_cast(0x01), static_cast(0x00), static_cast(0x15), + static_cast(0x0F), static_cast(0x0E), static_cast(0x0D), + static_cast(0x0C), static_cast(0x0B), static_cast(0x0A), + static_cast(0x09), static_cast(0x08), static_cast(0x0A), + static_cast(0x17), static_cast(0x16), static_cast(0x15), + static_cast(0x14), static_cast(0x13), static_cast(0x12), + static_cast(0x11), static_cast(0x10), static_cast(0x05)}; + + char read_bytes_name[] = "test.read_bytes"; + char read_bytes_nonzero_index_name[] = "test.read_bytes_nonzero_index"; + char read_bytes_rl_name[] = "test.read_bytes_rl"; + char read_shorts_name[] = "test.read_shorts"; + char read_words_name[] = "test.read_words"; + char read_integers_name[] = "test.read_integers"; + char read_longs_name[] = "test.read_longs"; + char read_customs_name[] = "test.read_customs"; + char read_customs_nonzero_index_rl_name[] = "test.read_customs_nonzero_index_rl"; + + for (unsigned i = 0; i < NUM_ELEMENTS; i++) { + for (unsigned j = 0; j < (NUM_ELEMENTS + 1); j++) { + if (test_vpiRawFourStateVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(read_shorts_name, read_shorts, i, 0, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiRawFourStateVal(read_words_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawFourStateVal(read_integers_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawFourStateVal(read_longs_name, read_longs, i, 0, j, NUM_ELEMENTS, 8)) + return 1; + if (test_vpiRawFourStateVal(read_customs_name, read_customs, i, 0, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiRawFourStateVal(read_customs_nonzero_index_rl_name, read_customs, i + 1, 1, + j, NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiRawTwoStateVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(read_shorts_name, read_shorts, i, 0, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiRawTwoStateVal(read_words_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawTwoStateVal(read_integers_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawTwoStateVal(read_longs_name, read_longs, i, 0, j, NUM_ELEMENTS, 8)) + return 1; + if (test_vpiRawTwoStateVal(read_customs_name, read_customs, i, 0, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiRawTwoStateVal(read_customs_nonzero_index_rl_name, read_customs, i + 1, 1, + j, NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiVectorVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiVectorVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiVectorVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiVectorVal(read_shorts_name, read_shorts, i, 0, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiVectorVal(read_words_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) return 1; + if (test_vpiVectorVal(read_integers_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiVectorVal(read_longs_name, read_longs, i, 0, j, NUM_ELEMENTS, 8)) return 1; + if (test_vpiVectorVal(read_customs_name, read_customs, i, 0, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiVectorVal(read_customs_nonzero_index_rl_name, read_customs, i + 1, 1, j, + NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiShortIntVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(read_shorts_name, read_shorts, i, 0, j, NUM_ELEMENTS, 2)) + return 1; + + if (test_vpiIntVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiIntVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiIntVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiIntVal(read_words_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) return 1; + if (test_vpiIntVal(read_integers_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) return 1; + + if (test_vpiLongIntVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiLongIntVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiLongIntVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiLongIntVal(read_shorts_name, read_shorts, i, 0, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiLongIntVal(read_words_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiLongIntVal(read_integers_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiLongIntVal(read_longs_name, read_longs, i, 0, j, NUM_ELEMENTS, 8)) + return 1; + } + } + + { + // test unsupported format + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_longs", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRealVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported foramt + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiShortRealVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported format + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_longs", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiTimeVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported TestVpiHandle + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported type + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_scalar", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test indexp out of bounds + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_bounds", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {4}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + indexp[0] = 0; + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported flags + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = vpiUserAllocFlag; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported format & vltype combination + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiShortIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test num out of bounds + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, 5); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test null arrayvalue + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, 0, indexp, 0); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test null indexp + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + + vpi_get_value_array(object, &arrayvalue, 0, 0); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + return 0; +} + +extern "C" int mon_check(void) { return mon_check_props(); } + +#ifdef IS_VPI +static int mon_check_vpi() { + TestVpiHandle href = vpi_handle(vpiSysTfCall, 0); + s_vpi_value vpi_value; + + vpi_value.format = vpiIntVal; + vpi_value.value.integer = mon_check(); + vpi_put_value(href, &vpi_value, NULL, vpiNoDelay); + + return 0; +} + +static s_vpi_systf_data vpi_systf_data[] = {{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$mon_check", + (PLI_INT32(*)(PLI_BYTE8*))mon_check_vpi, 0, 0, 0}, + 0}; + +// cver entry +void vpi_compat_bootstrap(void) { + p_vpi_systf_data systf_data_p; + systf_data_p = &(vpi_systf_data[0]); + while (systf_data_p->type != 0) vpi_register_systf(systf_data_p++); +} + +// icarus entry +void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; + +#else + +int main(int argc, char** argv) { + Verilated::commandArgs(argc, argv); + const std::unique_ptr contextp{new VerilatedContext}; + const std::unique_ptr top{new VM_PREFIX{contextp.get(), ""}}; + contextp->fatalOnVpiError(0); + +#ifdef VERILATOR +#ifdef TEST_VERBOSE + contextp->scopesDump(); +#endif +#endif + + while (!contextp->gotFinish()) { top->eval(); } + + return 0; +} +#endif diff --git a/test_regress/t/t_vpi_get_value_array.py b/test_regress/t/t_vpi_get_value_array.py new file mode 100755 index 0000000000..9f7ed18323 --- /dev/null +++ b/test_regress/t/t_vpi_get_value_array.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(make_top_shell=False, + make_main=False, + make_pli=True, + verilator_flags2=["--exe --vpi --no-l2name", test.pli_filename], + iv_flags2=["-g2005-sv -D USE_VPI_NOT_DPI"], + v_flags2=["+define+USE_VPI_NOT_DPI +define+VERILATOR_COMMENTS"]) + +test.execute(use_libvpi=True) + +test.passes() diff --git a/test_regress/t/t_vpi_get_value_array.v b/test_regress/t/t_vpi_get_value_array.v new file mode 100644 index 0000000000..86a82df92e --- /dev/null +++ b/test_regress/t/t_vpi_get_value_array.v @@ -0,0 +1,105 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2024 by Diego Roux. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +`ifdef VERILATOR_COMMENTS + `define PUBLIC_FLAT_RD /*verilator public_flat_rd*/ + `define PUBLIC_FLAT_RW /*verilator public_flat_rw*/ +`else + `define PUBLIC_FLAT_RD + `define PUBLIC_FLAT_RW +`endif + +module test (); + +`ifdef VERILATOR +`systemc_header +extern "C" int mon_check(); +`verilog +`endif + + reg [7:0] read_bytes [0:3] `PUBLIC_FLAT_RD; + reg [7:0] read_bytes_nonzero_index [1:4] `PUBLIC_FLAT_RD; + reg [7:0] read_bytes_rl [3:0] `PUBLIC_FLAT_RD; + + reg [15:0] read_shorts [0:3] `PUBLIC_FLAT_RD; + reg [31:0] read_words [0:3] `PUBLIC_FLAT_RD; + reg [31:0] read_words_rl [3:0] `PUBLIC_FLAT_RD; + reg [63:0] read_longs [0:3] `PUBLIC_FLAT_RD; + integer read_integers [0:3] `PUBLIC_FLAT_RD; + reg [68:0] read_customs [0:3] `PUBLIC_FLAT_RD; + reg [68:0] read_customs_nonzero_index_rl [4:1] `PUBLIC_FLAT_RD; + + reg [7:0] read_scalar `PUBLIC_FLAT_RD; + reg [7:0] read_bounds [1:3] `PUBLIC_FLAT_RD; + + integer status; + + initial begin + read_bytes[0] = 8'had; + read_bytes[1] = 8'hde; + read_bytes[2] = 8'hef; + read_bytes[3] = 8'hbe; + + read_bytes_rl[3] = 8'had; + read_bytes_rl[2] = 8'hde; + read_bytes_rl[1] = 8'hef; + read_bytes_rl[0] = 8'hbe; + + read_bytes_nonzero_index[1] = 8'had; + read_bytes_nonzero_index[2] = 8'hde; + read_bytes_nonzero_index[3] = 8'hef; + read_bytes_nonzero_index[4] = 8'hbe; + + read_shorts[0] = 16'hdead; + read_shorts[1] = 16'hbeef; + read_shorts[2] = 16'hcafe; + read_shorts[3] = 16'hf00d; + + read_words[0] = 32'hdeadbeef; + read_words[1] = 32'hcafef00d; + read_words[2] = 32'h00010203; + read_words[3] = 32'h04050607; + + read_integers[0] = 32'hdeadbeef; + read_integers[1] = 32'hcafef00d; + read_integers[2] = 32'h00010203; + read_integers[3] = 32'h04050607; + + read_longs[0] = 64'hdeadbeefcafef00d; + read_longs[1] = 64'h0001020304050607; + read_longs[2] = 64'h08090a0b0c0d0e0f; + read_longs[3] = 64'h1011121314151617; + + read_customs[0] = 69'hFAdeadbeefcafef00d; //0x001F'FFFF'FFFF'FFFF'FFFF + read_customs[1] = 69'hF50001020304050607; + read_customs[2] = 69'h0A08090a0b0c0d0e0f; + read_customs[3] = 69'h051011121314151617; + + read_customs_nonzero_index_rl[4] = 69'hFAdeadbeefcafef00d; //0x001F'FFFF'FFFF'FFFF'FFFF + read_customs_nonzero_index_rl[3] = 69'hF50001020304050607; + read_customs_nonzero_index_rl[2] = 69'h0A08090a0b0c0d0e0f; + read_customs_nonzero_index_rl[1] = 69'h051011121314151617; + + +`ifdef IVERILOG + status = $mon_check; +`endif + +`ifdef VERILATOR + status = $c32("mon_check()"); +`endif + + if (status != 0) begin + $write("%%Error: t_vpi_get_value_array.cpp:%0d: C Test failed\n", status); + $stop; + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_vpi_put_value_array.cpp b/test_regress/t/t_vpi_put_value_array.cpp new file mode 100644 index 0000000000..2e9008ba49 --- /dev/null +++ b/test_regress/t/t_vpi_put_value_array.cpp @@ -0,0 +1,759 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2024 by Diego Roux. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#ifndef IS_VPI + +#include "verilated.h" +#include "verilated_vpi.h" + +#include "Vt_vpi_put_value_array.h" + +#endif + +// These require the above. Comment prevents clang-format moving them +#include "TestSimulator.h" +#include "TestVpi.h" + +#include + +//====================================================================== + +int test_vpiRawFourStateVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + + // prepare index and test data arrays + int index_arr[1] = {index}; + std::vector test_data_four_state; + test_data_four_state.reserve(size * elem_size * 2); + for (unsigned i = 0; i < size; i++) { + for (unsigned j = 0; j < elem_size; j++) { + test_data_four_state[(i * 2 * elem_size) + j] = test_data[(i * elem_size) + j]; + } + for (unsigned j = 0; j < elem_size; j++) { + test_data_four_state[(((i * 2) + 1) * elem_size) + j] = -1; // bval should be ignored + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRawFourStateVal; + arrayvalue.flags = 0; + arrayvalue.value.rawvals = test_data_four_state.data(); + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to nu + arrayvalue.value.rawvals = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < (2 * size * elem_size); i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.rawvals[i] & 0xFF, i, + test_data_four_state[i] & 0xFF); + } +#endif + // compare to test data + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size; j++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", (i * 2 * elem_size) + j, (i * elem_size) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(i * 2 * elem_size) + j], + test_data[(i * elem_size) + j]); + } + for (unsigned j = 0; j < elem_size; j++) { + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(((i * 2) + 1) * elem_size) + j], 0); + } + } + + return 0; +} + +int test_vpiRawTwoStateVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRawTwoStateVal; + arrayvalue.flags = 0; + arrayvalue.value.rawvals = test_data; + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to check + arrayvalue.value.rawvals = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < (size * elem_size); i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.rawvals[i] & 0xFF, i, + test_data[i] & 0xFF); + } +#endif + + // compare to test data + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size; j++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", (i * elem_size) + j, (i * elem_size) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(i * elem_size) + j], + test_data[(i * elem_size) + j]); + } + } + + return 0; +} + +int test_vpiVectorVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + // prepare index + int index_arr[1] = {index}; + const unsigned elem_size_words = (elem_size + 3) / sizeof(PLI_UINT32); + const unsigned vec_size = elem_size_words * size; + std::vector test_data_vectors; + test_data_vectors.reserve(vec_size); + unsigned test_data_index = 0; + for (unsigned i = 0; i < size; i++) { + unsigned count = 0; + for (unsigned j = 0; j < elem_size_words; j++) { + PLI_UINT32& aval = test_data_vectors[(i * elem_size_words) + j].aval; + test_data_vectors[(i * elem_size_words) + j].bval = UINT32_MAX; + aval = 0; + for (unsigned k = 0; k < sizeof(PLI_UINT32); k++) { + if (count++ == elem_size) break; + aval |= static_cast(test_data[test_data_index++] & 0xFF) << (k * 8); + } + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiVectorVal; + arrayvalue.flags = 0; + arrayvalue.value.vectors = test_data_vectors.data(); + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to check + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < vec_size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.vectors[i].aval, i, + test_data_vectors[i].aval); + } +#endif + + // compare to test data + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size_words; j++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", (i * elem_size_words) + j, (i * elem_size_words) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.vectors[(i * elem_size_words) + j].aval, + test_data_vectors[(i * elem_size_words) + j].aval); + } + for (unsigned j = 0; j < elem_size_words; j++) { + CHECK_RESULT_HEX(arrayvalue.value.vectors[(i * elem_size_words) + j].bval, 0); + } + } + + return 0; +} + +int test_vpiIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_integers; + test_data_integers.reserve(size); + for (unsigned i = 0; i < size; i++) { + PLI_INT32& integer = test_data_integers[i]; + integer = 0; + for (unsigned j = 0; j < elem_size; j++) { + integer |= (static_cast(test_data[(i * elem_size) + j]) & 0xFF) << (j * 8); + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = test_data_integers.data(); + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to check + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.integers[i], i, + test_data_integers[i]); + } +#endif + + // compare to test data + for (unsigned i = 0; i < num; i++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", i, i); +#endif + CHECK_RESULT_HEX(arrayvalue.value.integers[i], test_data_integers[i]); + } + + return 0; +} + +int test_vpiShortIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_shortints; + test_data_shortints.reserve(size); + for (unsigned i = 0; i < size; i++) { + if (elem_size == 2) { + test_data_shortints[i] = test_data[i * 2] & 0xFF; + test_data_shortints[i] |= test_data[(i * 2) + 1] << 8; + } else { + test_data_shortints[i] = test_data[i] & 0xFF; + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiShortIntVal; + arrayvalue.flags = 0; + arrayvalue.value.shortints = test_data_shortints.data(); + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to check + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.shortints[i], i, + test_data_shortints[i]); + } +#endif + + // compare to test data + for (unsigned i = 0; i < num; i++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", i, i); +#endif + CHECK_RESULT_HEX(arrayvalue.value.shortints[i], test_data_shortints[i]); + } + + return 0; +} + +int test_vpiLongIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_longints; + test_data_longints.reserve(size); + for (unsigned i = 0; i < size; i++) { + PLI_INT64& longint = test_data_longints[i]; + longint = 0; + for (unsigned j = 0; j < elem_size; j++) { + longint |= (static_cast(test_data[(i * elem_size) + j]) & 0xFF) << (j * 8); + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiLongIntVal; + arrayvalue.flags = 0; + arrayvalue.value.longints = test_data_longints.data(); + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to check + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // compare to test data + for (unsigned i = 0; i < num; i++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", i, i); +#endif + CHECK_RESULT_HEX(arrayvalue.value.longints[i], test_data_longints[i]); + } + + return 0; +} + +int mon_check_props(void) { + // skip test if not verilator (value_array accessors unimplemented in other sims) + if (!TestSimulator::is_verilator()) { +#ifdef VERILATOR + printf("TestSimulator indicating not verilator, but VERILATOR macro is defined\n"); + return 1; +#endif + return 0; + } + + const unsigned NUM_ELEMENTS = 4; + + PLI_BYTE8 write_bytes[NUM_ELEMENTS] + = {static_cast(0xad), static_cast(0xde), + static_cast(0xef), static_cast(0xbe)}; + + PLI_BYTE8 write_shorts[NUM_ELEMENTS * 2] = { + static_cast(0xad), static_cast(0xde), static_cast(0xef), + static_cast(0xbe), static_cast(0xfe), static_cast(0xca), + static_cast(0x0d), static_cast(0xf0)}; + + PLI_BYTE8 write_words[NUM_ELEMENTS * 4] = { + static_cast(0xef), static_cast(0xbe), static_cast(0xad), + static_cast(0xde), static_cast(0x0d), static_cast(0xf0), + static_cast(0xfe), static_cast(0xca), static_cast(0x03), + static_cast(0x02), static_cast(0x01), static_cast(0x00), + static_cast(0x07), static_cast(0x06), static_cast(0x05), + static_cast(0x04)}; + + PLI_BYTE8 write_longs[NUM_ELEMENTS * 8] = { + static_cast(0x0d), static_cast(0xf0), static_cast(0xfe), + static_cast(0xca), static_cast(0xef), static_cast(0xbe), + static_cast(0xad), static_cast(0xde), static_cast(0x07), + static_cast(0x06), static_cast(0x05), static_cast(0x04), + static_cast(0x03), static_cast(0x02), static_cast(0x01), + static_cast(0x00), static_cast(0x0F), static_cast(0x0E), + static_cast(0x0D), static_cast(0x0C), static_cast(0x0B), + static_cast(0x0A), static_cast(0x09), static_cast(0x08), + static_cast(0x17), static_cast(0x16), static_cast(0x15), + static_cast(0x14), static_cast(0x13), static_cast(0x12), + static_cast(0x11), static_cast(0x10)}; + + PLI_BYTE8 write_customs[NUM_ELEMENTS * 9] = { + static_cast(0x0d), static_cast(0xf0), static_cast(0xfe), + static_cast(0xca), static_cast(0xef), static_cast(0xbe), + static_cast(0xad), static_cast(0xde), static_cast(0x1A), + static_cast(0x07), static_cast(0x06), static_cast(0x05), + static_cast(0x04), static_cast(0x03), static_cast(0x02), + static_cast(0x01), static_cast(0x00), static_cast(0x15), + static_cast(0x0F), static_cast(0x0E), static_cast(0x0D), + static_cast(0x0C), static_cast(0x0B), static_cast(0x0A), + static_cast(0x09), static_cast(0x08), static_cast(0x0A), + static_cast(0x17), static_cast(0x16), static_cast(0x15), + static_cast(0x14), static_cast(0x13), static_cast(0x12), + static_cast(0x11), static_cast(0x10), static_cast(0x05)}; + + char write_bytes_name[] = "test.write_bytes"; + char write_bytes_nonzero_index_name[] = "test.write_bytes_nonzero_index"; + char write_bytes_rl_name[] = "test.write_bytes_rl"; + char write_shorts_name[] = "test.write_shorts"; + char write_words_name[] = "test.write_words"; + char write_integers_name[] = "test.write_integers"; + char write_longs_name[] = "test.write_longs"; + char write_customs_name[] = "test.write_customs"; + char write_customs_nonzero_index_rl_name[] = "test.write_customs_nonzero_index_rl"; + + for (unsigned i = 0; i < NUM_ELEMENTS; i++) { + for (unsigned j = 0; j < (NUM_ELEMENTS + 1); j++) { + if (test_vpiRawFourStateVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(write_shorts_name, write_shorts, i, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiRawFourStateVal(write_words_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawFourStateVal(write_integers_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawFourStateVal(write_longs_name, write_longs, i, j, NUM_ELEMENTS, 8)) + return 1; + if (test_vpiRawFourStateVal(write_customs_name, write_customs, i, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiRawFourStateVal(write_customs_nonzero_index_rl_name, write_customs, i + 1, + j, NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiRawTwoStateVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(write_shorts_name, write_shorts, i, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiRawTwoStateVal(write_words_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawTwoStateVal(write_integers_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawTwoStateVal(write_longs_name, write_longs, i, j, NUM_ELEMENTS, 8)) + return 1; + if (test_vpiRawTwoStateVal(write_customs_name, write_customs, i, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiRawTwoStateVal(write_customs_nonzero_index_rl_name, write_customs, i + 1, + j, NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiVectorVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiVectorVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiVectorVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiVectorVal(write_shorts_name, write_shorts, i, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiVectorVal(write_words_name, write_words, i, j, NUM_ELEMENTS, 4)) return 1; + if (test_vpiVectorVal(write_integers_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiVectorVal(write_longs_name, write_longs, i, j, NUM_ELEMENTS, 8)) return 1; + if (test_vpiVectorVal(write_customs_name, write_customs, i, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiVectorVal(write_customs_nonzero_index_rl_name, write_customs, i + 1, j, + NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiShortIntVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(write_shorts_name, write_shorts, i, j, NUM_ELEMENTS, 2)) + return 1; + + if (test_vpiIntVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiIntVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, NUM_ELEMENTS, + 1)) + return 1; + if (test_vpiIntVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiIntVal(write_words_name, write_words, i, j, NUM_ELEMENTS, 4)) return 1; + if (test_vpiIntVal(write_integers_name, write_words, i, j, NUM_ELEMENTS, 4)) return 1; + + if (test_vpiLongIntVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiLongIntVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiLongIntVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiLongIntVal(write_shorts_name, write_shorts, i, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiLongIntVal(write_words_name, write_words, i, j, NUM_ELEMENTS, 4)) return 1; + if (test_vpiLongIntVal(write_integers_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiLongIntVal(write_longs_name, write_longs, i, j, NUM_ELEMENTS, 8)) return 1; + } + } + + { + // test unsupported format + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_longs", NULL); + CHECK_RESULT_NZ(object); + + double datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRealVal; + arrayvalue.flags = 0; + arrayvalue.value.reals = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + arrayvalue.format = vpiShortRealVal; + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + arrayvalue.format = vpiTimeVal; + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test null array value + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, 0, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported TestVpiHandle + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported type + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_scalar", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test index out of bounds + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_bounds", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {4}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + indexp[0] = 0; + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test inaccessible + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_inaccessible", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported flags + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiPropagateOff; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + arrayvalue.flags = vpiOneValue; + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported format & type combination + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiShortIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + arrayvalue.flags = vpiOneValue; + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test num out of bounds + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 5); + CHECK_RESULT_NZ(~vpi_chk_error(0)); + } + + { + // test null arrayvalue + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, 0, indexp, 0); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test null indexp + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + + vpi_get_value_array(object, &arrayvalue, 0, 0); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + return 0; +} + +extern "C" int mon_check(void) { return mon_check_props(); } + +#ifdef IS_VPI + +static int mon_check_vpi() { + TestVpiHandle href = vpi_handle(vpiSysTfCall, 0); + s_vpi_value vpi_value; + + vpi_value.format = vpiIntVal; + vpi_value.value.integer = mon_check(); + vpi_put_value(href, &vpi_value, NULL, vpiNoDelay); + + return 0; +} + +static s_vpi_systf_data vpi_systf_data[] = {{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$mon_check", + (PLI_INT32(*)(PLI_BYTE8*))mon_check_vpi, 0, 0, 0}, + 0}; + +// cver entry +void vpi_compat_bootstrap(void) { + p_vpi_systf_data systf_data_p; + systf_data_p = &(vpi_systf_data[0]); + while (systf_data_p->type != 0) vpi_register_systf(systf_data_p++); +} + +// icarus entry +void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; + +#else + +int main(int argc, char** argv) { + Verilated::commandArgs(argc, argv); + const std::unique_ptr contextp{new VerilatedContext}; + const std::unique_ptr top{new VM_PREFIX{contextp.get(), ""}}; + contextp->fatalOnVpiError(0); + +#ifdef VERILATOR +#ifdef TEST_VERBOSE + contextp->scopesDump(); +#endif +#endif + + while (!contextp->gotFinish()) { top->eval(); } + + return 0; +} +#endif diff --git a/test_regress/t/t_vpi_put_value_array.py b/test_regress/t/t_vpi_put_value_array.py new file mode 100755 index 0000000000..9f7ed18323 --- /dev/null +++ b/test_regress/t/t_vpi_put_value_array.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(make_top_shell=False, + make_main=False, + make_pli=True, + verilator_flags2=["--exe --vpi --no-l2name", test.pli_filename], + iv_flags2=["-g2005-sv -D USE_VPI_NOT_DPI"], + v_flags2=["+define+USE_VPI_NOT_DPI +define+VERILATOR_COMMENTS"]) + +test.execute(use_libvpi=True) + +test.passes() diff --git a/test_regress/t/t_vpi_put_value_array.v b/test_regress/t/t_vpi_put_value_array.v new file mode 100644 index 0000000000..f0aefe35b7 --- /dev/null +++ b/test_regress/t/t_vpi_put_value_array.v @@ -0,0 +1,76 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2024 by Diego Roux. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +`ifdef VERILATOR_COMMENTS + `define PUBLIC_FLAT_RD /*verilator public_flat_rd*/ + `define PUBLIC_FLAT_RW /*verilator public_flat_rw*/ +`else + `define PUBLIC_FLAT_RD + `define PUBLIC_FLAT_RW +`endif + +module test (); + +`ifdef VERILATOR +`systemc_header +extern "C" int mon_check(); +`verilog +`endif + + reg [7:0] write_bytes [0:3] `PUBLIC_FLAT_RW; + reg [7:0] write_bytes_rl [3:0] `PUBLIC_FLAT_RW; + reg [7:0] write_bytes_nonzero_index [1:4] `PUBLIC_FLAT_RW; + + reg [15:0] write_shorts [0:3] `PUBLIC_FLAT_RW; + reg [31:0] write_words [0:3] `PUBLIC_FLAT_RW; + reg [63:0] write_longs [0:3] `PUBLIC_FLAT_RW; + reg [68:0] write_customs [0:3] `PUBLIC_FLAT_RW; + reg [68:0] write_customs_nonzero_index_rl [4:1] `PUBLIC_FLAT_RW; + + integer write_integers [0:3] `PUBLIC_FLAT_RW; + + reg [7:0] write_scalar `PUBLIC_FLAT_RW; + reg [7:0] write_bounds [1:3] `PUBLIC_FLAT_RW; + reg [7:0] write_inaccessible [0:3] `PUBLIC_FLAT_RD; + +`ifdef IVERILOG + // stop icarus optimizing signals away + wire redundant = write_bytes[0][0] | + write_bytes[0][0] | + write_bytes_rl[0][0] | + write_bytes_nonzero_index[1][0] | + write_shorts[0][0] | + write_words[0][0] | + write_longs[0][0] | + write_customs[0][0] | + write_customs_nonzero_index_rl[1][0] | + write_integers[0][0] | + write_scalar[0] | + write_bounds[1][0] | + write_inaccessible[0][0]; +`endif + integer status; + + initial begin +`ifdef IVERILOG + status = $mon_check; +`endif + +`ifdef VERILATOR + status = $c32("mon_check()"); +`endif + + if (status != 0) begin + $write("%%Error: t_vpi_put_value_array.cpp:%0d: C Test failed\n", status); + $stop; + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_vpi_unimpl.cpp b/test_regress/t/t_vpi_unimpl.cpp index c6d73f324f..732b30d536 100644 --- a/test_regress/t/t_vpi_unimpl.cpp +++ b/test_regress/t/t_vpi_unimpl.cpp @@ -57,8 +57,6 @@ int _mon_check_unimpl(p_cb_data cb_data) { vpi_get64(0, NULL); vpi_get_delays(NULL, NULL); vpi_put_delays(NULL, NULL); - vpi_get_value_array(NULL, NULL, NULL, 0); - vpi_put_value_array(NULL, NULL, NULL, 0); vpi_get_time(NULL, NULL); vpi_mcd_name(0); vpi_compare_objects(NULL, NULL);