Support VPI access to unpacked struct members (#7823)
This commit is contained in:
parent
c76c94ef16
commit
6fbc7042a5
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue