From 69dce205d387ea9f633349424e16a69ca88e4ddb Mon Sep 17 00:00:00 2001 From: Krzysztof Starecki Date: Fri, 10 Jan 2025 01:04:26 +0100 Subject: [PATCH] Support multidimensional array access via VPI (#2812) (#5573) --- include/verilated.cpp | 32 +- include/verilated.h | 2 +- include/verilated_dpi.cpp | 8 +- include/verilated_sym_props.h | 112 +++-- include/verilated_vpi.cpp | 670 ++++++++++++++++-------------- src/V3AstNodes.cpp | 55 ++- src/V3EmitCSyms.cpp | 39 +- test_regress/t/t_scope_map.cpp | 6 +- test_regress/t/t_vpi_dump.out | 16 +- test_regress/t/t_vpi_get.cpp | 56 +-- test_regress/t/t_vpi_memory.cpp | 54 +-- test_regress/t/t_vpi_multidim.cpp | 462 ++++++++++++++++++++ test_regress/t/t_vpi_multidim.py | 22 + test_regress/t/t_vpi_multidim.v | 44 ++ test_regress/t/t_vpi_var.cpp | 16 +- 15 files changed, 1101 insertions(+), 493 deletions(-) create mode 100644 test_regress/t/t_vpi_multidim.cpp create mode 100755 test_regress/t/t_vpi_multidim.py create mode 100644 test_regress/t/t_vpi_multidim.v diff --git a/include/verilated.cpp b/include/verilated.cpp index a99d97dee..ecf7e8d73 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -3289,7 +3289,7 @@ uint32_t VerilatedVarProps::entSize() const VL_MT_SAFE { case VLVT_UINT16: size = sizeof(SData); break; case VLVT_UINT32: size = sizeof(IData); break; case VLVT_UINT64: size = sizeof(QData); break; - case VLVT_WDATA: size = VL_WORDS_I(packed().elements()) * sizeof(IData); break; + case VLVT_WDATA: size = VL_WORDS_I(entBits()) * sizeof(IData); break; default: size = 0; break; // LCOV_EXCL_LINE } return size; @@ -3308,7 +3308,7 @@ void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const uint8_t* bytep = reinterpret_cast(datap); // If on index 1 of a 2 index array, then each index 1 is index2sz*entsz size_t slicesz = entSize(); - for (int d = dim + 1; d <= m_udims; ++d) slicesz *= elements(d); + for (int d = dim + 1; d <= udims(); ++d) slicesz *= elements(d); bytep += indxAdj * slicesz; return bytep; } @@ -3369,32 +3369,28 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_ } void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, bool isParam, - VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE { + VerilatedVarType vltype, int vlflags, int udims, int pdims ...) VL_MT_UNSAFE { // Grab dimensions // In the future we may just create a large table at emit time and // statically construct from that. if (!finalize) return; if (!m_varsp) m_varsp = new VerilatedVarNameMap; - VerilatedVar var(namep, datap, vltype, static_cast(vlflags), dims, isParam); + VerilatedVar var(namep, datap, vltype, static_cast(vlflags), udims, pdims, isParam); va_list ap; - va_start(ap, dims); - for (int i = 0; i < dims; ++i) { + va_start(ap, pdims); + for (int i = 0; i < udims; ++i) { const int msb = va_arg(ap, int); const int lsb = va_arg(ap, int); - if (i == 0) { - var.m_packed.m_left = msb; - var.m_packed.m_right = lsb; - } else if (i >= 1 && i <= var.udims()) { - var.m_unpacked[i - 1].m_left = msb; - var.m_unpacked[i - 1].m_right = lsb; - } else { - // We could have a linked list of ranges, but really this whole thing needs - // to be generalized to support structs and unions, etc. - const std::string msg = "Unsupported multi-dimensional public varInsert: "s + namep; - VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); - } + var.m_unpacked[i].m_left = msb; + var.m_unpacked[i].m_right = lsb; + } + for (int i = 0; i < pdims; ++i) { + const int msb = va_arg(ap, int); + const int lsb = va_arg(ap, int); + var.m_packed[i].m_left = msb; + var.m_packed[i].m_right = lsb; } va_end(ap); diff --git a/include/verilated.h b/include/verilated.h index cee813c31..baccd0610 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -726,7 +726,7 @@ public: // But internals only - called from VerilatedModule's const Type& type) VL_MT_UNSAFE; void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE; void varInsert(int finalize, const char* namep, void* datap, bool isParam, - VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE; + VerilatedVarType vltype, int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE; // ACCESSORS const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; } const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; } diff --git a/include/verilated_dpi.cpp b/include/verilated_dpi.cpp index 04a926646..37b8a878e 100644 --- a/include/verilated_dpi.cpp +++ b/include/verilated_dpi.cpp @@ -289,7 +289,7 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, } case VLVT_WDATA: { WDataOutP wdatap = (reinterpret_cast(datap)); - for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) d[i] = wdatap[i]; + for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) d[i] = wdatap[i]; return; } default: // LCOV_EXCL_START // Errored earlier @@ -328,7 +328,7 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl } case VLVT_WDATA: { WDataOutP wdatap = (reinterpret_cast(datap)); - for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) { + for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) { d[i].aval = wdatap[i]; d[i].bval = 0; } @@ -354,7 +354,7 @@ static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecV case VLVT_UINT64: *(reinterpret_cast(datap)) = VL_SET_QII(s[1], s[0]); break; case VLVT_WDATA: { WDataOutP wdatap = (reinterpret_cast(datap)); - for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) wdatap[i] = s[i]; + for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) wdatap[i] = s[i]; return; } default: // LCOV_EXCL_START // Errored earlier @@ -376,7 +376,7 @@ static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogic case VLVT_UINT64: *(reinterpret_cast(datap)) = VL_SET_QII(s[1].aval, s[0].aval); break; case VLVT_WDATA: { WDataOutP wdatap = (reinterpret_cast(datap)); - for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) wdatap[i] = s[i].aval; + for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) wdatap[i] = s[i].aval; return; } default: // LCOV_EXCL_START // Errored earlier diff --git a/include/verilated_sym_props.h b/include/verilated_sym_props.h index 5298787b0..0d7e5f4a2 100644 --- a/include/verilated_sym_props.h +++ b/include/verilated_sym_props.h @@ -74,27 +74,41 @@ class VerilatedVarProps VL_NOT_FINAL { const uint32_t m_magic; // Magic number const VerilatedVarType m_vltype; // Data type const VerilatedVarFlags m_vlflags; // Direction - const int m_pdims; // Packed dimensions, 0 = none - const int m_udims; // Unpacked dimensions, 0 = none - VerilatedRange m_packed; // Packed array range std::vector m_unpacked; // Unpacked array ranges - void initUnpacked(const int* ulims) { - for (int i = 0; i < m_udims; ++i) { + std::vector m_packed; // Packed array ranges + VerilatedRange m_packedDpi; // Flattened packed array range + void initUnpacked(int udims, const int* ulims) { + for (int i = 0; i < udims; ++i) { const int uleft = ulims ? ulims[2 * i + 0] : 0; const int uright = ulims ? ulims[2 * i + 1] : 0; m_unpacked.emplace_back(uleft, uright); } } + void initPacked(int pdims, const int* plims) { + int packedSize = 1; + for (int i = 0; i < pdims; ++i) { + const int pleft = plims ? plims[2 * i + 0] : 0; + const int pright = plims ? plims[2 * i + 1] : 0; + m_packed.emplace_back(pleft, pright); + packedSize *= abs(pleft - pright) + 1; + } + if (pdims == 1) { + // Preserve packed array range if the packed component is 1-D + m_packedDpi = m_packed.front(); + } else { + m_packedDpi = VerilatedRange{packedSize - 1, 0}; + } + } // CONSTRUCTORS protected: friend class VerilatedScope; - VerilatedVarProps(VerilatedVarType vltype, VerilatedVarFlags vlflags, int pdims, int udims) + VerilatedVarProps(VerilatedVarType vltype, VerilatedVarFlags vlflags, int udims, int pdims) : m_magic{MAGIC} , m_vltype{vltype} - , m_vlflags{vlflags} - , m_pdims{pdims} - , m_udims{udims} { - initUnpacked(nullptr); + , m_vlflags{vlflags} { + // Only preallocate the ranges + initUnpacked(udims, nullptr); + initPacked(pdims, nullptr); } public: @@ -103,35 +117,29 @@ public: VerilatedVarProps(VerilatedVarType vltype, int vlflags) : m_magic{MAGIC} , m_vltype{vltype} - , m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning - , m_pdims{0} - , m_udims{0} {} + , m_vlflags(VerilatedVarFlags(vlflags)) {} // Need () or GCC 4.8 false warning + VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims) : m_magic{MAGIC} , m_vltype{vltype} - , m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning - , m_pdims{0} - , m_udims{udims} { - initUnpacked(ulims); + , m_vlflags(VerilatedVarFlags(vlflags)) { // Need () or GCC 4.8 false warning + initUnpacked(udims, ulims); } // With packed class Packed {}; - VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pl, int pr) + VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pdims, const int* plims) : m_magic{MAGIC} , m_vltype{vltype} - , m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning - , m_pdims{1} - , m_udims{0} - , m_packed{pl, pr} {} - VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pl, int pr, Unpacked, - int udims, const int* ulims) + , m_vlflags(VerilatedVarFlags(vlflags)) { // Need () or GCC 4.8 false warning + initPacked(pdims, plims); + } + VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims, + Packed, int pdims, const int* plims) : m_magic{MAGIC} , m_vltype{vltype} - , m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning - , m_pdims{1} - , m_udims{udims} - , m_packed{pl, pr} { - initUnpacked(ulims); + , m_vlflags(VerilatedVarFlags(vlflags)) { // Need () or GCC 4.8 false warning + initUnpacked(udims, ulims); + initPacked(pdims, plims); } ~VerilatedVarProps() = default; @@ -142,41 +150,56 @@ public: return static_cast(static_cast(m_vlflags) & VLVF_MASK_DIR); } uint32_t entSize() const VL_MT_SAFE; + uint32_t entBits() const VL_MT_SAFE { + uint32_t bits = 1; + for (auto it : m_packed) + bits *= it.elements(); + return bits; + } bool isPublicRW() const { return ((m_vlflags & VLVF_PUB_RW) != 0); } // DPI compatible C standard layout bool isDpiCLayout() const { return ((m_vlflags & VLVF_DPI_CLAY) != 0); } - int udims() const VL_MT_SAFE { return m_udims; } - int dims() const { return m_pdims + m_udims; } - const VerilatedRange& packed() const VL_MT_SAFE { return m_packed; } - const VerilatedRange& unpacked() const { return m_unpacked[0]; } - // DPI accessors + int udims() const VL_MT_SAFE { return m_unpacked.size(); } + int pdims() const VL_MT_SAFE { return m_packed.size(); } + int dims() const VL_MT_SAFE { return pdims() + udims(); } + const std::vector& packedRanges() const VL_MT_SAFE { return m_packed; } + const std::vector& unpackedRanges() const VL_MT_SAFE { return m_unpacked; } + const VerilatedRange* range(int dim) const VL_MT_SAFE { + if (dim < udims()) + return &m_unpacked[dim]; + else if (dim < dims()) + return &m_packed[dim - udims()]; + else + return nullptr; + } + // DPI accessors (with packed dimensions flattened!) int left(int dim) const VL_MT_SAFE { - return dim == 0 ? m_packed.left() + return dim == 0 ? m_packedDpi.left() : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].left() : 0; } int right(int dim) const VL_MT_SAFE { - return dim == 0 ? m_packed.right() + return dim == 0 ? m_packedDpi.right() : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].right() : 0; } int low(int dim) const VL_MT_SAFE { - return dim == 0 ? m_packed.low() + return dim == 0 ? m_packedDpi.low() : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].low() : 0; } int high(int dim) const VL_MT_SAFE { - return dim == 0 ? m_packed.high() + return dim == 0 ? m_packedDpi.high() : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].high() : 0; } int increment(int dim) const { - return dim == 0 ? m_packed.increment() + return dim == 0 ? m_packedDpi.increment() : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].increment() : 0; } int elements(int dim) const VL_MT_SAFE { - return dim == 0 ? m_packed.elements() + return dim == 0 ? m_packedDpi.elements() : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].elements() : 0; } @@ -208,8 +231,7 @@ public: bool magicOk() const { return m_propsp->magicOk(); } VerilatedVarType vltype() const { return m_propsp->vltype(); } bool isDpiStdLayout() const { return m_propsp->isDpiCLayout(); } - const VerilatedRange& packed() const { return m_propsp->packed(); } - const VerilatedRange& unpacked() const { return m_propsp->unpacked(); } + int entBits() const { return m_propsp->entBits(); } int udims() const VL_MT_SAFE { return m_propsp->udims(); } int left(int dim) const VL_MT_SAFE { return m_propsp->left(dim); } int right(int dim) const VL_MT_SAFE { return m_propsp->right(dim); } @@ -236,8 +258,8 @@ protected: friend class VerilatedScope; // CONSTRUCTORS VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype, - VerilatedVarFlags vlflags, int dims, bool isParam) - : VerilatedVarProps{vltype, vlflags, (dims > 0 ? 1 : 0), ((dims > 1) ? dims - 1 : 0)} + VerilatedVarFlags vlflags, int udims, int pdims, bool isParam) + : VerilatedVarProps{vltype, vlflags, udims, pdims} , m_datap{datap} , m_namep{namep} , m_isParam{isParam} {} @@ -246,8 +268,6 @@ public: ~VerilatedVar() = default; // ACCESSORS void* datap() const { return m_datap; } - const VerilatedRange& range() const { return packed(); } // Deprecated - const VerilatedRange& array() const { return unpacked(); } // Deprecated const char* name() const { return m_namep; } bool isParam() const { return m_isParam; } }; diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 795f367ae..7e661b4c6 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -85,7 +85,7 @@ public: // To simplify our free list, we use a size large enough for all derived types // We reserve word zero for the next pointer, as that's safer in case a // dangling reference to the original remains around. - static constexpr size_t CHUNK_SIZE = 96; + static constexpr size_t CHUNK_SIZE = 128; if (VL_UNCOVERABLE(size > CHUNK_SIZE)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase CHUNK_SIZE"); if (VL_LIKELY(t_freeHeadp)) { @@ -171,9 +171,9 @@ protected: const VerilatedVar* m_varp = nullptr; const VerilatedScope* m_scopep = nullptr; std::string m_fullname; - const VerilatedRange& get_range() const { - // Determine number of dimensions and return outermost - return (m_varp->dims() > 1) ? m_varp->unpacked() : m_varp->packed(); + int32_t m_indexedDim = -1; + const VerilatedRange* get_range() const { + return m_varp->range(m_indexedDim + 1); } public: @@ -186,6 +186,7 @@ public: m_varp = varp->m_varp; m_scopep = varp->m_scopep; m_fullname = varp->m_fullname; + m_indexedDim = varp->m_indexedDim; } } static VerilatedVpioVarBase* castp(vpiHandle h) { @@ -193,10 +194,36 @@ public: } const VerilatedVar* varp() const { return m_varp; } const VerilatedScope* scopep() const { return m_scopep; } - uint32_t size() const override { return get_range().elements(); } - const VerilatedRange* rangep() const override { return &get_range(); } + // Returns the number of the currently indexed dimension (starting at -1 for none). + int32_t indexedDim() const { return m_indexedDim; } + // Returns whether the currently indexed dimension is unpacked. + bool isIndexedDimUnpacked() const { return indexedDim() + 1 < varp()->udims(); } + // Returns a maximum accessible dimension number, counting only unpacked dimensions + // (if onlyUnpacked == true), or both unpacked + packed. + int32_t maxDim(bool onlyUnpacked) const { + return onlyUnpacked ? varp()->udims() - 1 : varp()->dims() - 1; + } + // Returns a number of elements in the array, stopping at the unpacked-packed boundary. + uint32_t size() const override { + const int maxDimNum = maxDim(isIndexedDimUnpacked()); + int size = 1; + for (int dim = indexedDim() + 1; dim <= maxDimNum; dim++) + size *= varp()->range(dim)->elements(); + return size; + } + // If the array is unpacked, returns the bitsize of a single underlying packed element. + // If the array is packed, returns the bitsize of the whole array. + uint32_t bitSize() const { + if (isIndexedDimUnpacked()) + return varp()->entBits(); + else + return size(); + } + const VerilatedRange* rangep() const override { return get_range(); } 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(); } + virtual uint32_t bitOffset() const { return 0; } }; class VerilatedVpioParam final : public VerilatedVpioVarBase { @@ -221,7 +248,6 @@ public: default: return vpiUndefined; } } - void* varDatap() const { return m_varp->datap(); } }; class VerilatedVpioRange final : public VerilatedVpio { @@ -240,25 +266,27 @@ public: }; class VerilatedVpioRangeIter final : public VerilatedVpio { - // Only supports 1 dimension - const VerilatedRange* const m_rangep; - bool m_done = false; + const std::vector m_ranges; + std::vector::const_iterator m_iter; public: - explicit VerilatedVpioRangeIter(const VerilatedRange* rangep) - : m_rangep{rangep} {} + explicit VerilatedVpioRangeIter(const std::vector& ranges) + : m_ranges{ranges} { + m_iter = m_ranges.begin(); + } ~VerilatedVpioRangeIter() override = default; static VerilatedVpioRangeIter* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } uint32_t type() const override { return vpiIterator; } vpiHandle dovpi_scan() override { - if (VL_UNLIKELY(m_done)) { + if (VL_UNLIKELY(m_iter == m_ranges.end())) { delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle return nullptr; } - m_done = true; - return ((new VerilatedVpioRange{m_rangep})->castVpiHandle()); + VerilatedRange* const rangep = new VerilatedRange(*m_iter); + ++m_iter; + return ((new VerilatedVpioRange{rangep})->castVpiHandle()); } }; @@ -297,14 +325,15 @@ class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpioVarBase { uint32_t u32; } m_mask; // memoized variable mask uint32_t m_entSize = 0; // memoized variable size + uint32_t m_bitOffset = 0; protected: void* m_varDatap = nullptr; // varp()->datap() adjusted for array entries - int32_t m_index = 0; + std::vector m_index; public: VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep) : VerilatedVpioVarBase{varp, scopep} { - m_mask.u32 = VL_MASK_I(varp->packed().elements()); + m_mask.u32 = VL_MASK_I(varp->entBits()); m_entSize = varp->entSize(); m_varDatap = varp->datap(); } @@ -326,10 +355,30 @@ public: static VerilatedVpioVar* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } + uint32_t bitOffset() const override { return m_bitOffset; } uint32_t mask() const { return m_mask.u32; } uint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; } uint32_t entSize() const { return m_entSize; } - uint32_t index() const { return m_index; } + const std::vector& index() const { return m_index; } + VerilatedVpioVar* withIndex(int32_t index) const { + if (VL_UNLIKELY(indexedDim() + 1 >= varp()->dims())) + return nullptr; + + auto ret = new VerilatedVpioVar{this}; + ret->m_index.push_back(index); + ret->m_indexedDim++; + + int chunkSize = 1; + for (int dim = maxDim(isIndexedDimUnpacked()); dim > indexedDim() + 1; dim--) + chunkSize *= varp()->range(dim)->elements(); + + if (isIndexedDimUnpacked()) + ret->m_varDatap = (static_cast(ret->m_varDatap)) + entSize() * chunkSize * (index - get_range()->low()); + else + ret->m_bitOffset += chunkSize * (index - get_range()->low()); + + return ret; + } uint32_t type() const override { uint32_t type = vpiReg; switch (varp()->vltype()) { @@ -337,10 +386,21 @@ public: case VLVT_STRING: type = vpiStringVar; break; default: break; } - return (varp()->dims() > 1) ? vpiMemory : type; // but might be wire, logic + if (isIndexedDimUnpacked()) + return vpiRegArray; + else + return type; + } + const char* fullname() const override { + static thread_local std::string t_out; + t_out = std::string{scopep()->name()} + "." + name(); + for (auto idx : index()) { + t_out += "[" + std::to_string(idx) + "]"; + } + return t_out.c_str(); } void* prevDatap() const { return m_prevDatap; } - void* varDatap() const { return m_varDatap; } + void* varDatap() const override { return m_varDatap; } void createPrevDatap() { if (VL_UNLIKELY(!m_prevDatap)) { m_prevDatap = new uint8_t[entSize()]; @@ -349,31 +409,6 @@ public: } }; -class VerilatedVpioMemoryWord final : public VerilatedVpioVar { -public: - VerilatedVpioMemoryWord(const VerilatedVar* varp, const VerilatedScope* scopep, int32_t index, - int offset) - : VerilatedVpioVar{varp, scopep} { - m_index = index; - m_varDatap = (static_cast(varp->datap())) + entSize() * offset; - } - ~VerilatedVpioMemoryWord() override = default; - static VerilatedVpioMemoryWord* castp(vpiHandle h) { - return dynamic_cast(reinterpret_cast(h)); - } - uint32_t type() const override { return vpiMemoryWord; } - uint32_t size() const override { return varp()->packed().elements(); } - const VerilatedRange* rangep() const override { return &(varp()->packed()); } - const char* fullname() const override { - static thread_local std::string t_out; - constexpr size_t LEN_MAX_INDEX = 25; - char num[LEN_MAX_INDEX]; - VL_SNPRINTF(num, LEN_MAX_INDEX, "%d", m_index); - t_out = std::string{scopep()->name()} + "." + name() + "[" + num + "]"; - return t_out.c_str(); - } -}; - class VerilatedVpioVarIter final : public VerilatedVpio { const VerilatedScope* const m_scopep; VerilatedVarNameMap::const_iterator m_it; @@ -433,35 +468,54 @@ public: } }; -class VerilatedVpioMemoryWordIter final : public VerilatedVpio { - const vpiHandle m_handle; - const VerilatedVar* const m_varp; - int32_t m_iteration; - const int32_t m_direction; - bool m_done = false; +class VerilatedVpioRegIter final : public VerilatedVpio { + VerilatedVpioVar* m_var; + std::vector m_ranges; + std::vector m_nextIndex; + const int32_t m_maxDim; public: - VerilatedVpioMemoryWordIter(const vpiHandle handle, const VerilatedVar* varp) - : m_handle{handle} - , m_varp{varp} - , m_iteration{varp->unpacked().right()} - , m_direction{VL_LIKELY(varp->unpacked().left() > varp->unpacked().right()) ? 1 : -1} {} - ~VerilatedVpioMemoryWordIter() override = default; - static VerilatedVpioMemoryWordIter* castp(vpiHandle h) { - return dynamic_cast(reinterpret_cast(h)); + explicit VerilatedVpioRegIter(const VerilatedVpioVar* vop) + : m_var{new VerilatedVpioVar(vop)} + , m_maxDim{vop->varp()->udims() - 1} { + for (auto it = vop->indexedDim() + 1; it <= m_maxDim; it++) + m_ranges.push_back(*vop->varp()->range(it)); + for (auto it : m_ranges) + m_nextIndex.push_back(it.right()); + } + ~VerilatedVpioRegIter() override = default; + static VerilatedVpioRegIter* castp(vpiHandle h) { + return dynamic_cast(reinterpret_cast(h)); } uint32_t type() const override { return vpiIterator; } - void iterationInc() { - if (!(m_done = (m_iteration == m_varp->unpacked().left()))) m_iteration += m_direction; - } vpiHandle dovpi_scan() override { - if (VL_UNLIKELY(m_done)) { - delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle + if (VL_UNLIKELY(m_var->indexedDim() >= m_maxDim)) { + // Trying to iterate over a non-array object + delete this; return nullptr; } - const vpiHandle result = vpi_handle_by_index(m_handle, m_iteration); - iterationInc(); - return result; + if (m_nextIndex.front() > m_ranges.front().high() || + m_nextIndex.front() < m_ranges.front().low()) { + // Finished iterating + delete this; + return nullptr; + } + + VerilatedVpioVar* ret = m_var; + for (auto it : m_nextIndex) + ret = ret->withIndex(it); + + // Increase the index, pretending the dimensions are flattened + for (int32_t it = m_ranges.size() - 1; it >= 0; it--) { + m_nextIndex.at(it) += m_ranges.at(it).increment(); + if (m_nextIndex.at(it) <= m_ranges.at(it).high() && + m_nextIndex.at(it) >= m_ranges.at(it).low()) + break; + else if (it > 0) + m_nextIndex.at(it) = m_ranges.at(it).right(); + } + + return ret->castVpiHandle(); } }; @@ -686,7 +740,7 @@ public: break; } case VLVT_WDATA: { - words = VL_WORDS_I(vop->varp()->packed().elements()); + words = VL_WORDS_I(vop->varp()->entBits()); break; } default: break; @@ -2047,26 +2101,17 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_index %p %d\n", object, indx);); VerilatedVpiImp::assertOneCheck(); VL_VPI_ERROR_RESET_(); - // Memory words are not indexable - const VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object); - if (VL_UNLIKELY(vop)) return nullptr; const VerilatedVpioVar* const varop = VerilatedVpioVar::castp(object); if (VL_LIKELY(varop)) { - if (varop->varp()->dims() < 2) return nullptr; - if (VL_LIKELY(varop->varp()->unpacked().left() >= varop->varp()->unpacked().right())) { - if (VL_UNLIKELY(indx > varop->varp()->unpacked().left() - || indx < varop->varp()->unpacked().right())) - return nullptr; - return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx, - indx - varop->varp()->unpacked().right()}) - ->castVpiHandle(); - } - if (VL_UNLIKELY(indx < varop->varp()->unpacked().left() - || indx > varop->varp()->unpacked().right())) + // Case: no dimensions left to index + if (VL_UNLIKELY(varop->indexedDim() + 1 > varop->varp()->dims() - 1)) return nullptr; - return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx, - indx - varop->varp()->unpacked().left()}) - ->castVpiHandle(); + + // Case: index out of range + if (VL_UNLIKELY(indx < varop->rangep()->low() || indx > varop->rangep()->high())) + return nullptr; + + return varop->withIndex(indx)->castVpiHandle(); } VL_VPI_INTERNAL_(__FILE__, __LINE__, "%s : can't resolve handle", __func__); return nullptr; @@ -2108,7 +2153,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { case vpiIndex: { const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; - const int32_t val = vop->index(); + const int32_t val = vop->index().back(); return (new VerilatedVpioConst{val})->castVpiHandle(); } case vpiScope: { @@ -2117,7 +2162,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { return (new VerilatedVpioScope{vop->scopep()})->castVpiHandle(); } case vpiParent: { - const VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object); + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; return (new VerilatedVpioVar{vop->varp(), vop->scopep()})->castVpiHandle(); } @@ -2139,35 +2184,26 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { VerilatedVpiImp::assertOneCheck(); VL_VPI_ERROR_RESET_(); switch (type) { - case vpiMemoryWord: { - const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); - if (VL_UNLIKELY(!vop)) return nullptr; - if (vop->varp()->dims() < 2) return nullptr; - if (vop->varp()->dims() > 2) { - VL_VPI_WARNING_(__FILE__, __LINE__, - "%s: %s, object %s has unsupported number of indices (%d)", __func__, - VerilatedVpiError::strFromVpiMethod(type), vop->fullname(), - vop->varp()->dims()); - } - return (new VerilatedVpioMemoryWordIter{object, vop->varp()})->castVpiHandle(); - } case vpiRange: { const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; - if (vop->varp()->dims() < 2) return nullptr; - // Unsupported is multidim list - if (vop->varp()->dims() > 2) { - VL_VPI_WARNING_(__FILE__, __LINE__, - "%s: %s, object %s has unsupported number of indices (%d)", __func__, - VerilatedVpiError::strFromVpiMethod(type), vop->fullname(), - vop->varp()->dims()); - } - return ((new VerilatedVpioRangeIter{vop->rangep()})->castVpiHandle()); + + std::vector ranges; + const int maxDim = vop->maxDim(vop->isIndexedDimUnpacked()); + for (int dim = vop->indexedDim() + 1; dim <= maxDim; dim++) + ranges.emplace_back(*vop->varp()->range(dim)); + + // allow one more range layer (regbit) + if (ranges.empty()) + ranges.emplace_back(VerilatedRange(0, 0)); + return ((new VerilatedVpioRangeIter{ranges})->castVpiHandle()); } case vpiReg: { - const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object); - if (VL_UNLIKELY(!vop)) return nullptr; - return ((new VerilatedVpioVarIter{vop})->castVpiHandle()); + const VerilatedVpioScope* const vscopep = VerilatedVpioScope::castp(object); + if (vscopep) return ((new VerilatedVpioVarIter{vscopep, false})->castVpiHandle()); + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); + if (vop) return ((new VerilatedVpioRegIter{vop})->castVpiHandle()); + return nullptr; } case vpiParameter: { const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object); @@ -2249,7 +2285,7 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) { case vpiVector: { const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object); if (VL_UNLIKELY(!vop)) return vpiUndefined; - return (property == vpiVector) ^ (vop->varp()->dims() == 0); + return (property == vpiVector) ^ (vop->varp()->packedRanges().empty() || !vop->rangep()); } case vpiSize: { const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object); @@ -2347,7 +2383,9 @@ bool vl_check_format(const VerilatedVar* varp, const p_vpi_value valuep, const c switch (varp->vltype()) { case VLVT_UINT8: case VLVT_UINT16: - case VLVT_UINT32: return status; + case VLVT_UINT32: + case VLVT_UINT64: + case VLVT_WDATA: return status; default: status = false; } } else if (valuep->format == vpiRealVal) { @@ -2384,11 +2422,136 @@ static void vl_strprintf(std::string& buffer, char const* fmt, ...) { va_end(args); } -void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, - const char* fullname) { +// Information about how to access packed array data. +// If underlying type is multi-word (VLVT_WDATA), the packed element might straddle word boundaries, +// in which case m_maskHi != 0. +template +struct VarAccessInfo final { + T* m_datap; // Typed pointer to packed array base address + size_t m_bitOffset; // Data start location (bit offset) + size_t m_wordOffset; // Data start location (word offset, VLVT_WDATA only) + T m_maskLo; // Access mask for m_datap[m_wordOffset] + T m_maskHi; // Access mask for m_datap[m_wordOffset + 1] (VLVT_WDATA only) +}; + +template +VarAccessInfo vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) { + // VarAccessInfo generation + // vop - variable to access (already indexed) + // bitCount - how many bits to write/read + // addOffset - additional offset to apply (within the packed array element) + + const size_t wordBits = sizeof(T) * 8; + uint32_t varBits = vop->bitSize(); + + if (vop->varp()->vltype() == VLVT_REAL) + varBits *= sizeof(double) * 8; + + // make sure we're not trying to write outside var bounds + assert(varBits > addOffset); + bitCount = std::min(bitCount, varBits - addOffset); + + VarAccessInfo info; + info.m_datap = reinterpret_cast(vop->varDatap()); + if (vop->varp()->vltype() == VLVT_WDATA) { + assert(sizeof(T) == sizeof(EData)); + assert(bitCount <= wordBits); + info.m_wordOffset = (vop->bitOffset() + addOffset) / wordBits; + info.m_bitOffset = (vop->bitOffset() + addOffset) % wordBits; + if (bitCount + info.m_bitOffset <= wordBits) { + // within single word + if (bitCount == wordBits) + info.m_maskLo = ~static_cast(0); + else + info.m_maskLo = (static_cast(1) << bitCount) - 1; + info.m_maskLo = info.m_maskLo << info.m_bitOffset; + info.m_maskHi = 0; + } else { + // straddles word boundary + info.m_maskLo = (static_cast(1) << (wordBits - info.m_bitOffset)) - 1; + info.m_maskLo = info.m_maskLo << info.m_bitOffset; + info.m_maskHi = (static_cast(1) << (bitCount + info.m_bitOffset - wordBits)) - 1; + } + } else { + info.m_wordOffset = 0; + info.m_bitOffset = vop->bitOffset() + addOffset; + assert(bitCount + info.m_bitOffset <= wordBits); + if (bitCount < wordBits) { + info.m_maskLo = (static_cast(1) << bitCount) - 1; + info.m_maskLo = info.m_maskLo << info.m_bitOffset; + } else { + info.m_maskLo = ~static_cast(0); + } + info.m_maskHi = 0; + } + return info; +} + +template +T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) { + const size_t wordBits = sizeof(T) * 8; + const VarAccessInfo info = vl_vpi_var_access_info(vop, bitCount, addOffset); + if (info.m_maskHi) + return ((info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset) | + ((info.m_datap[info.m_wordOffset + 1] & info.m_maskHi) << (wordBits - info.m_bitOffset)); + else + return (info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset; +} + +template +void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) { + const size_t wordBits = sizeof(T) * 8; + const VarAccessInfo info = vl_vpi_var_access_info(vop, bitCount, addOffset); + + if (info.m_maskHi) { + info.m_datap[info.m_wordOffset + 1] = (info.m_datap[info.m_wordOffset+1] & ~info.m_maskHi) | + ((word >> (wordBits - info.m_bitOffset)) & info.m_maskHi); + } + info.m_datap[info.m_wordOffset] = (info.m_datap[info.m_wordOffset] & ~info.m_maskLo) | + ((word << info.m_bitOffset) & info.m_maskLo); +} + +// bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds +// addOffset: additional read bitoffset +QData vl_vpi_get_word(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) { + switch (vop->varp()->vltype()) { + case VLVT_UINT8: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + case VLVT_UINT16: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + case VLVT_UINT32: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + case VLVT_UINT64: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + case VLVT_WDATA: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + default: + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__, vop->varp()->vltype()); + return 0; + } +} + +// word: data to be written +// bitCount: maximum number of bits to write, will stop earlier if it reaches the var bounds +// addOffset: additional write bitoffset +void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, size_t addOffset) { + switch (vop->varp()->vltype()) { + case VLVT_UINT8: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + case VLVT_UINT16: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + case VLVT_UINT32: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + case VLVT_UINT64: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + case VLVT_WDATA: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + default: + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__, vop->varp()->vltype()); + } +} + +void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { + const VerilatedVar* varp = vop->varp(); + void* varDatap = vop->varDatap(); + const char* fullname = vop->fullname(); + if (!vl_check_format(varp, valuep, fullname, true)) return; // string data type is dynamic and may vary in size during simulation static thread_local std::string t_outDynamicStr; + + const int varBits = vop->bitSize(); + // We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal // This may cause backward compatibility issues with older code. if (valuep->format == vpiVectorVal) { @@ -2396,27 +2559,8 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, // It only needs to persist until the next vpi_get_value static thread_local t_vpi_vecval t_out[VL_VALUE_STRING_MAX_WORDS * 2]; valuep->value.vector = t_out; - if (varp->vltype() == VLVT_UINT8) { - t_out[0].aval = *(reinterpret_cast(varDatap)); - t_out[0].bval = 0; - return; - } else if (varp->vltype() == VLVT_UINT16) { - t_out[0].aval = *(reinterpret_cast(varDatap)); - t_out[0].bval = 0; - return; - } else if (varp->vltype() == VLVT_UINT32) { - t_out[0].aval = *(reinterpret_cast(varDatap)); - t_out[0].bval = 0; - return; - } else if (varp->vltype() == VLVT_UINT64) { - const QData data = *(reinterpret_cast(varDatap)); - t_out[1].aval = static_cast(data >> 32ULL); - t_out[1].bval = 0; - t_out[0].aval = static_cast(data); - t_out[0].bval = 0; - return; - } else if (varp->vltype() == VLVT_WDATA) { - const int words = VL_WORDS_I(varp->packed().elements()); + if (varp->vltype() == VLVT_WDATA) { + const int words = VL_WORDS_I(varBits); if (VL_UNCOVERABLE(words >= VL_VALUE_STRING_MAX_WORDS)) { VL_VPI_ERROR_( __FILE__, __LINE__, @@ -2424,82 +2568,63 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, "recompile"); return; } - const WDataInP datap = (reinterpret_cast(varDatap)); for (int i = 0; i < words; ++i) { - t_out[i].aval = datap[i]; + t_out[i].aval = vl_vpi_get_word(vop, 32, i * 32); t_out[i].bval = 0; } return; + } else if (varp->vltype() == VLVT_UINT64 && varBits > 32) { + const QData data = vl_vpi_get_word(vop, 64, 0); + t_out[1].aval = static_cast(data >> 32ULL); + t_out[1].bval = 0; + t_out[0].aval = static_cast(data); + t_out[0].bval = 0; + return; + } else { + t_out[0].aval = vl_vpi_get_word(vop, 32, 0); + t_out[0].bval = 0; + return; } } else if (valuep->format == vpiBinStrVal) { - int bits = varp->packed().elements(); - t_outDynamicStr.resize(bits); + t_outDynamicStr.resize(varBits); const CData* datap = (reinterpret_cast(varDatap)); - for (size_t i = 0; i < bits; ++i) { - const char val = (datap[i >> 3] >> (i & 7)) & 1; - t_outDynamicStr[bits - i - 1] = val ? '1' : '0'; + for (size_t i = 0; i < varBits; ++i) { + const size_t pos = i + vop->bitOffset(); + const char val = (datap[pos >> 3] >> (pos & 7)) & 1; + t_outDynamicStr[varBits - i - 1] = val ? '1' : '0'; } valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; } else if (valuep->format == vpiOctStrVal) { - int chars = (varp->packed().elements() + 2) / 3; + const int chars = (varBits + 2) / 3; t_outDynamicStr.resize(chars); - const int bytes = VL_BYTES_I(varp->packed().elements()); - const CData* datap = (reinterpret_cast(varDatap)); for (size_t i = 0; i < chars; ++i) { - const div_t idx = div(i * 3, 8); - int val = datap[idx.quot]; - if ((idx.quot + 1) < bytes) { - // if the next byte is valid or that in - // for when the required 3 bits straddle adjacent bytes - val |= datap[idx.quot + 1] << 8; - } - // align so least significant 3 bits represent octal char - val >>= idx.rem; - if (i == (chars - 1)) { - // most significant char, mask off nonexistent bits when vector - // size is not a multiple of 3 - const unsigned int rem = varp->packed().elements() % 3; - if (rem) { - // generate bit mask & zero nonexistent bits - val &= (1 << rem) - 1; - } - } - t_outDynamicStr[chars - i - 1] = '0' + (val & 7); + const char val = vl_vpi_get_word(vop, 3, i * 3); + t_outDynamicStr[chars - i - 1] = '0' + val; } valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; } else if (valuep->format == vpiDecStrVal) { if (varp->vltype() == VLVT_UINT8) { vl_strprintf(t_outDynamicStr, "%hhu", - static_cast(*(reinterpret_cast(varDatap)))); + static_cast(vl_vpi_get_word(vop, 8, 0))); } else if (varp->vltype() == VLVT_UINT16) { vl_strprintf(t_outDynamicStr, "%hu", - static_cast(*(reinterpret_cast(varDatap)))); + static_cast(vl_vpi_get_word(vop, 16, 0))); } else if (varp->vltype() == VLVT_UINT32) { vl_strprintf(t_outDynamicStr, "%u", - static_cast(*(reinterpret_cast(varDatap)))); + static_cast(vl_vpi_get_word(vop, 32, 0))); } else if (varp->vltype() == VLVT_UINT64) { vl_strprintf(t_outDynamicStr, "%llu", // lintok-format-ll - static_cast(*(reinterpret_cast(varDatap)))); + static_cast(vl_vpi_get_word(vop, 64, 0))); } valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; } else if (valuep->format == vpiHexStrVal) { - int chars = (varp->packed().elements() + 3) >> 2; + const int chars = (varBits + 3) >> 2; t_outDynamicStr.resize(chars); - const CData* datap = (reinterpret_cast(varDatap)); for (size_t i = 0; i < chars; ++i) { - char val = (datap[i >> 1] >> ((i & 1) << 2)) & 15; - if (i == (chars - 1)) { - // most significant char, mask off nonexistent bits when vector - // size is not a multiple of 4 - const unsigned int rem = varp->packed().elements() & 3; - if (rem) { - // generate bit mask & zero nonexistent bits - val &= (1 << rem) - 1; - } - } + const char val = vl_vpi_get_word(vop, 4, i * 4); t_outDynamicStr[chars - i - 1] = "0123456789abcdef"[static_cast(val)]; } valuep->value.str = const_cast(t_outDynamicStr.c_str()); @@ -2515,28 +2640,19 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, return; } } else { - int bytes = VL_BYTES_I(varp->packed().elements()); - t_outDynamicStr.resize(bytes); - const CData* datap = (reinterpret_cast(varDatap)); - for (size_t i = 0; i < bytes; ++i) { - const char val = datap[bytes - i - 1]; + const int chars = VL_BYTES_I(varBits); + t_outDynamicStr.resize(chars); + for (size_t i = 0; i < chars; ++i) { + const char val = vl_vpi_get_word(vop, 8, i * 8); // other simulators replace [leading?] zero chars with spaces, replicate here. - t_outDynamicStr[i] = val ? val : ' '; + t_outDynamicStr[chars - i - 1] = val ? val : ' '; } valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; } } else if (valuep->format == vpiIntVal) { - if (varp->vltype() == VLVT_UINT8) { - valuep->value.integer = *(reinterpret_cast(varDatap)); - return; - } else if (varp->vltype() == VLVT_UINT16) { - valuep->value.integer = *(reinterpret_cast(varDatap)); - return; - } else if (varp->vltype() == VLVT_UINT32) { - valuep->value.integer = *(reinterpret_cast(varDatap)); - return; - } + valuep->value.integer = vl_vpi_get_word(vop, 32, 0); + return; } else if (valuep->format == vpiRealVal) { valuep->value.real = *(reinterpret_cast(varDatap)); return; @@ -2554,10 +2670,10 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) { if (VL_UNLIKELY(!valuep)) return; if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) { - vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname()); + vl_vpi_get_value(vop, valuep); return; } else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) { - vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname()); + vl_vpi_get_value(vop, valuep); return; } else if (const VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) { if (valuep->format == vpiIntVal) { @@ -2580,7 +2696,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer"); return nullptr; } - PLI_INT32 delay_mode = flags & 0xfff; + const PLI_INT32 delay_mode = flags & 0xfff; if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) { VL_DEBUG_IF_PLI( VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(), @@ -2607,97 +2723,51 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ return object; } VerilatedVpiImp::evalNeeded(true); + const int varBits = vop->bitSize(); if (valuep->format == vpiVectorVal) { if (VL_UNLIKELY(!valuep->value.vector)) return nullptr; - if (vop->varp()->vltype() == VLVT_UINT8) { - *(reinterpret_cast(vop->varDatap())) - = valuep->value.vector[0].aval & vop->mask(); + if (vop->varp()->vltype() == VLVT_WDATA) { + const int words = VL_WORDS_I(varBits); + for (int i = 0; i < words; ++i) + vl_vpi_put_word(vop, valuep->value.vector[i].aval, 32, i * 32); return object; - } else if (vop->varp()->vltype() == VLVT_UINT16) { - *(reinterpret_cast(vop->varDatap())) - = valuep->value.vector[0].aval & vop->mask(); + } else if (vop->varp()->vltype() == VLVT_UINT64 && varBits > 32) { + const QData val = (static_cast(valuep->value.vector[1].aval) << 32) | + static_cast(valuep->value.vector[0].aval); + vl_vpi_put_word(vop, val, 64, 0); return object; - } else if (vop->varp()->vltype() == VLVT_UINT32) { - *(reinterpret_cast(vop->varDatap())) - = valuep->value.vector[0].aval & vop->mask(); - return object; - } else if (vop->varp()->vltype() == VLVT_UINT64) { - *(reinterpret_cast(vop->varDatap())) = VL_SET_QII( - valuep->value.vector[1].aval & vop->mask(), valuep->value.vector[0].aval); - return object; - } else if (vop->varp()->vltype() == VLVT_WDATA) { - const int words = VL_WORDS_I(vop->varp()->packed().elements()); - WDataOutP datap = (reinterpret_cast(vop->varDatap())); - for (int i = 0; i < words; ++i) { - datap[i] = valuep->value.vector[i].aval; - if (i == (words - 1)) datap[i] &= vop->mask(); - } + } else { + vl_vpi_put_word(vop, valuep->value.vector[0].aval, 32, 0); return object; } } else if (valuep->format == vpiBinStrVal) { - const int bits = vop->varp()->packed().elements(); const int len = std::strlen(valuep->value.str); CData* const datap = (reinterpret_cast(vop->varDatap())); - for (int i = 0; i < bits; ++i) { - const char set = (i < len) ? (valuep->value.str[len - i - 1] == '1') : 0; - // zero bits 7:1 of byte when assigning to bit 0, else - // or in 1 if bit set - if (i & 7) { - datap[i >> 3] |= set << (i & 7); - } else { - datap[i >> 3] = set; - } + 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; + + if (set) + datap[pos >> 3] |= 1 << (pos & 7); + else + datap[pos >> 3] &= ~(1 << (pos & 7)); } return object; } else if (valuep->format == vpiOctStrVal) { - const int chars = (vop->varp()->packed().elements() + 2) / 3; - const int bytes = VL_BYTES_I(vop->varp()->packed().elements()); + const int chars = (varBits + 2) / 3; const int len = std::strlen(valuep->value.str); - CData* const datap = (reinterpret_cast(vop->varDatap())); - div_t idx; - datap[0] = 0; // reset zero'th byte - for (int i = 0; i < chars; ++i) { - union { - char byte[2]; - uint16_t half; - } val; - idx = div(i * 3, 8); - if (i < len) { - // ignore illegal chars - const char digit = valuep->value.str[len - i - 1]; - if (digit >= '0' && digit <= '7') { - val.half = digit - '0'; - } else { - VL_VPI_WARNING_(__FILE__, __LINE__, - "%s: Non octal character '%c' in '%s' as value %s for %s", - __func__, digit, valuep->value.str, - VerilatedVpiError::strFromVpiVal(valuep->format), - vop->fullname()); - val.half = 0; - } - } else { - val.half = 0; - } - // align octal character to position within vector, note that - // the three bits may straddle a byte boundary so two byte wide - // assignments are made to adjacent bytes - but not if the least - // significant byte of the aligned value is the most significant - // byte of the destination. - val.half <<= idx.rem; - datap[idx.quot] |= val.byte[0]; // or in value - if ((idx.quot + 1) < bytes) { - datap[idx.quot + 1] = val.byte[1]; // this also resets - // all bits to 0 prior to or'ing above + for (int i = 0; i < len; ++i) { + char digit = valuep->value.str[len - i - 1] - '0'; + if (digit < 0 || digit > 7) { + VL_VPI_WARNING_(__FILE__, __LINE__, + "%s: Non octal character '%c' in '%s' as value %s for %s", + __func__, digit + '0', valuep->value.str, + VerilatedVpiError::strFromVpiVal(valuep->format), + vop->fullname()); + digit = 0; } + vl_vpi_put_word(vop, digit, 3, i * 3); } - // mask off non-existent bits in the most significant byte - if (idx.quot == (bytes - 1)) { - datap[idx.quot] &= vop->mask_byte(idx.quot); - } else if (idx.quot + 1 == (bytes - 1)) { - datap[idx.quot + 1] &= vop->mask_byte(idx.quot + 1); - } - // zero off remaining top bytes - for (int i = idx.quot + 2; i < bytes; ++i) datap[i] = 0; return object; } else if (valuep->format == vpiDecStrVal) { char remainder[16]; @@ -2716,23 +2786,10 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ remainder, valuep->value.str, VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname()); } - if (vop->varp()->vltype() == VLVT_UINT8) { - *(reinterpret_cast(vop->varDatap())) = val & vop->mask(); - return object; - } else if (vop->varp()->vltype() == VLVT_UINT16) { - *(reinterpret_cast(vop->varDatap())) = val & vop->mask(); - return object; - } else if (vop->varp()->vltype() == VLVT_UINT32) { - *(reinterpret_cast(vop->varDatap())) = val & vop->mask(); - return object; - } else if (vop->varp()->vltype() == VLVT_UINT64) { - *(reinterpret_cast(vop->varDatap())) = val; - (reinterpret_cast(vop->varDatap()))[1] &= vop->mask(); - return object; - } + vl_vpi_put_word(vop, val, 64, 0); + return object; } else if (valuep->format == vpiHexStrVal) { - const int chars = (vop->varp()->packed().elements() + 3) >> 2; - CData* const datap = (reinterpret_cast(vop->varDatap())); + const int chars = (varBits + 3) >> 2; const char* val = valuep->value.str; // skip hex ident if one is detected at the start of the string if (val[0] == '0' && (val[1] == 'x' || val[1] == 'X')) val += 2; @@ -2760,41 +2817,26 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ hex = 0; } // assign hex digit value to destination - if (i & 1) { - datap[i >> 1] |= hex << 4; - } else { - datap[i >> 1] = hex; // this also resets all - // bits to 0 prior to or'ing above of the msb - } + vl_vpi_put_word(vop, hex, 4, i * 4); } - // apply bit mask to most significant byte - datap[(chars - 1) >> 1] &= vop->mask_byte((chars - 1) >> 1); return object; } else if (valuep->format == vpiStringVal) { if (vop->varp()->vltype() == VLVT_STRING) { *(reinterpret_cast(vop->varDatap())) = valuep->value.str; return object; } else { - const int bytes = VL_BYTES_I(vop->varp()->packed().elements()); + const int chars = VL_BYTES_I(varBits); const int len = std::strlen(valuep->value.str); - CData* const datap = (reinterpret_cast(vop->varDatap())); - for (int i = 0; i < bytes; ++i) { + for (int i = 0; i < chars; ++i) { // prepend with 0 values before placing string the least significant bytes - datap[i] = (i < len) ? valuep->value.str[len - i - 1] : 0; + const char c = (i < len) ? valuep->value.str[len - i - 1] : 0; + vl_vpi_put_word(vop, c, 8, i * 8); } } return object; } else if (valuep->format == vpiIntVal) { - if (vop->varp()->vltype() == VLVT_UINT8) { - *(reinterpret_cast(vop->varDatap())) = vop->mask() & valuep->value.integer; - return object; - } else if (vop->varp()->vltype() == VLVT_UINT16) { - *(reinterpret_cast(vop->varDatap())) = vop->mask() & valuep->value.integer; - return object; - } else if (vop->varp()->vltype() == VLVT_UINT32) { - *(reinterpret_cast(vop->varDatap())) = vop->mask() & valuep->value.integer; - return object; - } + vl_vpi_put_word(vop, valuep->value.integer, 64, 0); + return object; } else if (valuep->format == vpiRealVal) { if (vop->varp()->vltype() == VLVT_REAL) { *(reinterpret_cast(vop->varDatap())) = valuep->value.real; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index b792de2b0..f8b4a6f9b 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -570,15 +570,28 @@ string AstVar::vlEnumDir() const { string AstVar::vlPropDecl(const string& propName) const { string out; + std::vector plims; // Packed dimension limits std::vector ulims; // Unpacked dimension limits - for (const AstNodeDType* dtp = dtypep(); dtp;) { - dtp = dtp->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node - if (const AstNodeArrayDType* const adtypep = VN_CAST(dtp, NodeArrayDType)) { - ulims.push_back(adtypep->declRange().left()); - ulims.push_back(adtypep->declRange().right()); - dtp = adtypep->subDTypep(); - } else { - break; // AstBasicDType - nothing below + + if (const AstBasicDType* const bdtypep = basicp()) { + for (const AstNodeDType* dtp = dtypep(); dtp;) { + dtp = dtp->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node + if (const AstNodeArrayDType* const adtypep = VN_CAST(dtp, NodeArrayDType)) { + if (VN_IS(dtp, PackArrayDType)) { + plims.push_back(adtypep->declRange().left()); + plims.push_back(adtypep->declRange().right()); + } else { + ulims.push_back(adtypep->declRange().left()); + ulims.push_back(adtypep->declRange().right()); + } + dtp = adtypep->subDTypep(); + } else { + if (bdtypep->isRanged()) { + plims.push_back(bdtypep->left()); + plims.push_back(bdtypep->right()); + } + break; // AstBasicDType - nothing below + } } } @@ -595,23 +608,37 @@ string AstVar::vlPropDecl(const string& propName) const { out += "};\n"; } + if (!plims.empty()) { + out += "static const int " + propName + "__plims["; + out += cvtToStr(plims.size()); + out += "] = {"; + auto it = plims.cbegin(); + out += cvtToStr(*it); + while (++it != plims.cend()) { + out += ", "; + out += cvtToStr(*it); + } + out += "};\n"; + } + out += "static const VerilatedVarProps "; out += propName; out += "("; out += vlEnumType(); // VLVT_UINT32 etc out += ", " + vlEnumDir(); // VLVD_IN etc - if (const AstBasicDType* const bdtypep = basicp()) { - out += ", VerilatedVarProps::Packed()"; - out += ", " + cvtToStr(bdtypep->left()); - out += ", " + cvtToStr(bdtypep->right()); - } if (!ulims.empty()) { - out += ", VerilatedVarProps::Unpacked()"; + out += ", VerilatedVarProps::Unpacked{}"; out += ", " + cvtToStr(ulims.size() / 2); out += ", " + propName + "__ulims"; } + if (!plims.empty()) { + out += ", VerilatedVarProps::Packed{}"; + out += ", " + cvtToStr(plims.size() / 2); + out += ", " + propName + "__plims"; + } + out += ");\n"; return out; } diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 47b890ae8..b0ea5f576 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -929,21 +929,11 @@ void EmitCSyms::emitSymImp() { checkSplit(true); AstScope* const scopep = it->second.m_scopep; AstVar* const varp = it->second.m_varp; - // - int pwidth = 1; int pdim = 0; int udim = 0; string bounds; if (AstBasicDType* const basicp = varp->basicp()) { // Range is always first, it's not in "C" order - if (basicp->isRanged()) { - bounds += " ,"; - bounds += cvtToStr(basicp->hi()); - bounds += ","; - bounds += cvtToStr(basicp->lo()); - pdim++; - pwidth *= basicp->elements(); - } for (AstNodeDType* dtypep = varp->dtypep(); dtypep;) { dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node @@ -952,28 +942,25 @@ void EmitCSyms::emitSymImp() { bounds += cvtToStr(adtypep->left()); bounds += ","; bounds += cvtToStr(adtypep->right()); - if (VN_IS(dtypep, PackArrayDType)) { + if (VN_IS(dtypep, PackArrayDType)) pdim++; - pwidth *= adtypep->elementsConst(); - } else { + else udim++; - } dtypep = adtypep->subDTypep(); } else { + if (basicp->isRanged()) { + bounds += " ,"; + bounds += cvtToStr(basicp->hi()); + bounds += ","; + bounds += cvtToStr(basicp->lo()); + pdim++; + } break; // AstBasicDType - nothing below, 1 } } } - // TODO: actually expose packed arrays as vpiRegArray - if (pdim > 1 && udim == 0) { - bounds = ", "; - bounds += cvtToStr(pwidth - 1); - bounds += ",0"; - pdim = 1; - } - if (pdim > 1 || udim > 1) { - puts("//UNSUP "); // VerilatedImp can't deal with >2d or packed arrays - } + + putns(scopep, protect("__Vscope_" + it->second.m_scopeName)); putns(varp, ".varInsert(__Vfinal,"); putsQuoted(protect(it->second.m_varBasePretty)); @@ -1005,7 +992,9 @@ void EmitCSyms::emitSymImp() { puts(","); puts(varp->vlEnumDir()); // VLVD_IN etc puts(","); - puts(cvtToStr(pdim + udim)); + puts(cvtToStr(udim)); + puts(","); + puts(cvtToStr(pdim)); puts(bounds); puts(");\n"); ++m_numStmts; diff --git a/test_regress/t/t_scope_map.cpp b/test_regress/t/t_scope_map.cpp index 93de549dc..df6e22e1a 100644 --- a/test_regress/t/t_scope_map.cpp +++ b/test_regress/t/t_scope_map.cpp @@ -50,8 +50,8 @@ int main(int argc, char** argv) { for (const auto& varname : *varNameMap) { const VerilatedVar* varp = &(varname.second); - int varLeft = varp->packed().left(); - int varRight = varp->packed().right(); + int varLeft = varp->range(0)->left(); + int varRight = varp->range(0)->right(); #ifdef TEST_VERBOSE VL_PRINTF("\tVar = %s\n", varname.first); @@ -125,7 +125,7 @@ int main(int argc, char** argv) { for (const auto& varname : *varNameMap) { const VerilatedVar* varp = &(varname.second); - int varLeft = varp->packed().left(); + int varLeft = varp->range(0)->left(); int varBits = varLeft + 1; uint8_t* varData = reinterpret_cast(varp->datap()); diff --git a/test_regress/t/t_vpi_dump.out b/test_regress/t/t_vpi_dump.out index a100566d1..bf2aa75bf 100644 --- a/test_regress/t/t_vpi_dump.out +++ b/test_regress/t/t_vpi_dump.out @@ -7,18 +7,18 @@ t (vpiModule) t count (vpiReg) t.count do_generate (vpiParameter) t.do_generate vpiConstType=vpiDecConst - fourthreetwoone (vpiMemory) t.fourthreetwoone - vpiMemoryWord: - fourthreetwoone (vpiMemoryWord) t.fourthreetwoone[3] - fourthreetwoone (vpiMemoryWord) t.fourthreetwoone[4] + fourthreetwoone (vpiRegArray) t.fourthreetwoone + vpiReg: + fourthreetwoone (vpiReg) t.fourthreetwoone[3] + fourthreetwoone (vpiReg) t.fourthreetwoone[4] half_count (vpiReg) t.half_count long_int (vpiParameter) t.long_int vpiConstType=vpiDecConst onebit (vpiReg) t.onebit - quads (vpiMemory) t.quads - vpiMemoryWord: - quads (vpiMemoryWord) t.quads[3] - quads (vpiMemoryWord) t.quads[2] + quads (vpiRegArray) t.quads + vpiReg: + quads (vpiReg) t.quads[3] + quads (vpiReg) t.quads[2] real1 (vpiRealVar) t.real1 status (vpiReg) t.status str1 (vpiStringVar) t.str1 diff --git a/test_regress/t/t_vpi_get.cpp b/test_regress/t/t_vpi_get.cpp index 021d761b6..8e2babd4f 100644 --- a/test_regress/t/t_vpi_get.cpp +++ b/test_regress/t/t_vpi_get.cpp @@ -49,7 +49,7 @@ static int _mon_check_props(TestVpiHandle& handle, int size, int direction, int CHECK_RESULT(vpisize, size); // icarus verilog does not support vpiScalar, vpiVector or vpi*Range - if (TestSimulator::has_get_scalar()) { + if (TestSimulator::has_get_scalar() && type != vpiParameter) { int vpiscalar = vpi_get(vpiScalar, handle); CHECK_RESULT((bool)vpiscalar, (bool)scalar); int vpivector = vpi_get(vpiVector, handle); @@ -57,23 +57,29 @@ static int _mon_check_props(TestVpiHandle& handle, int size, int direction, int } // Icarus only supports ranges on memories - if (!scalar && !(TestSimulator::is_icarus() && type != vpiMemory)) { - TestVpiHandle left_h, right_h; - + if (!scalar && type != vpiIntVar && type != vpiParameter + && !(TestSimulator::is_icarus() && type != vpiMemory)) { // check coherency for vectors - // get left hand side of range - left_h = vpi_handle(vpiLeftRange, handle); - CHECK_RESULT_NZ(left_h); - vpi_get_value(left_h, &value); - int coherency = value.value.integer; - // get right hand side of range - right_h = vpi_handle(vpiRightRange, handle); - CHECK_RESULT_NZ(right_h); - vpi_get_value(right_h, &value); - TEST_MSG("%d:%d\n", coherency, value.value.integer); - coherency -= value.value.integer; - // calculate size & check - coherency = abs(coherency) + 1; + + int coherency = 1; + TestVpiHandle iter_h = vpi_iterate(vpiRange, handle); + while (TestVpiHandle range_h = vpi_scan(iter_h)) { + int rangeSize; + TestVpiHandle left_h, right_h; + // get left hand side of range + left_h = vpi_handle(vpiLeftRange, range_h); + CHECK_RESULT_NZ(left_h); + vpi_get_value(left_h, &value); + rangeSize = value.value.integer; + // get right hand side of range + right_h = vpi_handle(vpiRightRange, range_h); + CHECK_RESULT_NZ(right_h); + vpi_get_value(right_h, &value); + rangeSize = abs(rangeSize - value.value.integer) + 1; + coherency *= rangeSize; + } + iter_h.freed(); + CHECK_RESULT(coherency, size); } @@ -83,12 +89,12 @@ static int _mon_check_props(TestVpiHandle& handle, int size, int direction, int int vpidir = vpi_get(vpiDirection, handle); // Don't check port directions in verilator // See issue #681 - if (!TestSimulator::is_verilator()) CHECK_RESULT(vpidir, direction); + if (!TestSimulator::is_verilator() && !TestSimulator::is_questa()) CHECK_RESULT(vpidir, direction); } // check type of object int vpitype = vpi_get(vpiType, handle); - if (!(TestSimulator::is_verilator() && type == vpiPort)) { + if (!TestSimulator::is_verilator() && !TestSimulator::is_questa() && type == vpiPort) { // Don't check for ports in verilator // See issue #681 CHECK_RESULT(vpitype, type); @@ -117,13 +123,11 @@ int mon_check_props() { static struct params values[] = {{"onebit", {1, vpiNoDirection, 1, vpiReg}, {0, 0, 0, 0}}, {"twoone", {2, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, - {"onetwo", - {2, vpiNoDirection, 0, TestSimulator::is_verilator() ? vpiReg : vpiMemory}, - {0, 0, 0, 0}}, + {"onetwo", {2, vpiNoDirection, 1, vpiRegArray}, {0, 0, 0, 0}}, {"fourthreetwoone", - {2, vpiNoDirection, 0, vpiMemory}, - {2, vpiNoDirection, 0, vpiMemoryWord}}, - {"theint", {32, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, + {2, vpiNoDirection, 0, vpiRegArray}, + {2, vpiNoDirection, 0, vpiReg}}, + {"theint", {32, vpiNoDirection, 0, TestSimulator::is_verilator() ? vpiReg : vpiIntVar}, {0, 0, 0, 0}}, {"clk", {1, vpiInput, 1, vpiPort}, {0, 0, 0, 0}}, {"testin", {16, vpiInput, 0, vpiPort}, {0, 0, 0, 0}}, {"testout", {24, vpiOutput, 0, vpiPort}, {0, 0, 0, 0}}, @@ -145,7 +149,7 @@ int mon_check_props() { return status; if (value->children.size) { int size = 0; - TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, h); + TestVpiHandle iter_h = vpi_iterate(vpiReg, h); while (TestVpiHandle word_h = vpi_scan(iter_h)) { // check size and range if (int status diff --git a/test_regress/t/t_vpi_memory.cpp b/test_regress/t/t_vpi_memory.cpp index e61cf737a..857ee2d90 100644 --- a/test_regress/t/t_vpi_memory.cpp +++ b/test_regress/t/t_vpi_memory.cpp @@ -52,25 +52,27 @@ void _mon_check_range(const TestVpiHandle& handle, int size, int left, int right int vpisize = vpi_get(vpiSize, handle); TEST_CHECK_EQ(vpisize, size); } - int coherency; + // check coherency + int coherency = 1; { - // check left hand side of range - TestVpiHandle left_h = vpi_handle(vpiLeftRange, handle); - TEST_CHECK_NZ(left_h); - vpi_get_value(left_h, &value); - TEST_CHECK_EQ(value.value.integer, left); - coherency = value.value.integer; + TestVpiHandle iter_h = vpi_iterate(vpiRange, handle); + while (TestVpiHandle range_h = vpi_scan(iter_h)) { + int rangeSize; + TestVpiHandle left_h, right_h; + // get left hand side of range + left_h = vpi_handle(vpiLeftRange, range_h); + TEST_CHECK_NZ(left_h); + vpi_get_value(left_h, &value); + rangeSize = value.value.integer; + // get right hand side of range + right_h = vpi_handle(vpiRightRange, range_h); + TEST_CHECK_NZ(right_h); + vpi_get_value(right_h, &value); + rangeSize = abs(rangeSize - value.value.integer) + 1; + coherency *= rangeSize; + } + iter_h.freed(); } - { - // check right hand side of range - TestVpiHandle right_h = vpi_handle(vpiRightRange, handle); - TEST_CHECK_NZ(right_h); - vpi_get_value(right_h, &value); - TEST_CHECK_EQ(value.value.integer, right); - coherency -= value.value.integer; - } - // calculate size & check - coherency = abs(coherency) + 1; TEST_CHECK_EQ(coherency, size); } @@ -83,8 +85,8 @@ void _mem_check(const char* name, int size, int left, int right, int words) { TEST_CHECK_NZ(mem_h); // check type int vpitype = vpi_get(vpiType, mem_h); - if (vpitype != vpiMemory && vpitype != vpiReg) { - printf("%%Error: %s:%d vpiType neither vpiMemory or vpiReg: %d\n", FILENM, __LINE__, + if (vpitype != vpiRegArray && vpitype != vpiReg) { + printf("%%Error: %s:%d vpiType neither vpiRegArray or vpiReg: %d\n", FILENM, __LINE__, vpitype); errors++; } @@ -96,9 +98,9 @@ void _mem_check(const char* name, int size, int left, int right, int words) { } } // iterate and store - if (vpitype == vpiMemory) { + if (vpitype == vpiRegArray) { _mon_check_range(mem_h, words, words, 1); - TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, mem_h); + TestVpiHandle iter_h = vpi_iterate(vpiReg, mem_h); int cnt = 0; while (TestVpiHandle lcl_h = vpi_scan(iter_h)) { value.format = vpiIntVal; @@ -112,15 +114,15 @@ void _mem_check(const char* name, int size, int left, int right, int words) { TEST_CHECK_EQ(cnt, words); // should be words addresses } else { int expSize = size * words; - _mon_check_range(mem_h, expSize, expSize - 1, 0); + _mon_check_range(mem_h, expSize, words, 1); value.format = vpiBinStrVal; value.value.str = const_cast(binStr.c_str()); vpi_put_value(mem_h, &value, NULL, vpiNoDelay); TEST_CHECK_Z(vpi_chk_error(&e)); } - if (vpitype == vpiMemory) { + if (vpitype == vpiRegArray) { // iterate and accumulate - TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, mem_h); + TestVpiHandle iter_h = vpi_iterate(vpiReg, mem_h); int cnt = 0; while (TestVpiHandle lcl_h = vpi_scan(iter_h)) { ++cnt; @@ -148,7 +150,7 @@ void _mem_check(const char* name, int size, int left, int right, int words) { { // make sure trying to get properties that don't exist // doesn't crash - TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, mem_h); + TestVpiHandle iter_h = vpi_iterate(vpiReg, mem_h); int should_be_undefined = vpi_get(vpiSize, iter_h); TEST_CHECK_EQ(should_be_undefined, vpiUndefined); should_be_undefined = vpi_get(vpiIndex, iter_h); @@ -160,7 +162,7 @@ void _mem_check(const char* name, int size, int left, int right, int words) { should_be_NULL = vpi_handle(vpiScope, iter_h); TEST_CHECK_EQ(should_be_NULL, 0); } - if (vpitype == vpiMemory) { + if (vpitype == vpiRegArray) { // check vpiRange TestVpiHandle iter_h = vpi_iterate(vpiRange, mem_h); TEST_CHECK_NZ(iter_h); diff --git a/test_regress/t/t_vpi_multidim.cpp b/test_regress/t/t_vpi_multidim.cpp new file mode 100644 index 000000000..f66c28dec --- /dev/null +++ b/test_regress/t/t_vpi_multidim.cpp @@ -0,0 +1,462 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// 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 +// +//************************************************************************* + +#ifdef IS_VPI + +#include "vpi_user.h" + +#include + +#else + +#include "verilated.h" +#include "verilated_vcd_c.h" +#include "verilated_vpi.h" + +#include "Vt_vpi_multidim.h" +#include "Vt_vpi_multidim__Dpi.h" +#include "svdpi.h" + +#endif + +#include +#include +#include +#include +#include + +// These require the above. Comment prevents clang-format moving them +#include "TestCheck.h" +#include "TestSimulator.h" +#include "TestVpi.h" + +int errors = 0; + +// TEST START + +void _arr_type_check(TestVpiHandle& arr_h, int expType, int expSize, int expRangeHigh, int expRangeLow) +{ + const int vpitype = vpi_get(vpiType, arr_h); + TEST_CHECK_EQ(vpitype, expType); + const int vpisize = vpi_get(vpiSize, arr_h); + TEST_CHECK_EQ(vpisize, expSize); + + s_vpi_value value; + value.format = vpiIntVal; + + TestVpiHandle left_h = vpi_handle(vpiLeftRange, arr_h); + TEST_CHECK_NZ(left_h); + vpi_get_value(left_h, &value); + TEST_CHECK_EQ(value.value.integer, expRangeHigh); + + TestVpiHandle right_h = vpi_handle(vpiRightRange, arr_h); + TEST_CHECK_NZ(right_h); + vpi_get_value(right_h, &value); + TEST_CHECK_EQ(value.value.integer, expRangeLow); +} + +void _arr_iter_check(const char* name, int wordSize, const int* lows) { + TestVpiHandle arr_h = vpi_handle_by_name(const_cast(TestSimulator::rooted(name)), NULL); + TEST_CHECK_NZ(arr_h); + + _arr_type_check(arr_h, vpiRegArray, 4, lows[0] + 1, lows[0]); + + { + // can't iterate through RegArrays on a nested RegArray + TestVpiHandle arr_iter_h = vpi_iterate(vpiRegArray, arr_h); + TEST_CHECK_Z(vpi_scan(arr_iter_h)); + arr_iter_h.freed(); + } + + if (!TestSimulator::is_questa()) { + // but we can access them by index (Questa can't) + for (int idx = lows[0]; idx < lows[0] + 2; idx++) { + TestVpiHandle arr_elem_h = vpi_handle_by_index(arr_h, idx); + TEST_CHECK_NZ(arr_elem_h); + + // first indexing yields size-2 RegArrays + _arr_type_check(arr_elem_h, vpiRegArray, 2, lows[1] + 1, lows[1]); + + for (int idx2 = lows[1]; idx2 < lows[1] + 2; idx2++) { + TestVpiHandle arr_elem2_h = vpi_handle_by_index(arr_elem_h, idx2); + TEST_CHECK_NZ(arr_elem2_h); + + // second indexing yields wordSize Regs + _arr_type_check(arr_elem2_h, vpiReg, wordSize, lows[2] + 1, lows[2]); + } + } + } + + { + // it's also possible to directly iterate through all four Regs + TestVpiHandle arr_iter_h = vpi_iterate(vpiReg, arr_h); + for (int idx = 0; idx < 4; idx++) { + TestVpiHandle arr_elem_h = vpi_scan(arr_iter_h); + TEST_CHECK_NZ(arr_elem_h); + + // which gives us wordSize Regs + _arr_type_check(arr_elem_h, vpiReg, wordSize, lows[2] + 1, lows[2]); + + { + // can't iterate through Regs on a nested Reg + TestVpiHandle arr_iter2_h = vpi_iterate(vpiReg, arr_elem_h); + TEST_CHECK_Z(vpi_scan(arr_iter2_h)); + arr_iter2_h.freed(); + } + + // but we can access them by index + for (int idx2 = lows[2]; idx2 < lows[2] + 2; idx2++) { + TestVpiHandle arr_elem2_h = vpi_handle_by_index(arr_elem_h, idx2); + TEST_CHECK_NZ(arr_elem2_h); + + // first indexing yields wordSize / 2 Regs + _arr_type_check(arr_elem2_h, vpiReg, wordSize / 2, lows[3] + wordSize / 2 - 1, lows[3]); + + for (int idx3 = lows[3]; idx3 < lows[3] + wordSize / 2; idx3++) { + TestVpiHandle arr_elem3_h = vpi_handle_by_index(arr_elem2_h, idx3); + TEST_CHECK_NZ(arr_elem3_h); + { + // second indexing yields size-1 RegBits (no support for RegBit VPI type yet) + const int vpitype = vpi_get(vpiType, arr_elem3_h); + if (TestSimulator::is_verilator()) { + TEST_CHECK_EQ(vpitype, vpiReg); + } else { + TEST_CHECK_EQ(vpitype, vpiRegBit); + } + const int vpisize = vpi_get(vpiSize, arr_elem3_h); + TEST_CHECK_EQ(vpisize, 1); + } + } + } + + // iterating through packed ranges + TestVpiHandle range_iter_h = vpi_iterate(vpiRange, arr_elem_h); + for (int idx2 = 0; idx2 < 2; idx2++) { + TestVpiHandle range_h = vpi_scan(range_iter_h); + TEST_CHECK_NZ(range_h); + { + s_vpi_value value; + value.format = vpiIntVal; + TestVpiHandle side_h = vpi_handle(vpiLeftRange, range_h); + TEST_CHECK_NZ(side_h); + vpi_get_value(side_h, &value); + if (idx2 == 0) { + TEST_CHECK_EQ(value.value.integer, lows[2] + 1); + } else { + TEST_CHECK_EQ(value.value.integer, lows[3] + wordSize / 2 - 1); + } + side_h = vpi_handle(vpiRightRange, range_h); + TEST_CHECK_NZ(side_h); + vpi_get_value(side_h, &value); + if (idx2 == 0) { + TEST_CHECK_EQ(value.value.integer, lows[2]); + } else { + TEST_CHECK_EQ(value.value.integer, lows[3]); + } + } + } + TEST_CHECK_Z(vpi_scan(range_iter_h)); + range_iter_h.freed(); + } + TEST_CHECK_Z(vpi_scan(arr_iter_h)); + arr_iter_h.freed(); + } + + { + // iterating through unpacked ranges + TestVpiHandle range_iter_h = vpi_iterate(vpiRange, arr_h); + for (int idx = 0; idx < 2; idx++) { + TestVpiHandle range_h = vpi_scan(range_iter_h); + TEST_CHECK_NZ(range_h); + { + s_vpi_value value; + value.format = vpiIntVal; + TestVpiHandle side_h = vpi_handle(vpiLeftRange, range_h); + TEST_CHECK_NZ(side_h); + vpi_get_value(side_h, &value); + if (idx == 0) { + TEST_CHECK_EQ(value.value.integer, lows[0] + 1); + } else { + TEST_CHECK_EQ(value.value.integer, lows[1] + 1); + } + side_h = vpi_handle(vpiRightRange, range_h); + TEST_CHECK_NZ(side_h); + vpi_get_value(side_h, &value); + if (idx == 0) { + TEST_CHECK_EQ(value.value.integer, lows[0]); + } else { + TEST_CHECK_EQ(value.value.integer, lows[1]); + } + } + } + TEST_CHECK_Z(vpi_scan(range_iter_h)); + range_iter_h.freed(); + } +} + +void _arr_access_format_check(TestVpiHandle ®_h, int wordSize, const int* lows, const char *octVal_s, PLI_INT32 format) +{ + const int spanSize = wordSize / 2; + s_vpi_value value_in; + s_vpi_value value_out; + s_vpi_error_info e; + char zero_s[2] = "0"; + + // zero out the vector + value_in.format = vpiOctStrVal; + value_in.value.str = zero_s; + vpi_put_value(reg_h, &value_in, NULL, vpiNoDelay); + TEST_CHECK_Z(vpi_chk_error(&e)); + + value_in.format = format; + value_out.format = format; + + for (int i = 0; i < 2; i++) { + TestVpiHandle subreg_h = vpi_handle_by_index(reg_h, lows[2] + i); + TEST_CHECK_NZ(subreg_h); + + char octSpan_s[spanSize / 3 + 1]; + strncpy(octSpan_s, &octVal_s[spanSize / 3 * (1 - i)], spanSize / 3); + octSpan_s[spanSize / 3] = '\0'; + + uint64_t intVal; + t_vpi_vecval vecVal[2]; + sscanf(octSpan_s, "%" SCNo64, &intVal); + char strVal_s[spanSize + 1]; // max length of the string happens for binary + + if (format == vpiIntVal) { + value_in.value.integer = intVal; + } else if (format == vpiVectorVal) { + if (spanSize > 32) { + vecVal[1].aval = intVal >> 32; + vecVal[1].bval = 0; + } + vecVal[0].aval = intVal; + vecVal[0].bval = 0; + value_in.value.vector = vecVal; + } else if (format == vpiBinStrVal) { + for (int j = 0; j < spanSize; j++) + strVal_s[j] = (intVal >> (spanSize - j - 1)) % 2 + '0'; + strVal_s[spanSize] = '\0'; + value_in.value.str = strVal_s; + } else if (format == vpiDecStrVal) { + sprintf(strVal_s, "%" PRIu64, intVal); + value_in.value.str = strVal_s; + } else if (format == vpiHexStrVal) { + sprintf(strVal_s, "%0*" PRIx64, (spanSize + 3) / 4, intVal); + value_in.value.str = strVal_s; + } else if (format == vpiOctStrVal) { + sprintf(strVal_s, "%" PRIo64, intVal); + value_in.value.str = strVal_s; + } else if (format == vpiStringVal) { + const int byteCount = (spanSize + 7) / 8; + for (int j = 0; j < byteCount; j++) + strVal_s[j] = (intVal >> (8 * (byteCount - j - 1))) & 0xff; + strVal_s[byteCount] = '\0'; + value_in.value.str = strVal_s; + } + + vpi_put_value(subreg_h, &value_in, NULL, vpiNoDelay); + TEST_CHECK_Z(vpi_chk_error(&e)); + + vpi_get_value(subreg_h, &value_out); + switch (format) { + case vpiIntVal: + TEST_CHECK_EQ(value_out.value.integer, value_in.value.integer); + break; + case vpiVectorVal: + if (spanSize > 32) + TEST_CHECK_EQ(value_out.value.vector[1].aval, value_in.value.vector[1].aval); + TEST_CHECK_EQ(value_out.value.vector[0].aval, value_in.value.vector[0].aval); + break; + case vpiStringVal: + TEST_CHECK_EQ(value_out.value.str[0], value_in.value.str[0] ? value_in.value.str[0] : ' '); + break; + case vpiBinStrVal: + case vpiDecStrVal: + case vpiHexStrVal: + case vpiOctStrVal: + TEST_CHECK_CSTR(value_out.value.str, value_in.value.str); + break; + } + } + + // validate the resulting flattened vector + value_out.format = vpiOctStrVal; + vpi_get_value(reg_h, &value_out); + TEST_CHECK_CSTR(value_out.value.str, octVal_s); +} + +std::default_random_engine rng; + +void _arr_access_check(const char* name, int wordSize, const int* lows) { + TestVpiHandle arr_h = vpi_handle_by_name(const_cast(TestSimulator::rooted(name)), NULL); + TEST_CHECK_NZ(arr_h); + + std::uniform_int_distribution rand64( + std::numeric_limits::min(), + std::numeric_limits::max() + ); + + char octVal_s[wordSize / 3 + 1]; + + // fill octVal_s with random octal digits + if (wordSize < 64) { + sprintf(octVal_s, "%0*" PRIo64, wordSize / 3, rand64(rng) % (static_cast(1) << wordSize)); + } else { + sprintf(octVal_s, "%0*" PRIo64, 63 / 3, rand64(rng)); + sprintf(octVal_s + 63 / 3, "%0*" PRIo64, (wordSize - 63) / 3, rand64(rng) % (static_cast(1) << (wordSize - 63))); + } + + // Assume that reading/writing to the "flattened" packed register is already tested, + // check only reading/writing to sub-regs and validate the flattened result. + { + TestVpiHandle arr_iter_h = vpi_iterate(vpiReg, arr_h); + while (TestVpiHandle reg_h = vpi_scan(arr_iter_h)) { + s_vpi_value value_in; + s_vpi_value value_out; + s_vpi_error_info e; + + value_out.format = vpiOctStrVal; + value_in.format = vpiOctStrVal; + value_in.value.str = octVal_s; + vpi_put_value(reg_h, &value_in, NULL, vpiNoDelay); + TEST_CHECK_Z(vpi_chk_error(&e)); + vpi_get_value(reg_h, &value_out); + TEST_CHECK_CSTR(value_out.value.str, octVal_s); + + // test each I/O data format + if (wordSize <= 64) { + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiIntVal); + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiDecStrVal); + } + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiVectorVal); + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiBinStrVal); + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiOctStrVal); + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiHexStrVal); + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiStringVal); + } + arr_iter_h.freed(); + } +} + +struct params { + const char* name; + int wordSize; + const int lows[4]; +}; + +void _multidim_check() { + static struct params values[] = { + {"arr_cdata", 6, {0, 1, 2, 3}}, + {"arr_sdata", 12, {4, 5, 6, 7}}, + {"arr_idata", 30, {8, 9, 10, 11}}, + {"arr_qdata", 60, {12, 13, 14, 15}}, + {"arr_wdata", 126, {16, 17, 18, 19}}, + {nullptr, 0, {0, 0, 0, 0}} + }; + struct params* value = values; + while (value->name) { + _arr_iter_check(value->name, value->wordSize, value->lows); + _arr_access_check(value->name, value->wordSize, value->lows); + value++; + } +} + +// TEST END + +extern "C" int mon_check() { + // Callback from initial block in monitor + //if (int status = _mon_check_param()) return status; + printf("-mon_check()\n"); + _multidim_check(); + return errors; +} + +#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}; + +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++); +} + +void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; + +#else + +int main(int argc, char** argv) { + const std::unique_ptr contextp{new VerilatedContext}; + + uint64_t sim_time = 1100; // TODO + contextp->debug(0); + contextp->commandArgs(argc, argv); + + const std::unique_ptr topp{new VM_PREFIX{contextp.get(), + // Note null name - we're flattening it out + ""}}; + +#ifdef VERILATOR +#ifdef TEST_VERBOSE + contextp->scopesDump(); +#endif +#endif + +#if VM_TRACE + contextp->traceEverOn(true); + VL_PRINTF("Enabling waves...\n"); + VerilatedVcdC* tfp = new VerilatedVcdC; + topp->trace(tfp, 99); + tfp->open(STRINGIFY(TEST_OBJ_DIR) "/simx.vcd"); +#endif + + topp->eval(); + topp->clk = 0; + contextp->timeInc(10); + + while (contextp->time() < sim_time && !contextp->gotFinish()) { + contextp->timeInc(1); + topp->eval(); + VerilatedVpi::callValueCbs(); + topp->clk = !topp->clk; + // mon_do(); +#if VM_TRACE + if (tfp) tfp->dump(main_time); +#endif + } + if (!contextp->gotFinish()) { + vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish"); + } + topp->final(); + +#if VM_TRACE + if (tfp) tfp->close(); +#endif + + return 0; +} + +#endif diff --git a/test_regress/t/t_vpi_multidim.py b/test_regress/t/t_vpi_multidim.py new file mode 100755 index 000000000..ab0961ef9 --- /dev/null +++ b/test_regress/t/t_vpi_multidim.py @@ -0,0 +1,22 @@ +#!/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, + v_flags2=["+define+USE_VPI_NOT_DPI"], + verilator_flags2=["--exe --vpi --no-l2name --public-flat-rw", test.pli_filename]) + +test.execute(use_libvpi=True) + +test.passes() diff --git a/test_regress/t/t_vpi_multidim.v b/test_regress/t/t_vpi_multidim.v new file mode 100644 index 000000000..2cdd39c9b --- /dev/null +++ b/test_regress/t/t_vpi_multidim.v @@ -0,0 +1,44 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// 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 + +module t (/*AUTOARG*/ + // Inputs + clk + ); /*verilator public_module*/ + +`ifdef VERILATOR +`systemc_header +extern "C" int mon_check(); +`verilog +`endif + + input clk; + + logic [3:2][5:3] arr_cdata [1:0][2:1]; // 2x3 (6) bit words + logic [7:6][12:7] arr_sdata [5:4][6:5]; // 2x6 (12) bit words + logic [11:10][25:11] arr_idata [9:8][10:9]; // 2x15 (30) bit words + logic [15:14][44:15] arr_qdata [13:12][14:13]; // 2x30 (60) bit words + logic [19:18][81:19] arr_wdata [17:16][18:17]; // 2x63 (126) bit words + + int status; + + initial begin +`ifdef VERILATOR + status = $c32("mon_check()"); +`else + status = $mon_check(); +`endif + if (status!=0) begin + $write("%%Error: t_vpi_multidim.cpp:%0d: C Test failed\n", status); + $stop; + end + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule : t diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index 99ab60f9f..2cb16563f 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -297,7 +297,7 @@ int _mon_check_var() { d = vpi_get(vpiVector, vh4); CHECK_RESULT(d, 1); p = vpi_get_str(vpiType, vh4); - CHECK_RESULT_CSTR(p, "vpiMemory"); + CHECK_RESULT_CSTR(p, "vpiRegArray"); } t_vpi_value tmpValue; @@ -320,14 +320,14 @@ int _mon_check_var() { CHECK_RESULT_CSTR(p, "vpiConstant"); } { - TestVpiHandle vh10 = vpi_iterate(vpiMemoryWord, vh4); + TestVpiHandle vh10 = vpi_iterate(vpiReg, vh4); CHECK_RESULT_NZ(vh10); p = vpi_get_str(vpiType, vh10); CHECK_RESULT_CSTR(p, "vpiIterator"); TestVpiHandle vh11 = vpi_scan(vh10); CHECK_RESULT_NZ(vh11); p = vpi_get_str(vpiType, vh11); - CHECK_RESULT_CSTR(p, "vpiMemoryWord"); + CHECK_RESULT_CSTR(p, "vpiReg"); TestVpiHandle vh12 = vpi_handle(vpiLeftRange, vh11); CHECK_RESULT_NZ(vh12); vpi_get_value(vh12, &tmpValue); @@ -643,15 +643,15 @@ int _mon_check_quad() { TestVpiHandle vhidx3 = vpi_handle_by_index(vh2, 3); CHECK_RESULT_NZ(vhidx3); - // Memory words should not be indexable + // Packed words should be indexable TestVpiHandle vhidx3idx0 = vpi_handle_by_index(vhidx3, 0); - CHECK_RESULT(vhidx3idx0, 0); + CHECK_RESULT_NZ(vhidx3idx0); TestVpiHandle vhidx2idx2 = vpi_handle_by_index(vhidx2, 2); - CHECK_RESULT(vhidx2idx2, 0); + CHECK_RESULT_NZ(vhidx2idx2); TestVpiHandle vhidx3idx3 = vpi_handle_by_index(vhidx3, 3); - CHECK_RESULT(vhidx3idx3, 0); + CHECK_RESULT_NZ(vhidx3idx3); TestVpiHandle vhidx2idx61 = vpi_handle_by_index(vhidx2, 61); - CHECK_RESULT(vhidx2idx61, 0); + CHECK_RESULT_NZ(vhidx2idx61); v.format = vpiVectorVal; v.value.vector = vv;