Support multidimensional array access via VPI (#2812) (#5573)

This commit is contained in:
Krzysztof Starecki 2025-01-10 01:04:26 +01:00 committed by GitHub
parent 052812bb87
commit 69dce205d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 1101 additions and 493 deletions

View File

@ -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<uint8_t*>(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<VerilatedVarFlags>(vlflags), dims, isParam);
VerilatedVar var(namep, datap, vltype, static_cast<VerilatedVarFlags>(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);

View File

@ -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; }

View File

@ -289,7 +289,7 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s,
}
case VLVT_WDATA: {
WDataOutP wdatap = (reinterpret_cast<WDataOutP>(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<WDataOutP>(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<QData*>(datap)) = VL_SET_QII(s[1], s[0]); break;
case VLVT_WDATA: {
WDataOutP wdatap = (reinterpret_cast<WDataOutP>(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<QData*>(datap)) = VL_SET_QII(s[1].aval, s[0].aval); break;
case VLVT_WDATA: {
WDataOutP wdatap = (reinterpret_cast<WDataOutP>(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

View File

@ -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<VerilatedRange> m_unpacked; // Unpacked array ranges
void initUnpacked(const int* ulims) {
for (int i = 0; i < m_udims; ++i) {
std::vector<VerilatedRange> 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<VerilatedVarFlags>(static_cast<int>(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<VerilatedRange>& packedRanges() const VL_MT_SAFE { return m_packed; }
const std::vector<VerilatedRange>& 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; }
};

View File

@ -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<VerilatedRange> m_ranges;
std::vector<VerilatedRange>::const_iterator m_iter;
public:
explicit VerilatedVpioRangeIter(const VerilatedRange* rangep)
: m_rangep{rangep} {}
explicit VerilatedVpioRangeIter(const std::vector<VerilatedRange>& ranges)
: m_ranges{ranges} {
m_iter = m_ranges.begin();
}
~VerilatedVpioRangeIter() override = default;
static VerilatedVpioRangeIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioRangeIter*>(reinterpret_cast<VerilatedVpio*>(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<int32_t> 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<VerilatedVpioVar*>(reinterpret_cast<VerilatedVpio*>(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<int32_t>& 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<uint8_t*>(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<uint8_t*>(varp->datap())) + entSize() * offset;
}
~VerilatedVpioMemoryWord() override = default;
static VerilatedVpioMemoryWord* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioMemoryWord*>(reinterpret_cast<VerilatedVpio*>(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<VerilatedRange> m_ranges;
std::vector<int> 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<VerilatedVpioMemoryWordIter*>(reinterpret_cast<VerilatedVpio*>(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<VerilatedVpioRegIter*>(reinterpret_cast<VerilatedVpio*>(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<VerilatedRange> 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<typename T>
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<typename T>
VarAccessInfo<T> 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<T> info;
info.m_datap = reinterpret_cast<T*>(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<T>(0);
else
info.m_maskLo = (static_cast<T>(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<T>(1) << (wordBits - info.m_bitOffset)) - 1;
info.m_maskLo = info.m_maskLo << info.m_bitOffset;
info.m_maskHi = (static_cast<T>(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<T>(1) << bitCount) - 1;
info.m_maskLo = info.m_maskLo << info.m_bitOffset;
} else {
info.m_maskLo = ~static_cast<T>(0);
}
info.m_maskHi = 0;
}
return info;
}
template<typename T>
T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
const size_t wordBits = sizeof(T) * 8;
const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(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<typename T>
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<T> info = vl_vpi_var_access_info<T>(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<CData>(vop, bitCount, addOffset);
case VLVT_UINT16: return vl_vpi_get_word_gen<SData>(vop, bitCount, addOffset);
case VLVT_UINT32: return vl_vpi_get_word_gen<IData>(vop, bitCount, addOffset);
case VLVT_UINT64: return vl_vpi_get_word_gen<QData>(vop, bitCount, addOffset);
case VLVT_WDATA: return vl_vpi_get_word_gen<EData>(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<CData>(vop, word, bitCount, addOffset); break;
case VLVT_UINT16: vl_vpi_put_word_gen<SData>(vop, word, bitCount, addOffset); break;
case VLVT_UINT32: vl_vpi_put_word_gen<IData>(vop, word, bitCount, addOffset); break;
case VLVT_UINT64: vl_vpi_put_word_gen<QData>(vop, word, bitCount, addOffset); break;
case VLVT_WDATA: vl_vpi_put_word_gen<EData>(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<CData*>(varDatap));
t_out[0].bval = 0;
return;
} else if (varp->vltype() == VLVT_UINT16) {
t_out[0].aval = *(reinterpret_cast<SData*>(varDatap));
t_out[0].bval = 0;
return;
} else if (varp->vltype() == VLVT_UINT32) {
t_out[0].aval = *(reinterpret_cast<IData*>(varDatap));
t_out[0].bval = 0;
return;
} else if (varp->vltype() == VLVT_UINT64) {
const QData data = *(reinterpret_cast<QData*>(varDatap));
t_out[1].aval = static_cast<IData>(data >> 32ULL);
t_out[1].bval = 0;
t_out[0].aval = static_cast<IData>(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<EData*>(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<IData>(data >> 32ULL);
t_out[1].bval = 0;
t_out[0].aval = static_cast<IData>(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<CData*>(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<PLI_BYTE8*>(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<CData*>(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<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
} else if (valuep->format == vpiDecStrVal) {
if (varp->vltype() == VLVT_UINT8) {
vl_strprintf(t_outDynamicStr, "%hhu",
static_cast<unsigned char>(*(reinterpret_cast<CData*>(varDatap))));
static_cast<unsigned char>(vl_vpi_get_word(vop, 8, 0)));
} else if (varp->vltype() == VLVT_UINT16) {
vl_strprintf(t_outDynamicStr, "%hu",
static_cast<unsigned short>(*(reinterpret_cast<SData*>(varDatap))));
static_cast<unsigned short>(vl_vpi_get_word(vop, 16, 0)));
} else if (varp->vltype() == VLVT_UINT32) {
vl_strprintf(t_outDynamicStr, "%u",
static_cast<unsigned int>(*(reinterpret_cast<IData*>(varDatap))));
static_cast<unsigned int>(vl_vpi_get_word(vop, 32, 0)));
} else if (varp->vltype() == VLVT_UINT64) {
vl_strprintf(t_outDynamicStr, "%llu", // lintok-format-ll
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(varDatap))));
static_cast<unsigned long long>(vl_vpi_get_word(vop, 64, 0)));
}
valuep->value.str = const_cast<PLI_BYTE8*>(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<CData*>(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<int>(val)];
}
valuep->value.str = const_cast<PLI_BYTE8*>(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<CData*>(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<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
}
} else if (valuep->format == vpiIntVal) {
if (varp->vltype() == VLVT_UINT8) {
valuep->value.integer = *(reinterpret_cast<CData*>(varDatap));
return;
} else if (varp->vltype() == VLVT_UINT16) {
valuep->value.integer = *(reinterpret_cast<SData*>(varDatap));
return;
} else if (varp->vltype() == VLVT_UINT32) {
valuep->value.integer = *(reinterpret_cast<IData*>(varDatap));
return;
}
valuep->value.integer = vl_vpi_get_word(vop, 32, 0);
return;
} else if (valuep->format == vpiRealVal) {
valuep->value.real = *(reinterpret_cast<double*>(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<CData*>(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<SData*>(vop->varDatap()))
= valuep->value.vector[0].aval & vop->mask();
} else if (vop->varp()->vltype() == VLVT_UINT64 && varBits > 32) {
const QData val = (static_cast<QData>(valuep->value.vector[1].aval) << 32) |
static_cast<QData>(valuep->value.vector[0].aval);
vl_vpi_put_word(vop, val, 64, 0);
return object;
} else if (vop->varp()->vltype() == VLVT_UINT32) {
*(reinterpret_cast<IData*>(vop->varDatap()))
= valuep->value.vector[0].aval & vop->mask();
return object;
} else if (vop->varp()->vltype() == VLVT_UINT64) {
*(reinterpret_cast<QData*>(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<EData*>(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<CData*>(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<CData*>(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<CData*>(vop->varDatap())) = val & vop->mask();
return object;
} else if (vop->varp()->vltype() == VLVT_UINT16) {
*(reinterpret_cast<SData*>(vop->varDatap())) = val & vop->mask();
return object;
} else if (vop->varp()->vltype() == VLVT_UINT32) {
*(reinterpret_cast<IData*>(vop->varDatap())) = val & vop->mask();
return object;
} else if (vop->varp()->vltype() == VLVT_UINT64) {
*(reinterpret_cast<QData*>(vop->varDatap())) = val;
(reinterpret_cast<IData*>(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<CData*>(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<std::string*>(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<CData*>(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<CData*>(vop->varDatap())) = vop->mask() & valuep->value.integer;
return object;
} else if (vop->varp()->vltype() == VLVT_UINT16) {
*(reinterpret_cast<SData*>(vop->varDatap())) = vop->mask() & valuep->value.integer;
return object;
} else if (vop->varp()->vltype() == VLVT_UINT32) {
*(reinterpret_cast<IData*>(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<double*>(vop->varDatap())) = valuep->value.real;

View File

@ -570,15 +570,28 @@ string AstVar::vlEnumDir() const {
string AstVar::vlPropDecl(const string& propName) const {
string out;
std::vector<int> plims; // Packed dimension limits
std::vector<int> 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;
}

View File

@ -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;

View File

@ -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<uint8_t*>(varp->datap());

View File

@ -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

View File

@ -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

View File

@ -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<char*>(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);

View File

@ -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 <cstdlib>
#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 <cstdio>
#include <cstring>
#include <iostream>
#include <random>
#include <cstdlib>
// 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<PLI_BYTE8*>(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 &reg_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<PLI_BYTE8*>(TestSimulator::rooted(name)), NULL);
TEST_CHECK_NZ(arr_h);
std::uniform_int_distribution<uint64_t> rand64(
std::numeric_limits<uint64_t>::min(),
std::numeric_limits<uint64_t>::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<uint64_t>(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<uint64_t>(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<VerilatedContext> contextp{new VerilatedContext};
uint64_t sim_time = 1100; // TODO
contextp->debug(0);
contextp->commandArgs(argc, argv);
const std::unique_ptr<VM_PREFIX> 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

View File

@ -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()

View File

@ -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

View File

@ -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;