Support VPI access to unpacked struct members (#7823)

This commit is contained in:
Nick Brereton 2026-06-23 07:04:51 -04:00 committed by GitHub
parent c76c94ef16
commit 6fbc7042a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 1267 additions and 62 deletions

View File

@ -3957,6 +3957,7 @@ std::unique_ptr<VerilatedTraceConfig> VerilatedModel::traceConfig() const { retu
// cppcheck-suppress unusedFunction // Used by applications
uint32_t VerilatedVarProps::entSize() const VL_MT_SAFE {
if (m_entSize) return m_entSize;
uint32_t size = 1;
switch (vltype()) {
case VLVT_PTR: size = sizeof(void*); break;
@ -4073,6 +4074,27 @@ VerilatedVar* VerilatedScope::varInsert(const char* namep, void* datap, bool isP
return &(m_varsp->find(namep)->second);
}
VerilatedVar* VerilatedScope::varInsertSized(const char* namep, void* datap, bool isParam,
VerilatedVarType vltype, int vlflags, int udims,
uint32_t entSize...) VL_MT_UNSAFE {
if (!m_varsp) m_varsp = new VerilatedVarNameMap;
VerilatedVar var(namep, datap, vltype, static_cast<VerilatedVarFlags>(vlflags), udims, 0,
isParam, entSize);
va_list ap;
va_start(ap, entSize);
for (int i = 0; i < udims; ++i) {
const int msb = va_arg(ap, int);
const int lsb = va_arg(ap, int);
var.m_unpacked[i].m_left = msb;
var.m_unpacked[i].m_right = lsb;
}
va_end(ap);
m_varsp->emplace(namep, std::move(var));
return &(m_varsp->find(namep)->second);
}
VerilatedVar*
VerilatedScope::forceableVarInsert(const char* namep, void* datap, bool isParam,
VerilatedVarType vltype, int vlflags, void* forceReadSignalData,

View File

@ -137,7 +137,9 @@ enum VerilatedVarType : uint8_t {
VLVT_UINT64, // AKA QData
VLVT_WDATA, // AKA VlWide
VLVT_STRING, // C++ string
VLVT_REAL // AKA double
VLVT_REAL, // AKA double
VLVT_STRUCT, // SystemVerilog unpacked struct
VLVT_UNION // SystemVerilog unpacked union
};
enum VerilatedVarFlags : uint32_t {
@ -154,7 +156,8 @@ enum VerilatedVarFlags : uint32_t {
VLVF_CONTINUOUSLY = (1 << 11), // Is continously assigned
VLVF_FORCEABLE = (1 << 12), // Forceable
VLVF_SIGNED = (1 << 13), // Signed integer
VLVF_BITVAR = (1 << 14) // Four state bit (vs two state logic)
VLVF_BITVAR = (1 << 14), // Four state bit (vs two state logic)
VLVF_NET = (1 << 15) // Net object
};
// IEEE 1800-2023 Table 20-6
@ -781,6 +784,9 @@ public: // But internals only - called from verilated modules, VerilatedSyms
void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE;
VerilatedVar* varInsert(const char* namep, void* datap, bool isParam, VerilatedVarType vltype,
int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE;
VerilatedVar* varInsertSized(const char* namep, void* datap, bool isParam,
VerilatedVarType vltype, int vlflags, int udims, uint32_t entSize,
...) VL_MT_UNSAFE;
VerilatedVar* forceableVarInsert(const char* namep, void* datap, bool isParam,
VerilatedVarType vltype, int vlflags,
void* forceReadSignalData, const char* forceReadSignalName,

View File

@ -76,6 +76,7 @@ class VerilatedVarProps VL_NOT_FINAL {
const uint32_t m_magic; // Magic number
const VerilatedVarType m_vltype; // Data type
const VerilatedVarFlags m_vlflags; // Direction
const uint32_t m_entSize; // Element size in bytes, or 0 to derive from type
std::vector<VerilatedRange> m_unpacked; // Unpacked array ranges
std::vector<VerilatedRange> m_packed; // Packed array ranges
VerilatedRange m_packedDpi; // Flattened packed array range
@ -104,10 +105,12 @@ class VerilatedVarProps VL_NOT_FINAL {
// CONSTRUCTORS
protected:
friend class VerilatedScope;
VerilatedVarProps(VerilatedVarType vltype, VerilatedVarFlags vlflags, int udims, int pdims)
VerilatedVarProps(VerilatedVarType vltype, VerilatedVarFlags vlflags, int udims, int pdims,
uint32_t entSize = 0)
: m_magic{MAGIC}
, m_vltype{vltype}
, m_vlflags{vlflags} {
, m_vlflags{vlflags}
, m_entSize{entSize} {
// Only preallocate the ranges
initUnpacked(udims, nullptr);
initPacked(pdims, nullptr);
@ -119,12 +122,14 @@ public:
VerilatedVarProps(VerilatedVarType vltype, int vlflags)
: m_magic{MAGIC}
, m_vltype{vltype}
, m_vlflags(VerilatedVarFlags(vlflags)) {} // Need () or GCC 4.8 false warning
, m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning
, m_entSize{0} {}
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_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning
, m_entSize{0} {
initUnpacked(udims, ulims);
}
// With packed
@ -132,14 +137,16 @@ public:
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_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning
, m_entSize{0} {
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_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning
, m_entSize{0} {
initUnpacked(udims, ulims);
initPacked(pdims, plims);
}
@ -164,6 +171,7 @@ public:
bool isDpiCLayout() const { return ((m_vlflags & VLVF_DPI_CLAY) != 0); }
bool isSigned() const { return ((m_vlflags & VLVF_SIGNED) != 0); }
bool isBitVar() const { return ((m_vlflags & VLVF_BITVAR) != 0); }
bool isNet() const { return ((m_vlflags & VLVF_NET) != 0); }
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(); }
@ -265,6 +273,8 @@ protected:
// CONSTRUCTORS
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam);
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam, uint32_t entSize);
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam,
std::unique_ptr<const VerilatedForceControlSignals> forceControlSignals);
@ -296,6 +306,13 @@ inline VerilatedVar::VerilatedVar(const char* namep, void* datap, VerilatedVarTy
, m_datap{datap}
, m_namep{namep}
, m_isParam{isParam} {}
inline VerilatedVar::VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam,
uint32_t entSize)
: VerilatedVarProps{vltype, vlflags, udims, pdims, entSize}
, m_datap{datap}
, m_namep{namep}
, m_isParam{isParam} {}
inline VerilatedVar::VerilatedVar(
const char* namep, void* datap, VerilatedVarType vltype, VerilatedVarFlags vlflags, int udims,
int pdims, bool isParam,

View File

@ -38,6 +38,7 @@
#include <algorithm>
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <list>
#include <map>
#include <set>
@ -68,6 +69,25 @@ constexpr unsigned VL_VPI_LINE_SIZE_ = 8192;
//======================================================================
// Implementation
static const char* _vl_vpi_find_unescaped_dot(const char* posp) {
for (; *posp; ++posp) {
if (*posp == '\\') {
while (*posp && *posp != ' ') ++posp;
if (!*posp) return nullptr;
} else if (*posp == '.') {
return posp;
}
}
return nullptr;
}
static std::string _vl_vpi_member_local_name(const char* const namep) {
const char* localp = namep;
const char* posp = namep;
while ((posp = _vl_vpi_find_unescaped_dot(posp))) localp = ++posp;
return localp;
}
// Base VPI handled object
class VerilatedVpio VL_NOT_FINAL {
// CONSTANTS
@ -201,6 +221,9 @@ public:
}
const VerilatedVar* varp() const { return m_varp; }
const VerilatedScope* scopep() const { return m_scopep; }
bool isStructOrUnion() const {
return varp()->vltype() == VLVT_STRUCT || varp()->vltype() == VLVT_UNION;
}
// 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.
@ -363,6 +386,8 @@ class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpioVarBase {
uint32_t m_entSize = 0; // memoized variable size
uint32_t m_bitOffset = 0;
int32_t m_partselBits = -1; // Part-select width, -1 means no part-select active
std::string m_name;
std::string m_fullNameOverride;
protected:
void* m_varDatap = nullptr; // varp()->datap() adjusted for array entries
@ -373,6 +398,17 @@ public:
: VerilatedVpioVarBase{varp, scopep} {
m_entSize = varp->entSize();
m_varDatap = varp->datap();
if (_vl_vpi_find_unescaped_dot(varp->name())) {
m_name = _vl_vpi_member_local_name(varp->name());
}
}
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep, void* datap,
const std::string& name, const std::string& fullname)
: VerilatedVpioVarBase{varp, scopep} {
m_entSize = varp->entSize();
m_varDatap = datap;
m_name = name;
m_fullNameOverride = fullname;
}
explicit VerilatedVpioVar(const VerilatedVpioVar* vop)
: VerilatedVpioVarBase{vop} {
@ -380,6 +416,8 @@ public:
m_entSize = vop->m_entSize;
m_varDatap = vop->m_varDatap;
m_index = vop->m_index;
m_name = vop->m_name;
m_fullNameOverride = vop->m_fullNameOverride;
m_partselBits = vop->m_partselBits;
m_bitOffset = vop->m_bitOffset;
// Not copying m_prevDatap, must be nullptr
@ -395,15 +433,22 @@ public:
uint32_t bitOffset() const override { return m_bitOffset; }
int32_t partselBits() const { return m_partselBits; }
uint32_t bitSize() const {
if (isStructOrUnion() && !isIndexedDimUnpacked()) return 0;
if (m_partselBits >= 0) return static_cast<uint32_t>(m_partselBits);
return VerilatedVpioVarBase::bitSize();
}
uint32_t size() const override {
if (isStructOrUnion() && !isIndexedDimUnpacked()) return 0;
if (m_partselBits >= 0) return static_cast<uint32_t>(m_partselBits);
return VerilatedVpioVarBase::size();
}
const VerilatedRange* rangep() const override {
if (isStructOrUnion() && !isIndexedDimUnpacked()) return nullptr;
return VerilatedVpioVarBase::rangep();
}
uint32_t entSize() const { return m_entSize; }
const std::vector<int32_t>& index() const { return m_index; }
const char* name() const override { return m_name.empty() ? varp()->name() : m_name.c_str(); }
// Create a part-selected view of this variable with the given bit range [hi:lo].
VerilatedVpioVar* withPartSelect(int32_t hi, int32_t lo) const {
if (isIndexedDimUnpacked()) return nullptr;
@ -456,22 +501,43 @@ public:
return ret;
}
VerilatedVpioVar* withMember(const VerilatedVar* memberVarp) const {
const char* const parentName = varp()->name();
const std::string memberName = memberVarp->name();
const size_t parentLen = std::strlen(parentName);
void* const parentDatap = varp()->datap();
void* const memberDatap = memberVarp->datap();
if (VL_UNLIKELY(!parentDatap) || VL_UNLIKELY(!memberDatap)) return nullptr;
const auto offset
= static_cast<uint8_t*>(memberDatap) - static_cast<uint8_t*>(parentDatap);
const std::string localName = _vl_vpi_member_local_name(memberVarp->name());
return new VerilatedVpioVar{memberVarp, scopep(),
static_cast<uint8_t*>(varDatap()) + offset, localName,
std::string{fullname()} + memberName.substr(parentLen)};
}
uint32_t type() const override {
uint32_t type;
// TODO have V3EmitCSyms.cpp put vpiType directly into constant table
switch (varp()->vltype()) {
case VLVT_REAL: type = vpiRealVar; break;
case VLVT_STRING: type = vpiStringVar; break;
case VLVT_STRUCT: type = varp()->isNet() ? vpiStructNet : vpiStructVar; break;
case VLVT_UNION: type = varp()->isNet() ? vpiUnionNet : vpiUnionVar; break;
default: type = varp()->isBitVar() ? vpiBitVar : vpiReg; break;
}
if (isIndexedDimUnpacked()) return vpiRegArray;
if (isIndexedDimUnpacked())
return isStructOrUnion() && varp()->isNet() ? vpiNetArray : vpiRegArray;
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();
m_fullname = m_fullNameOverride.empty()
? std::string{scopep()->name()} + "." + varp()->name()
: m_fullNameOverride;
for (auto idx : index()) m_fullname += "[" + std::to_string(idx) + "]";
return m_fullname.c_str();
}
void* prevDatap() const { return m_prevDatap; }
void* varDatap() const override { return m_varDatap; }
@ -590,25 +656,63 @@ public:
}
};
class VerilatedVpioMemberIter final : public VerilatedVpio {
const VerilatedScope* const m_scopep;
const VerilatedVarNameMap* const m_varsp;
VerilatedVarNameMap::const_iterator m_it;
VerilatedVpioVar* m_varp;
const std::string m_namePrefix;
bool m_started = false;
static std::string namePrefix(const VerilatedVpioVar* vop) {
return std::string{vop->varp()->name()} + ".";
}
vpiHandle atEnd() {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
}
public:
explicit VerilatedVpioMemberIter(const VerilatedVpioVar* vop)
: m_scopep{vop->scopep()}
, m_varsp{m_scopep->varsp()}
, m_varp{new VerilatedVpioVar{vop}}
, m_namePrefix{namePrefix(vop)} {}
~VerilatedVpioMemberIter() override { VL_DO_CLEAR(delete m_varp, m_varp = nullptr); }
// cppcheck-suppress duplInheritedMember
static VerilatedVpioMemberIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioMemberIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
uint32_t type() const override { return vpiIterator; }
vpiHandle dovpi_scan() override {
if (VL_UNLIKELY(!m_varsp)) return atEnd();
if (VL_UNLIKELY(!m_started)) {
m_it = m_varsp->begin();
m_started = true;
} else if (VL_LIKELY(m_it != m_varsp->end())) {
++m_it;
}
for (; m_it != m_varsp->end(); ++m_it) {
const char* const name = m_it->second.name();
if (std::strncmp(name, m_namePrefix.c_str(), m_namePrefix.length()) != 0) continue;
// Only direct members, not grandchildren
if (_vl_vpi_find_unescaped_dot(name + m_namePrefix.length())) continue;
VerilatedVpioVar* const memberp = m_varp->withMember(&(m_it->second));
if (VL_UNLIKELY(!memberp)) continue;
return memberp->castVpiHandle();
}
return atEnd();
}
};
class VerilatedVpioModule final : public VerilatedVpioScope {
public:
explicit VerilatedVpioModule(const VerilatedScope* modulep)
: VerilatedVpioScope{modulep} {
// Look for '.' not inside escaped identifier
const std::string scopename = m_fullname;
std::string::size_type pos = std::string::npos;
size_t i = 0;
while (i < scopename.length()) {
if (scopename[i] == '\\') {
while (i < scopename.length() && scopename[i] != ' ') ++i;
++i; // Proc ' ', it should always be there. Then grab '.' on next cycle
} else {
while (i < scopename.length() && scopename[i] != '.') ++i;
if (i < scopename.length()) pos = i++;
}
}
if (VL_UNLIKELY(pos == std::string::npos)) m_toplevel = true;
if (VL_UNLIKELY(!_vl_vpi_find_unescaped_dot(m_fullname))) m_toplevel = true;
}
// cppcheck-suppress duplInheritedMember
static VerilatedVpioModule* castp(vpiHandle h) {
@ -1770,6 +1874,8 @@ const char* VerilatedVpiError::strFromVpiObjType(PLI_INT32 vpiVal) VL_PURE {
};
// clang-format on
if (VL_UNCOVERABLE(vpiVal < 0)) return names[0];
// vpiUnionNet is outside the otherwise contiguous SystemVerilog object type range.
if (vpiVal == vpiUnionNet) return "vpiUnionNet";
if (vpiVal <= vpiAutomatics) return names[vpiVal];
if (vpiVal >= vpiPackage && vpiVal <= vpiPropFormalDecl)
return sv_names1[(vpiVal - vpiPackage)];
@ -2135,6 +2241,7 @@ void VerilatedVpiError::selfTest() VL_MT_UNSAFE_ONE {
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumVar);
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructVar);
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionVar);
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionNet);
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitVar);
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassObj);
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiChandleVar);
@ -2404,6 +2511,162 @@ static bool vl_vpi_parse_indices(std::string& name, std::vector<PLI_INT32>& indi
return true;
}
static const VerilatedScope* _vl_vpi_top_port_scopep(const VerilatedScope* const scopep) {
if (VL_UNLIKELY(!scopep)) return nullptr;
if (scopep->type() != VerilatedScope::SCOPE_MODULE) return nullptr;
if (std::strcmp(scopep->name(), "TOP") == 0) return nullptr;
if (_vl_vpi_find_unescaped_dot(scopep->name())) return nullptr;
return Verilated::threadContextp()->scopeFind("TOP");
}
static bool _vl_vpi_find_dotted_var(const std::string& scopename, const std::string& basename,
const VerilatedScope*& scopep, const VerilatedVar*& varp,
std::string& fullname) {
if (scopename.empty()) return false;
// Unpacked struct/union members are exposed as synthetic vars whose names contain dots
// (e.g. "mystruct.member"), so the boundary between the scope and the variable name is
// ambiguous. Walk the boundary leftward, moving one scope segment at a time onto the front
// of the dotted variable name, and try each candidate scope. An exhausted scope means the
// variable lives in the toplevel "TOP" scope.
std::string dottedName = basename;
std::string dottedScope = scopename;
while (true) {
const std::string::size_type lastDot = dottedScope.rfind('.');
dottedName = (lastDot == std::string::npos ? dottedScope : dottedScope.substr(lastDot + 1))
+ "." + dottedName;
dottedScope.resize(lastDot == std::string::npos ? 0 : lastDot);
scopep = Verilated::threadContextp()->scopeFind(dottedScope.empty() ? "TOP"
: dottedScope.c_str());
if (scopep) {
if (const VerilatedScope* const topScopep = _vl_vpi_top_port_scopep(scopep)) {
if (const VerilatedVar* const topVarp = topScopep->varFind(dottedName.c_str())) {
scopep = topScopep;
varp = topVarp;
fullname = dottedScope.empty() ? dottedName : dottedScope + "." + dottedName;
return true;
}
}
varp = scopep->varFind(dottedName.c_str());
if (varp) return true;
}
if (lastDot == std::string::npos) return false;
}
}
static VerilatedVpioVar* _vl_vpi_handle_member_by_name(const std::string& name,
const VerilatedVpioVar* vop) {
const VerilatedScope* const scopep = vop->scopep();
if (VL_UNLIKELY(!scopep)) return nullptr;
const std::string memberName = std::string{vop->varp()->name()} + "." + name;
const VerilatedVar* const memberVarp = scopep->varFind(memberName.c_str());
if (!memberVarp) return nullptr;
return vop->withMember(memberVarp);
}
static VerilatedVpioVar* _vl_vpi_handle_apply_indices(VerilatedVpioVar* vop,
const std::vector<PLI_INT32>& indices) {
for (const PLI_INT32 index : indices) {
VerilatedVpioVar* const nextVop = vop->withIndex(index);
VL_DO_CLEAR(delete vop, vop = nullptr);
if (!nextVop) return nullptr;
vop = nextVop;
}
return vop;
}
static bool _vl_vpi_parse_optional_indices(std::string& name, std::vector<PLI_INT32>& indices) {
indices.clear();
if (name.empty()) return false;
if (name.back() != ']') return true;
VlVpiBitRange bitRange;
return vl_vpi_parse_indices(name, indices, &bitRange) && !bitRange.valid;
}
static void _vl_vpi_split_dotted_name(const std::string& name, std::vector<std::string>& parts) {
parts.clear();
const char* const namep = name.c_str();
const char* beginp = namep;
const char* posp = beginp;
while ((posp = _vl_vpi_find_unescaped_dot(posp))) {
parts.emplace_back(beginp, posp - beginp);
beginp = ++posp;
}
parts.emplace_back(beginp);
}
static VerilatedVpioVar*
_vl_vpi_handle_indexed_member_from_scope(const VerilatedScope* const scopep,
const std::vector<std::string>& parts,
const size_t firstPart) {
if (VL_UNLIKELY(!scopep) || VL_UNLIKELY(firstPart >= parts.size())) return nullptr;
std::string baseName = parts[firstPart];
std::vector<PLI_INT32> indices;
if (!_vl_vpi_parse_optional_indices(baseName, indices)) return nullptr;
const VerilatedScope* varScopep = scopep;
const VerilatedVar* baseVarp = nullptr;
std::string fullnameOverride;
if (const VerilatedScope* const topScopep = _vl_vpi_top_port_scopep(scopep)) {
if (const VerilatedVar* const topVarp = topScopep->varFind(baseName.c_str())) {
varScopep = topScopep;
baseVarp = topVarp;
fullnameOverride = std::string{scopep->name()} + "." + baseName;
}
}
if (!baseVarp) baseVarp = scopep->varFind(baseName.c_str());
if (!baseVarp) return nullptr;
VerilatedVpioVar* baseVop
= fullnameOverride.empty()
? new VerilatedVpioVar{baseVarp, varScopep}
: new VerilatedVpioVar{baseVarp, varScopep, baseVarp->datap(),
_vl_vpi_member_local_name(baseVarp->name()),
fullnameOverride};
VerilatedVpioVar* vop = _vl_vpi_handle_apply_indices(baseVop, indices);
if (!vop) return nullptr;
for (size_t i = firstPart + 1; i < parts.size(); ++i) {
std::string memberName = parts[i];
if (!_vl_vpi_parse_optional_indices(memberName, indices)) {
VL_DO_CLEAR(delete vop, vop = nullptr);
return nullptr;
}
VerilatedVpioVar* const memberVop = _vl_vpi_handle_member_by_name(memberName, vop);
VL_DO_CLEAR(delete vop, vop = nullptr);
if (!memberVop) return nullptr;
vop = _vl_vpi_handle_apply_indices(memberVop, indices);
if (!vop) return nullptr;
}
return vop;
}
static VerilatedVpioVar*
_vl_vpi_handle_dotted_indexed_member_by_name(const std::string& scopeAndName) {
std::vector<std::string> parts;
_vl_vpi_split_dotted_name(scopeAndName, parts);
if (parts.size() < 2) return nullptr;
for (size_t firstPart = parts.size() - 1; firstPart > 0; --firstPart) {
std::string scopeName = parts[0];
for (size_t i = 1; i < firstPart; ++i) scopeName += "." + parts[i];
const VerilatedScope* const scopep
= Verilated::threadContextp()->scopeFind(scopeName.c_str());
if (!scopep) continue;
if (VerilatedVpioVar* const vop
= _vl_vpi_handle_indexed_member_from_scope(scopep, parts, firstPart)) {
return vop;
}
}
const VerilatedScope* const topScopep = Verilated::threadContextp()->scopeFind("TOP");
return _vl_vpi_handle_indexed_member_from_scope(topScopep, parts, 0);
}
// for obtaining handles
vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
@ -2425,7 +2688,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
const VerilatedVar* varp = nullptr;
const VerilatedScope* scopep;
std::string fullnameOverride;
const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
const VerilatedVpioVar* const voVarp = VerilatedVpioVar::castp(scope);
if (0 == std::strncmp(scopeAndName.c_str(), "$root.", std::strlen("$root."))) {
scopeAndName.erase(0, std::strlen("$root."));
@ -2433,6 +2698,12 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
const bool scopeIsPackage = VerilatedVpioPackage::castp(scope) != nullptr;
scopeAndName
= std::string{voScopep->fullname()} + (scopeIsPackage ? "" : ".") + scopeAndName;
} else if (voVarp && voVarp->isStructOrUnion()) {
if (VerilatedVpioVar* const memberp
= _vl_vpi_handle_member_by_name(scopeAndName, voVarp)) {
return memberp->castVpiHandle();
}
scopeAndName = std::string{voVarp->fullname()} + "." + scopeAndName;
}
{
// This doesn't yet follow the hierarchy in the proper way
@ -2491,8 +2762,18 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
}
if (!varp) {
scopep = Verilated::threadContextp()->scopeFind(scopename.c_str());
if (!scopep) return nullptr;
varp = scopep->varFind(basename.c_str());
if (scopep) { varp = scopep->varFind(basename.c_str()); }
// Unpacked struct members are exposed as synthetic variables with dotted names.
// Exact unindexed member names can be found directly; indexed member paths need the
// component walker so array indices are applied before member offsets.
if (!varp
&& !_vl_vpi_find_dotted_var(scopename, basename, scopep, varp, fullnameOverride)) {
if (VerilatedVpioVar* const memberp
= _vl_vpi_handle_dotted_indexed_member_by_name(scopeAndName)) {
return memberp->castVpiHandle();
}
return nullptr;
}
}
}
if (!varp) return nullptr;
@ -2501,6 +2782,11 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
vpiHandle resultHandle;
if (varp->isParam()) {
resultHandle = (new VerilatedVpioParam{varp, scopep})->castVpiHandle();
} else if (!fullnameOverride.empty()) {
resultHandle
= (new VerilatedVpioVar{varp, scopep, varp->datap(),
_vl_vpi_member_local_name(varp->name()), fullnameOverride})
->castVpiHandle();
} else {
resultHandle = (new VerilatedVpioVar{varp, scopep})->castVpiHandle();
}
@ -2642,6 +2928,11 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
if (vop) return ((new VerilatedVpioRegIter{vop})->castVpiHandle());
return nullptr;
}
case vpiMember: {
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
if (!vop || !vop->isStructOrUnion()) return nullptr;
return ((new VerilatedVpioMemberIter{vop})->castVpiHandle());
}
case vpiParameter: {
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
@ -2734,6 +3025,11 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
if (VL_UNLIKELY(!vop)) return vpiUndefined;
return vop->varp()->isSigned();
}
case vpiPacked: {
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
if (VL_LIKELY(vop && vop->isStructOrUnion())) return 0;
[[fallthrough]];
}
default:
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported property %s, nothing will be returned",
__func__, VerilatedVpiError::strFromVpiProp(property));

View File

@ -174,6 +174,7 @@ public:
bool isStreamableFixedAggregate() const;
bool containsUnpackedStruct() const;
int widthStream() const;
string vlEnumType() const; // Return VerilatedVarType: VLVT_UINT32, etc
static int uniqueNumInc() { return ++s_uniqueNum; }
const char* charIQWN() const {
return (isString() ? "N" : isWide() ? "W" : isDouble() ? "D" : isQuad() ? "Q" : "I");

View File

@ -703,9 +703,14 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
return ostatic + dtypep()->cType(oname, forFunc, asRef);
}
string AstVar::vlEnumType() const {
string AstNodeDType::vlEnumType() const {
string arg;
const AstBasicDType* const bdtypep = basicp();
const AstNodeDType* dtypep = skipRefp();
while (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
dtypep = adtypep->subDTypep()->skipRefp();
}
const AstBasicDType* const bdtypep = dtypep->basicp();
const AstNodeUOrStructDType* const sdtypep = VN_CAST(dtypep, NodeUOrStructDType);
const bool strtype = bdtypep && bdtypep->keyword() == VBasicDTypeKwd::STRING;
if (bdtypep && bdtypep->keyword() == VBasicDTypeKwd::CHARPTR) {
return "VLVT_PTR";
@ -715,6 +720,8 @@ string AstVar::vlEnumType() const {
arg += "VLVT_STRING";
} else if (isDouble()) {
arg += "VLVT_REAL";
} else if (sdtypep && !sdtypep->packed()) {
arg += VN_IS(sdtypep, StructDType) ? "VLVT_STRUCT" : "VLVT_UNION";
} else if (widthMin() <= 8) {
arg += "VLVT_UINT8";
} else if (widthMin() <= 16) {
@ -730,6 +737,8 @@ string AstVar::vlEnumType() const {
return arg;
}
string AstVar::vlEnumType() const { return dtypep()->vlEnumType(); }
string AstVar::vlEnumDir() const {
string out;
if (isInout()) {
@ -759,6 +768,7 @@ string AstVar::vlEnumDir() const {
if (AstBasicDType* const basicp = dtypep()->skipRefp()->basicp()) {
if (basicp->keyword() == VBasicDTypeKwd::BIT) out += "|VLVF_BITVAR";
}
if (isNet()) out += "|VLVF_NET";
return out;
}

View File

@ -176,26 +176,26 @@ class EmitCSyms final : EmitCBaseVisitorConst {
return pos != std::string::npos ? scpname.substr(pos + 1) : scpname;
}
static std::tuple<int, int, std::string> getDimensions(const AstVar* const varp) {
static std::tuple<int, int, std::string> getDimensions(const AstNodeDType* const rootDtypep) {
int pdim = 0;
int udim = 0;
std::string bounds;
if (const AstBasicDType* const basicp = varp->basicp()) {
// Range is always first, it's not in "C" order
for (AstNodeDType* dtypep = varp->dtypep(); dtypep;) {
// Skip AstRefDType/AstTypedef, or return same node
dtypep = dtypep->skipRefp();
if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) {
bounds += " ,";
bounds += std::to_string(adtypep->left());
bounds += ",";
bounds += std::to_string(adtypep->right());
if (VN_IS(dtypep, PackArrayDType))
pdim++;
else
udim++;
dtypep = adtypep->subDTypep();
} else {
// Range is always first, it's not in "C" order
for (const AstNodeDType* dtypep = rootDtypep; dtypep;) {
// Skip AstRefDType/AstTypedef, or return same node
dtypep = dtypep->skipRefp();
if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) {
bounds += " ,";
bounds += std::to_string(adtypep->left());
bounds += ",";
bounds += std::to_string(adtypep->right());
if (VN_IS(dtypep, PackArrayDType))
pdim++;
else
udim++;
dtypep = adtypep->subDTypep();
} else {
if (const AstBasicDType* const basicp = dtypep->basicp()) {
if (basicp->isRanged()) {
bounds += " ,";
bounds += std::to_string(basicp->left());
@ -203,13 +203,33 @@ class EmitCSyms final : EmitCBaseVisitorConst {
bounds += std::to_string(basicp->right());
pdim++;
}
break; // AstBasicDType - nothing below, 1
}
break; // Non-array leaf
}
}
return {pdim, udim, bounds};
}
static std::tuple<int, int, std::string> getDimensions(const AstVar* const varp) {
return getDimensions(varp->dtypep());
}
static int getUnpackedElements(const AstNodeDType* rootDtypep) {
int elements = 1;
for (const AstNodeDType* dtypep = rootDtypep; dtypep;) {
dtypep = dtypep->skipRefp();
const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType);
if (!adtypep) break;
elements *= adtypep->elementsConst();
dtypep = adtypep->subDTypep();
}
return elements;
}
static bool needsEmittedEntSize(const std::string& vlEnumType) {
return vlEnumType == "VLVT_STRUCT" || vlEnumType == "VLVT_UNION";
}
static std::pair<bool, std::string> isForceControlSignal(const AstVar* const signalVarp) {
// __VforceRd should not show up here because it is never public, but just in case it does,
// it should be skipped because forceableVarInsert creates its VerilatedVar.
@ -225,15 +245,47 @@ class EmitCSyms final : EmitCBaseVisitorConst {
return std::pair<bool, std::string>{false, ""};
}
static void appendVarProperties(std::string& stmt, const std::string& vlEnumType,
const std::string& vlEnumDir, const int udim, const int pdim,
const std::string& bounds, const std::string& entSize = "") {
stmt += vlEnumType; // VLVT_UINT32 etc
stmt += ", ";
stmt += vlEnumDir; // VLVD_IN etc
stmt += ", ";
stmt += std::to_string(udim);
if (!entSize.empty()) {
stmt += ", ";
stmt += entSize;
} else {
stmt += ", ";
stmt += std::to_string(pdim);
}
stmt += bounds;
stmt += ")";
}
static std::string memberVlEnumDir(const AstVar* const varp,
const AstNodeDType* const dtypep) {
std::string out = "((" + varp->vlEnumDir() + ") & ~(VLVF_SIGNED|VLVF_BITVAR))";
const AstNodeDType* const skipDTypep = dtypep->skipRefp();
if (skipDTypep->isSigned()) out += "|VLVF_SIGNED";
if (const AstBasicDType* const basicp = skipDTypep->basicp()) {
if (basicp->keyword() == VBasicDTypeKwd::BIT) out += "|VLVF_BITVAR";
}
return out;
}
static std::string insertVarStatement(const ScopeVarData& svd, const AstScope* const scopep,
const AstVar* const varp, const int udim, const int pdim,
const std::string& bounds) {
std::string stmt;
stmt += protect("__Vscopep_" + svd.m_scopeName) + "->varInsert(\"";
stmt += V3OutFormatter::quoteNameControls(protect(svd.m_varBasePretty)) + '"';
const std::string varName = VIdProtect::protectIf(scopep->nameDotless(), scopep->protect())
+ "." + protect(varp->name());
const std::string vlEnumType = varp->vlEnumType();
const bool needsEntSize = needsEmittedEntSize(vlEnumType);
std::string stmt;
stmt += protect("__Vscopep_" + svd.m_scopeName);
stmt += needsEntSize ? "->varInsertSized(\"" : "->varInsert(\"";
stmt += V3OutFormatter::quoteNameControls(protect(svd.m_varBasePretty)) + '"';
if (!varp->isParam()) {
stmt += ", &(";
@ -250,18 +302,87 @@ class EmitCSyms final : EmitCBaseVisitorConst {
stmt += "))), true, ";
}
stmt += varp->vlEnumType(); // VLVT_UINT32 etc
stmt += ", ";
stmt += varp->vlEnumDir(); // VLVD_IN etc
stmt += ", ";
stmt += std::to_string(udim);
stmt += ", ";
stmt += std::to_string(pdim);
stmt += bounds;
stmt += ")";
const std::string entSize = needsEntSize
? "sizeof(" + varName + ") / "
+ std::to_string(getUnpackedElements(varp->dtypep()))
: "";
appendVarProperties(stmt, vlEnumType, varp->vlEnumDir(), udim, pdim, bounds, entSize);
return stmt;
}
static std::string insertDTypeVarStatement(const ScopeVarData& svd,
const AstScope* const scopep,
const std::string& prettyName,
const std::string& cName,
const AstNodeDType* const dtypep, const int udim,
const int pdim, const std::string& bounds) {
const std::string vlEnumType = dtypep->vlEnumType();
const bool needsEntSize = needsEmittedEntSize(vlEnumType);
std::string stmt;
stmt += protect("__Vscopep_" + svd.m_scopeName);
stmt += needsEntSize ? "->varInsertSized(\"" : "->varInsert(\"";
stmt += V3OutFormatter::quoteNameControls(prettyName) + '"';
stmt += ", &(";
stmt += VIdProtect::protectIf(scopep->nameDotless(), scopep->protect());
stmt += ".";
stmt += cName;
stmt += "), false, ";
const std::string varName
= VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + "." + cName;
const std::string entSize
= needsEntSize
? "sizeof(" + varName + ") / " + std::to_string(getUnpackedElements(dtypep))
: "";
appendVarProperties(stmt, vlEnumType, memberVlEnumDir(svd.m_varp, dtypep), udim, pdim,
bounds, entSize);
return stmt;
}
static void addUOrStructMemberVars(std::vector<std::string>& stmts, const ScopeVarData& svd,
const AstScope* const scopep,
const std::string& prettyPrefix, const std::string& cPrefix,
const AstNodeUOrStructDType* const sdtypep) {
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
const AstNodeDType* const itemDTypep = itemp->dtypep();
const std::string prettyName
= prettyPrefix + "." + AstNode::vpiName(itemp->shortName());
const std::string cName = cPrefix + "." + itemp->nameProtect();
const std::tuple<int, int, std::string> dimensions = getDimensions(itemDTypep);
const int pdim = std::get<0>(dimensions);
const int udim = std::get<1>(dimensions);
const std::string bounds = std::get<2>(dimensions);
stmts.emplace_back(insertDTypeVarStatement(svd, scopep, prettyName, cName, itemDTypep,
udim, pdim, bounds)
+ ";");
if (const AstNodeUOrStructDType* const subp
= VN_CAST(itemDTypep->skipRefp(), NodeUOrStructDType)) {
if (!subp->packed())
addUOrStructMemberVars(stmts, svd, scopep, prettyName, cName, subp);
} else if (VN_IS(itemDTypep->skipRefp(), UnpackArrayDType)) {
addUnpackedArrayUOrStructMemberVars(stmts, svd, scopep, prettyName, cName,
itemDTypep);
}
}
}
static void addUnpackedArrayUOrStructMemberVars(std::vector<std::string>& stmts,
const ScopeVarData& svd,
const AstScope* const scopep,
const std::string& prettyPrefix,
const std::string& cPrefix,
const AstNodeDType* const dtypep) {
const AstNodeDType* const skipDTypep = dtypep->skipRefp();
if (const AstUnpackArrayDType* const adtypep = VN_CAST(skipDTypep, UnpackArrayDType)) {
addUnpackedArrayUOrStructMemberVars(stmts, svd, scopep, prettyPrefix, cPrefix + "[0]",
adtypep->subDTypep());
} else if (const AstNodeUOrStructDType* const sdtypep
= VN_CAST(skipDTypep, NodeUOrStructDType)) {
if (!sdtypep->packed())
addUOrStructMemberVars(stmts, svd, scopep, prettyPrefix, cPrefix, sdtypep);
}
}
std::string insertForceableVarStatement(const ScopeVarData& svd, const AstScope* const scopep,
const AstVar* const varp, const int udim,
const int pdim, const std::string& bounds) {
@ -1061,6 +1182,16 @@ std::vector<std::string> EmitCSyms::getSymCtorStmts() {
const std::string stmt
= insertVarStatement(svd, scopep, varp, udim, pdim, bounds) + ";";
add(stmt);
if (const AstNodeUOrStructDType* const sdtypep
= VN_CAST(varp->dtypeSkipRefp(), NodeUOrStructDType)) {
if (!sdtypep->packed()) {
addUOrStructMemberVars(stmts, svd, scopep, svd.m_varBasePretty,
protect(varp->name()), sdtypep);
}
} else if (VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType)) {
addUnpackedArrayUOrStructMemberVars(stmts, svd, scopep, svd.m_varBasePretty,
protect(varp->name()), varp->dtypep());
}
}
}
}

View File

@ -38,6 +38,8 @@
#endif
#include <set>
#ifdef VERILATOR
#include "verilated.h"
#endif
@ -258,6 +260,626 @@ int _mon_check_big() {
return 0;
}
int _mon_check_unpacked_struct_members() {
const char* p;
PLI_INT32 d;
t_vpi_value tmpValue;
// unpacked struct port members
tmpValue.format = vpiIntVal;
{
TestVpiHandle vh101 = VPI_HANDLE("unpacked_struct_port");
CHECK_RESULT_NZ(vh101);
d = vpi_get(vpiType, vh101);
CHECK_RESULT(d, vpiStructVar);
CHECK_RESULT(vpi_get(vpiSize, vh101), 0);
CHECK_RESULT(vpi_get(vpiPacked, vh101), 0);
CHECK_RESULT_Z(vpi_handle(vpiLeftRange, vh101));
p = vpi_get_str(vpiType, vh101);
CHECK_RESULT_CSTR(p, "vpiStructVar");
TestVpiHandle vh102
= vpi_handle_by_name((PLI_BYTE8*)"t.unpacked_struct_port.member_a", NULL);
CHECK_RESULT_NZ(vh102);
CHECK_RESULT(vpi_get(vpiType, vh102), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh102), 7);
p = vpi_get_str(vpiName, vh102);
CHECK_RESULT_CSTR(p, "member_a");
p = vpi_get_str(vpiFullName, vh102);
CHECK_RESULT_CSTR(p, "t.unpacked_struct_port.member_a");
CHECK_RESULT_NZ(vpi_handle(vpiLeftRange, vh102));
CHECK_RESULT_Z(vpi_iterate(vpiMember, vh102));
CHECK_RESULT_Z(vpi_handle_by_name((PLI_BYTE8*)"member_a", vh102));
TestVpiHandle vh103
= vpi_handle_by_name((PLI_BYTE8*)"t.unpacked_struct_port.member_b", NULL);
CHECK_RESULT_NZ(vh103);
CHECK_RESULT(vpi_get(vpiType, vh103), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh103), 1);
TestVpiHandle vh104 = vpi_handle_by_name((PLI_BYTE8*)"member_c", vh101);
CHECK_RESULT_NZ(vh104);
CHECK_RESULT(vpi_get(vpiType, vh104), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh104), 16);
CHECK_RESULT_Z(vpi_iterate(vpiMember, nullptr));
TestVpiHandle vh105 = vpi_iterate(vpiMember, vh101);
CHECK_RESULT_NZ(vh105);
CHECK_RESULT(vpi_get(vpiType, vh105), vpiIterator);
std::set<std::string> memberNames;
while (TestVpiHandle member = vpi_scan(vh105)) {
memberNames.insert(vpi_get_str(vpiName, member));
}
vh105.freed(); // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
CHECK_RESULT(memberNames.count("member_a"), 1);
CHECK_RESULT(memberNames.count("member_b"), 1);
CHECK_RESULT(memberNames.count("member_c"), 1);
s_vpi_value putValue;
putValue.format = vpiIntVal;
putValue.value.integer = 0x35;
vpi_put_value(vh102, &putValue, NULL, vpiNoDelay);
vpi_get_value(vh102, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x35);
putValue.value.integer = 0x4321;
vpi_put_value(vh104, &putValue, NULL, vpiNoDelay);
vpi_get_value(vh104, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x4321);
// Members resolve even without the toplevel scope prefix
TestVpiHandle vh106
= vpi_handle_by_name((PLI_BYTE8*)"unpacked_struct_port.member_a", NULL);
CHECK_RESULT_NZ(vh106);
CHECK_RESULT(vpi_get(vpiType, vh106), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh106), 7);
vpi_get_value(vh106, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x35);
TestVpiHandle vh107
= vpi_handle_by_name((PLI_BYTE8*)"$root.t.unpacked_struct_port.member_a", NULL);
CHECK_RESULT_NZ(vh107);
CHECK_RESULT(vpi_get(vpiType, vh107), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh107), 7);
TestVpiHandle vh108 = VPI_HANDLE("");
CHECK_RESULT_NZ(vh108);
TestVpiHandle vh109
= vpi_handle_by_name((PLI_BYTE8*)"unpacked_struct_port.member_b", vh108);
CHECK_RESULT_NZ(vh109);
CHECK_RESULT(vpi_get(vpiType, vh109), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh109), 1);
TestVpiHandle vh100
= vpi_handle_by_name((PLI_BYTE8*)"t.unpacked_struct_port.member_c[7:0]", NULL);
CHECK_RESULT_NZ(vh100);
CHECK_RESULT(vpi_get(vpiType, vh100), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh100), 8);
CHECK_RESULT_Z(vpi_handle_by_name((PLI_BYTE8*)"t.unpacked_struct_port[7:0]", NULL));
CHECK_RESULT_Z(
vpi_handle_by_name((PLI_BYTE8*)"t.unpacked_struct_port.no_such_member", NULL));
}
// Synthetic member vars preserve type-derived flags from the member dtype
{
TestVpiHandle vh114 = VPI_HANDLE("member_flags_struct_signal");
CHECK_RESULT_NZ(vh114);
CHECK_RESULT(vpi_get(vpiType, vh114), vpiStructVar);
TestVpiHandle vh115 = vpi_handle_by_name((PLI_BYTE8*)"unsigned_member", vh114);
CHECK_RESULT_NZ(vh115);
CHECK_RESULT(vpi_get(vpiType, vh115), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh115), 7);
CHECK_RESULT(vpi_get(vpiSigned, vh115), 0);
TestVpiHandle vh116 = vpi_handle_by_name((PLI_BYTE8*)"signed_member", vh114);
CHECK_RESULT_NZ(vh116);
CHECK_RESULT(vpi_get(vpiType, vh116), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh116), 7);
CHECK_RESULT(vpi_get(vpiSigned, vh116), 1);
TestVpiHandle vh117 = vpi_handle_by_name((PLI_BYTE8*)"bit_member", vh114);
CHECK_RESULT_NZ(vh117);
CHECK_RESULT(vpi_get(vpiType, vh117), vpiBitVar);
CHECK_RESULT(vpi_get(vpiSize, vh117), 1);
CHECK_RESULT(vpi_get(vpiSigned, vh117), 0);
}
// unpacked union port members
{
TestVpiHandle vh110 = VPI_HANDLE("unpacked_union_port");
CHECK_RESULT_NZ(vh110);
CHECK_RESULT(vpi_get(vpiType, vh110), vpiUnionVar);
CHECK_RESULT(vpi_get(vpiSize, vh110), 0);
CHECK_RESULT(vpi_get(vpiPacked, vh110), 0);
p = vpi_get_str(vpiType, vh110);
CHECK_RESULT_CSTR(p, "vpiUnionVar");
TestVpiHandle vh111 = vpi_handle_by_name((PLI_BYTE8*)"union_byte0", vh110);
CHECK_RESULT_NZ(vh111);
CHECK_RESULT(vpi_get(vpiType, vh111), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh111), 8);
TestVpiHandle vh112 = vpi_iterate(vpiMember, vh110);
CHECK_RESULT_NZ(vh112);
std::set<std::string> unionMembers;
while (TestVpiHandle member = vpi_scan(vh112)) {
unionMembers.insert(vpi_get_str(vpiName, member));
}
vh112.freed(); // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
CHECK_RESULT(unionMembers.count("union_byte0"), 1);
CHECK_RESULT(unionMembers.count("union_byte1"), 1);
}
// nested unpacked struct port members
{
TestVpiHandle vh120 = VPI_HANDLE("nested_struct_port");
CHECK_RESULT_NZ(vh120);
CHECK_RESULT(vpi_get(vpiType, vh120), vpiStructVar);
// The nested struct member is itself a struct
TestVpiHandle vh121 = vpi_handle_by_name((PLI_BYTE8*)"outer_inner", vh120);
CHECK_RESULT_NZ(vh121);
CHECK_RESULT(vpi_get(vpiType, vh121), vpiStructVar);
CHECK_RESULT(vpi_get(vpiSize, vh121), 0);
// Leaf reachable via the full hierarchical name
TestVpiHandle vh122
= vpi_handle_by_name((PLI_BYTE8*)"t.nested_struct_port.outer_inner.inner_x", NULL);
CHECK_RESULT_NZ(vh122);
CHECK_RESULT(vpi_get(vpiType, vh122), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh122), 4);
// Leaf reachable relative to the intermediate struct handle
TestVpiHandle vh123 = vpi_handle_by_name((PLI_BYTE8*)"inner_y", vh121);
CHECK_RESULT_NZ(vh123);
CHECK_RESULT(vpi_get(vpiType, vh123), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh123), 4);
// Iterating the outer struct yields direct members only, not grandchildren
TestVpiHandle vh124 = vpi_iterate(vpiMember, vh120);
CHECK_RESULT_NZ(vh124);
std::set<std::string> nestedMembers;
while (TestVpiHandle member = vpi_scan(vh124)) {
nestedMembers.insert(vpi_get_str(vpiName, member));
}
vh124.freed(); // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
CHECK_RESULT(nestedMembers.count("outer_a"), 1);
CHECK_RESULT(nestedMembers.count("outer_inner"), 1);
CHECK_RESULT(nestedMembers.count("inner_x"), 0);
TestVpiHandle vh125 = vpi_handle_by_name((PLI_BYTE8*)"t.sub.subsig1", NULL);
CHECK_RESULT_NZ(vh125);
CHECK_RESULT(vpi_get(vpiType, vh125), vpiReg);
// Write through a leaf of the nested struct
s_vpi_value putValue;
putValue.format = vpiIntVal;
putValue.value.integer = 0xa;
vpi_put_value(vh122, &putValue, NULL, vpiNoDelay);
vpi_get_value(vh122, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0xa);
}
// unpacked struct port with multidimensional packed-array members
{
TestVpiHandle vh130 = VPI_HANDLE("struct_with_packed_arrays_port");
CHECK_RESULT_NZ(vh130);
CHECK_RESULT(vpi_get(vpiType, vh130), vpiStructVar);
TestVpiHandle vh131 = vpi_handle_by_name((PLI_BYTE8*)"packed_matrix", vh130);
CHECK_RESULT_NZ(vh131);
CHECK_RESULT(vpi_get(vpiType, vh131), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh131), 512);
TestVpiHandle vh132 = vpi_handle_by_index(vh131, 2);
CHECK_RESULT_NZ(vh132);
CHECK_RESULT(vpi_get(vpiSize, vh132), 32);
TestVpiHandle vh133 = vpi_handle_by_index(vh132, 2);
CHECK_RESULT_NZ(vh133);
CHECK_RESULT(vpi_get(vpiSize, vh133), 8);
TestVpiHandle vh134 = vpi_handle_by_name((PLI_BYTE8*)"reverse_matrix", vh130);
CHECK_RESULT_NZ(vh134);
CHECK_RESULT(vpi_get(vpiType, vh134), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh134), 128);
TestVpiHandle vh135 = vpi_handle_by_index(vh134, -2);
CHECK_RESULT_NZ(vh135);
CHECK_RESULT(vpi_get(vpiSize, vh135), 8);
}
// port array of unpacked structs
{
TestVpiHandle vh140 = VPI_HANDLE("struct_array_port");
CHECK_RESULT_NZ(vh140);
CHECK_RESULT(vpi_get(vpiType, vh140), vpiRegArray);
TestVpiHandle vh141 = vpi_handle_by_index(vh140, 0);
CHECK_RESULT_NZ(vh141);
CHECK_RESULT(vpi_get(vpiType, vh141), vpiStructVar);
TestVpiHandle vh142 = vpi_handle_by_name((PLI_BYTE8*)"member_c", vh141);
CHECK_RESULT_NZ(vh142);
CHECK_RESULT(vpi_get(vpiType, vh142), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh142), 16);
s_vpi_value putValue;
putValue.format = vpiIntVal;
putValue.value.integer = 0x6789;
vpi_put_value(vh142, &putValue, NULL, vpiNoDelay);
vpi_get_value(vh142, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x6789);
TestVpiHandle vh144 = vpi_handle_by_index(vh140, 1);
CHECK_RESULT_NZ(vh144);
TestVpiHandle vh145 = vpi_handle_by_name((PLI_BYTE8*)"member_c", vh144);
CHECK_RESULT_NZ(vh145);
putValue.value.integer = 0x2468;
vpi_put_value(vh145, &putValue, NULL, vpiNoDelay);
vpi_get_value(vh145, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x2468);
vpi_get_value(vh142, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x6789);
TestVpiHandle vh146
= vpi_handle_by_name((PLI_BYTE8*)"struct_array_port[1].member_c", nullptr);
CHECK_RESULT_NZ(vh146);
vpi_get_value(vh146, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x2468);
TestVpiHandle vh143 = vpi_iterate(vpiMember, vh141);
CHECK_RESULT_NZ(vh143);
std::set<std::string> memberNames;
while (TestVpiHandle member = vpi_scan(vh143)) {
memberNames.insert(vpi_get_str(vpiName, member));
}
vh143.freed(); // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
CHECK_RESULT(memberNames.count("member_a"), 1);
CHECK_RESULT(memberNames.count("member_b"), 1);
CHECK_RESULT(memberNames.count("member_c"), 1);
}
// multidimensional port array of unpacked structs
{
TestVpiHandle vh150 = VPI_HANDLE("struct_matrix_port");
CHECK_RESULT_NZ(vh150);
CHECK_RESULT(vpi_get(vpiType, vh150), vpiRegArray);
CHECK_RESULT(vpi_get(vpiSize, vh150), 6);
TestVpiHandle vh151 = vpi_handle_by_index(vh150, 1);
CHECK_RESULT_NZ(vh151);
CHECK_RESULT(vpi_get(vpiType, vh151), vpiRegArray);
CHECK_RESULT(vpi_get(vpiSize, vh151), 3);
TestVpiHandle vh152 = vpi_handle_by_index(vh151, 2);
CHECK_RESULT_NZ(vh152);
CHECK_RESULT(vpi_get(vpiType, vh152), vpiStructVar);
TestVpiHandle vh153 = vpi_handle_by_name((PLI_BYTE8*)"member_c", vh152);
CHECK_RESULT_NZ(vh153);
CHECK_RESULT(vpi_get(vpiType, vh153), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh153), 16);
p = vpi_get_str(vpiName, vh153);
CHECK_RESULT_CSTR(p, "member_c");
p = vpi_get_str(vpiFullName, vh153);
{
const std::string expectedFullName
= std::string{vpi_get_str(vpiFullName, vh152)} + ".member_c";
CHECK_RESULT_CSTR(p, expectedFullName.c_str());
}
s_vpi_value putValue;
putValue.format = vpiIntVal;
putValue.value.integer = 0x1357;
vpi_put_value(vh153, &putValue, NULL, vpiNoDelay);
vpi_get_value(vh153, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x1357);
TestVpiHandle vh154
= vpi_handle_by_name((PLI_BYTE8*)"struct_matrix_port[1][2].member_c", nullptr);
CHECK_RESULT_NZ(vh154);
vpi_get_value(vh154, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x1357);
}
// unpacked struct net port members
{
TestVpiHandle vh190 = VPI_HANDLE("wire_unpacked_struct_port");
CHECK_RESULT_NZ(vh190);
CHECK_RESULT(vpi_get(vpiType, vh190), vpiStructNet);
CHECK_RESULT(vpi_get(vpiPacked, vh190), 0);
p = vpi_get_str(vpiType, vh190);
CHECK_RESULT_CSTR(p, "vpiStructNet");
TestVpiHandle vh191 = vpi_handle_by_name((PLI_BYTE8*)"member_a", vh190);
CHECK_RESULT_NZ(vh191);
CHECK_RESULT(vpi_get(vpiType, vh191), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh191), 7);
s_vpi_value putValue;
putValue.format = vpiIntVal;
putValue.value.integer = 0x25;
vpi_put_value(vh191, &putValue, NULL, vpiNoDelay);
vpi_get_value(vh191, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x25);
TestVpiHandle vh192 = vpi_iterate(vpiMember, vh190);
CHECK_RESULT_NZ(vh192);
std::set<std::string> memberNames;
while (TestVpiHandle member = vpi_scan(vh192)) {
memberNames.insert(vpi_get_str(vpiName, member));
}
vh192.freed(); // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
CHECK_RESULT(memberNames.count("member_a"), 1);
CHECK_RESULT(memberNames.count("member_b"), 1);
CHECK_RESULT(memberNames.count("member_c"), 1);
}
// net array of unpacked structs
{
TestVpiHandle vh193 = VPI_HANDLE("wire_struct_array_port");
CHECK_RESULT_NZ(vh193);
CHECK_RESULT(vpi_get(vpiType, vh193), vpiNetArray);
TestVpiHandle vh194 = vpi_handle_by_index(vh193, 0);
CHECK_RESULT_NZ(vh194);
CHECK_RESULT(vpi_get(vpiType, vh194), vpiStructNet);
CHECK_RESULT(vpi_get(vpiPacked, vh194), 0);
TestVpiHandle vh195 = vpi_handle_by_name((PLI_BYTE8*)"member_c", vh194);
CHECK_RESULT_NZ(vh195);
CHECK_RESULT(vpi_get(vpiType, vh195), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh195), 16);
s_vpi_value putValue;
putValue.format = vpiIntVal;
putValue.value.integer = 0x579b;
vpi_put_value(vh195, &putValue, NULL, vpiNoDelay);
vpi_get_value(vh195, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x579b);
}
// non-port array of unpacked structs
{
TestVpiHandle vh160 = VPI_HANDLE("struct_array_signal");
CHECK_RESULT_NZ(vh160);
CHECK_RESULT(vpi_get(vpiType, vh160), vpiRegArray);
TestVpiHandle vh161 = vpi_handle_by_index(vh160, 1);
CHECK_RESULT_NZ(vh161);
CHECK_RESULT(vpi_get(vpiType, vh161), vpiStructVar);
TestVpiHandle vh162 = vpi_handle_by_name((PLI_BYTE8*)"member_c", vh161);
CHECK_RESULT_NZ(vh162);
CHECK_RESULT(vpi_get(vpiType, vh162), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh162), 16);
s_vpi_value putValue;
putValue.format = vpiIntVal;
putValue.value.integer = 0xace0;
vpi_put_value(vh162, &putValue, NULL, vpiNoDelay);
TestVpiHandle vh163 = vpi_handle_by_name(
const_cast<PLI_BYTE8*>(TestSimulator::rooted("struct_array_signal[1].member_c")),
nullptr);
CHECK_RESULT_NZ(vh163);
vpi_get_value(vh163, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0xace0);
}
// array of unpacked structs with unpacked-array members
{
TestVpiHandle vh170 = VPI_HANDLE("parent_struct_array");
CHECK_RESULT_NZ(vh170);
CHECK_RESULT(vpi_get(vpiType, vh170), vpiRegArray);
CHECK_RESULT(vpi_get(vpiSize, vh170), 2);
TestVpiHandle vh171 = vpi_handle_by_index(vh170, 0);
CHECK_RESULT_NZ(vh171);
CHECK_RESULT(vpi_get(vpiType, vh171), vpiStructVar);
TestVpiHandle vh172 = vpi_handle_by_index(vh170, 1);
CHECK_RESULT_NZ(vh172);
CHECK_RESULT(vpi_get(vpiType, vh172), vpiStructVar);
TestVpiHandle vh173 = vpi_handle_by_name((PLI_BYTE8*)"tail_array", vh171);
CHECK_RESULT_NZ(vh173);
CHECK_RESULT(vpi_get(vpiType, vh173), vpiRegArray);
CHECK_RESULT(vpi_get(vpiSize, vh173), 4);
TestVpiHandle vh174 = vpi_handle_by_index(vh173, 3);
CHECK_RESULT_NZ(vh174);
CHECK_RESULT(vpi_get(vpiType, vh174), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh174), 8);
s_vpi_value putValue;
putValue.format = vpiIntVal;
putValue.value.integer = 0xaa;
vpi_put_value(vh174, &putValue, NULL, vpiNoDelay);
TestVpiHandle vh17a = vpi_handle_by_name((PLI_BYTE8*)"trailing_children", vh171);
CHECK_RESULT_NZ(vh17a);
CHECK_RESULT(vpi_get(vpiType, vh17a), vpiRegArray);
CHECK_RESULT(vpi_get(vpiSize, vh17a), 2);
TestVpiHandle vh17b = vpi_handle_by_index(vh17a, 2);
CHECK_RESULT_NZ(vh17b);
CHECK_RESULT(vpi_get(vpiType, vh17b), vpiStructVar);
TestVpiHandle vh17c = vpi_handle_by_name((PLI_BYTE8*)"child_leaf", vh17b);
CHECK_RESULT_NZ(vh17c);
CHECK_RESULT(vpi_get(vpiType, vh17c), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh17c), 8);
putValue.value.integer = 0x11;
vpi_put_value(vh17c, &putValue, NULL, vpiNoDelay);
TestVpiHandle vh175 = vpi_handle_by_name((PLI_BYTE8*)"scalar", vh172);
CHECK_RESULT_NZ(vh175);
CHECK_RESULT(vpi_get(vpiType, vh175), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh175), 16);
putValue.value.integer = 0x55cc;
vpi_put_value(vh175, &putValue, NULL, vpiNoDelay);
vpi_get_value(vh175, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x55cc);
vpi_get_value(vh174, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0xaa);
vpi_get_value(vh17c, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x11);
TestVpiHandle vh176 = vpi_handle_by_name((PLI_BYTE8*)"children", vh172);
CHECK_RESULT_NZ(vh176);
CHECK_RESULT(vpi_get(vpiType, vh176), vpiRegArray);
CHECK_RESULT(vpi_get(vpiSize, vh176), 2);
TestVpiHandle vh177 = vpi_handle_by_index(vh176, 3);
CHECK_RESULT_NZ(vh177);
CHECK_RESULT(vpi_get(vpiType, vh177), vpiStructVar);
TestVpiHandle vh178 = vpi_handle_by_name((PLI_BYTE8*)"child_leaf", vh177);
CHECK_RESULT_NZ(vh178);
CHECK_RESULT(vpi_get(vpiType, vh178), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh178), 8);
putValue.value.integer = 0x5a;
vpi_put_value(vh178, &putValue, NULL, vpiNoDelay);
vpi_get_value(vh178, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x5a);
TestVpiHandle vh179
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(TestSimulator::rooted(
"parent_struct_array[1].children[3].child_leaf")),
nullptr);
CHECK_RESULT_NZ(vh179);
vpi_get_value(vh179, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x5a);
TestVpiHandle vh17d = vpi_handle_by_name((PLI_BYTE8*)"trailing_children", vh172);
CHECK_RESULT_NZ(vh17d);
TestVpiHandle vh17e = vpi_handle_by_index(vh17d, 2);
CHECK_RESULT_NZ(vh17e);
TestVpiHandle vh17f = vpi_handle_by_name((PLI_BYTE8*)"child_leaf", vh17e);
CHECK_RESULT_NZ(vh17f);
putValue.value.integer = 0x6b;
vpi_put_value(vh17f, &putValue, NULL, vpiNoDelay);
vpi_get_value(vh17f, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x6b);
TestVpiHandle vh17g
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(TestSimulator::rooted(
"parent_struct_array[1].trailing_children[2].child_leaf")),
nullptr);
CHECK_RESULT_NZ(vh17g);
vpi_get_value(vh17g, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x6b);
CHECK_RESULT_Z(vpi_handle_by_name(const_cast<PLI_BYTE8*>(TestSimulator::rooted(
"parent_struct_array[1].children[3:2].child_leaf")),
nullptr));
}
// array of unpacked structs with members requiring different C++ alignments
{
TestVpiHandle vh180 = VPI_HANDLE("aligned_struct_array");
CHECK_RESULT_NZ(vh180);
CHECK_RESULT(vpi_get(vpiType, vh180), vpiRegArray);
CHECK_RESULT(vpi_get(vpiSize, vh180), 2);
TestVpiHandle vh181 = vpi_handle_by_index(vh180, 1);
CHECK_RESULT_NZ(vh181);
CHECK_RESULT(vpi_get(vpiType, vh181), vpiStructVar);
TestVpiHandle vh182 = vpi_handle_by_name((PLI_BYTE8*)"word_member", vh181);
CHECK_RESULT_NZ(vh182);
CHECK_RESULT(vpi_get(vpiType, vh182), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh182), 32);
TestVpiHandle vh183 = vpi_handle_by_name((PLI_BYTE8*)"quad_member", vh181);
CHECK_RESULT_NZ(vh183);
CHECK_RESULT(vpi_get(vpiType, vh183), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh183), 64);
TestVpiHandle vh184 = vpi_handle_by_name((PLI_BYTE8*)"real_member", vh181);
CHECK_RESULT_NZ(vh184);
CHECK_RESULT(vpi_get(vpiType, vh184), vpiRealVar);
TestVpiHandle vh185 = vpi_handle_by_name((PLI_BYTE8*)"string_member", vh181);
CHECK_RESULT_NZ(vh185);
CHECK_RESULT(vpi_get(vpiType, vh185), vpiStringVar);
TestVpiHandle vh186 = vpi_handle_by_name((PLI_BYTE8*)"nested_member", vh181);
CHECK_RESULT_NZ(vh186);
CHECK_RESULT(vpi_get(vpiType, vh186), vpiStructVar);
TestVpiHandle vh187 = vpi_handle_by_name((PLI_BYTE8*)"child_leaf", vh186);
CHECK_RESULT_NZ(vh187);
CHECK_RESULT(vpi_get(vpiType, vh187), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh187), 8);
}
// Array stride for a struct whose C++ size needs tail padding after a nested struct
{
TestVpiHandle vh188a = VPI_HANDLE("alignment_stride_array");
CHECK_RESULT_NZ(vh188a);
CHECK_RESULT(vpi_get(vpiType, vh188a), vpiRegArray);
CHECK_RESULT(vpi_get(vpiSize, vh188a), 2);
TestVpiHandle vh188b = vpi_handle_by_index(vh188a, 1);
CHECK_RESULT_NZ(vh188b);
CHECK_RESULT(vpi_get(vpiType, vh188b), vpiStructVar);
TestVpiHandle vh188c = vpi_handle_by_name((PLI_BYTE8*)"tail", vh188b);
CHECK_RESULT_NZ(vh188c);
CHECK_RESULT(vpi_get(vpiType, vh188c), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh188c), 8);
s_vpi_value putValue;
putValue.format = vpiIntVal;
putValue.value.integer = 0xc3;
vpi_put_value(vh188c, &putValue, NULL, vpiNoDelay);
vpi_get_value(vh188c, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0xc3);
}
TestVpiHandle vh188 = VPI_HANDLE("nested_struct_port");
CHECK_RESULT_NZ(vh188);
CHECK_RESULT_Z(vpi_handle_by_name((PLI_BYTE8*)"outer_inner.no_such", vh188));
CHECK_RESULT_Z(vpi_handle_by_name((PLI_BYTE8*)"t.\\no.such escaped.member", nullptr));
// Unpacked struct member with an escaped identifier containing a dot
{
TestVpiHandle vh200 = VPI_HANDLE("escaped_member_struct_signal");
CHECK_RESULT_NZ(vh200);
CHECK_RESULT(vpi_get(vpiType, vh200), vpiStructVar);
TestVpiHandle vh201 = vpi_handle_by_name((PLI_BYTE8*)"\\a.b ", vh200);
CHECK_RESULT_NZ(vh201);
CHECK_RESULT(vpi_get(vpiType, vh201), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh201), 8);
TestVpiHandle vh202
= vpi_handle_by_name((PLI_BYTE8*)"t.escaped_member_struct_signal.\\a.b ", nullptr);
CHECK_RESULT_NZ(vh202);
CHECK_RESULT(vpi_get(vpiType, vh202), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh202), 8);
TestVpiHandle vh203 = vpi_iterate(vpiMember, vh200);
CHECK_RESULT_NZ(vh203);
std::set<std::string> escapedMembers;
while (TestVpiHandle member = vpi_scan(vh203)) {
escapedMembers.insert(vpi_get_str(vpiName, member));
}
vh203.freed(); // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
CHECK_RESULT(escapedMembers.count("\\a.b "), 1);
CHECK_RESULT(escapedMembers.count("plain_after"), 1);
}
return 0;
}
int _mon_check_var() {
TestVpiHandle vh1 = VPI_HANDLE("onebit");
CHECK_RESULT_NZ(vh1);
@ -1543,6 +2165,9 @@ extern "C" int mon_check() {
printf("-mon_check()\n");
#endif
#if !defined(T_VPI_VAR2) && !defined(T_VPI_VAR3)
if (int status = _mon_check_unpacked_struct_members()) return status;
#endif
if (int status = _mon_check_mcd()) return status;
if (int status = _mon_check_callbacks()) return status;
if (int status = _mon_check_value_callbacks()) return status;
@ -1607,6 +2232,7 @@ int main(int argc, char** argv) {
uint64_t sim_time = 1100;
contextp->debug(0);
contextp->commandArgs(argc, argv);
VerilatedVpi::selfTest();
const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{contextp.get(),
// Note null name - we're flattening it out

View File

@ -16,7 +16,9 @@ module t (/*AUTOARG*/
// Outputs
x,
// Inputs
clk, a
clk, a, unpacked_struct_port, unpacked_union_port, nested_struct_port,
struct_array_port, struct_matrix_port, struct_with_packed_arrays_port,
wire_unpacked_struct_port, wire_struct_array_port
);
`ifdef VERILATOR
@ -54,6 +56,99 @@ extern "C" int mon_check();
// verilator lint_on ASCRANGE
reg unpacked_only[7:0];
typedef struct {
logic [6:0] member_a;
logic member_b;
logic [15:0] member_c;
} unpacked_struct_t;
input unpacked_struct_t unpacked_struct_port /*verilator public_flat_rw*/;
input wire unpacked_struct_t wire_unpacked_struct_port /*verilator public_flat_rw*/;
typedef union {
logic [7:0] union_byte0;
logic [7:0] union_byte1;
} unpacked_union_t;
input unpacked_union_t unpacked_union_port /*verilator public_flat_rw*/;
typedef struct {
logic [3:0] inner_x;
logic [3:0] inner_y;
} inner_struct_t;
typedef struct {
logic [7:0] outer_a;
inner_struct_t outer_inner;
} nested_struct_t;
input nested_struct_t nested_struct_port /*verilator public_flat_rw*/;
input unpacked_struct_t struct_array_port [1:0] /*verilator public_flat_rw*/;
input unpacked_struct_t struct_matrix_port [1:0][2:0] /*verilator public_flat_rw*/;
input wire unpacked_struct_t wire_struct_array_port [1:0] /*verilator public_flat_rw*/;
unpacked_struct_t struct_array_signal [1:0] /*verilator public_flat_rw*/;
typedef struct {
logic [6:0] unsigned_member;
logic signed [6:0] signed_member;
bit bit_member;
} member_flags_struct_t;
member_flags_struct_t member_flags_struct_signal /*verilator public_flat_rw*/;
typedef struct {
logic [7:0] child_leaf;
} child_struct_t;
typedef struct {
logic [15:0] scalar;
child_struct_t children [3:2];
logic [7:0] tail_array [2:5];
child_struct_t trailing_children [3:2];
} parent_struct_t;
parent_struct_t parent_struct_array [1:0] /*verilator public_flat_rw*/;
typedef struct {
logic [7:0] \a.b ;
logic [7:0] plain_after;
} escaped_member_struct_t;
escaped_member_struct_t escaped_member_struct_signal /*verilator public_flat_rw*/;
typedef struct {
logic [31:0] word_member;
logic [63:0] quad_member;
real real_member;
string string_member;
child_struct_t nested_member;
} aligned_struct_t;
aligned_struct_t aligned_struct_array [1:0] /*verilator public_flat_rw*/;
typedef struct {
logic [63:0] quad_leaf;
logic [7:0] byte_tail;
} stride_inner_struct_t;
typedef struct {
logic [7:0] lead;
stride_inner_struct_t nested;
logic [7:0] tail;
} stride_outer_struct_t;
stride_outer_struct_t alignment_stride_array [1:0] /*verilator public_flat_rw*/;
// verilator lint_off ASCRANGE
typedef struct {
logic [0:15][0:3][7:0] packed_matrix;
logic [8:-7][3:-4] reverse_matrix;
} struct_with_packed_arrays_t;
// verilator lint_on ASCRANGE
input struct_with_packed_arrays_t struct_with_packed_arrays_port /*verilator public_flat_rw*/;
reg [7:0] text_byte /*verilator public_flat_rw @(posedge clk) */;
reg [15:0] text_half /*verilator public_flat_rw @(posedge clk) */;
reg [31:0] text_word /*verilator public_flat_rw @(posedge clk) */;
@ -166,6 +261,7 @@ extern "C" int mon_check();
if (text != "lorem ipsum") $stop;
if (str1 != "something a lot longer than hello") $stop;
if (real1 > 123456.7895 || real1 < 123456.7885 ) $stop;
if (alignment_stride_array[1].tail != 8'hc3) $stop;
end
always @(posedge clk) begin