Support VPI access to unpacked struct members
This commit is contained in:
parent
9670dabcfe
commit
5a3998b8ff
|
|
@ -3920,6 +3920,7 @@ std::unique_ptr<VerilatedTraceConfig> VerilatedModel::traceConfig() const { retu
|
||||||
|
|
||||||
// cppcheck-suppress unusedFunction // Used by applications
|
// cppcheck-suppress unusedFunction // Used by applications
|
||||||
uint32_t VerilatedVarProps::entSize() const VL_MT_SAFE {
|
uint32_t VerilatedVarProps::entSize() const VL_MT_SAFE {
|
||||||
|
if (m_entSize) return m_entSize;
|
||||||
uint32_t size = 1;
|
uint32_t size = 1;
|
||||||
switch (vltype()) {
|
switch (vltype()) {
|
||||||
case VLVT_PTR: size = sizeof(void*); break;
|
case VLVT_PTR: size = sizeof(void*); break;
|
||||||
|
|
@ -3928,6 +3929,8 @@ uint32_t VerilatedVarProps::entSize() const VL_MT_SAFE {
|
||||||
case VLVT_UINT32: size = sizeof(IData); break;
|
case VLVT_UINT32: size = sizeof(IData); break;
|
||||||
case VLVT_UINT64: size = sizeof(QData); break;
|
case VLVT_UINT64: size = sizeof(QData); break;
|
||||||
case VLVT_WDATA: size = VL_WORDS_I(entBits()) * sizeof(IData); break;
|
case VLVT_WDATA: size = VL_WORDS_I(entBits()) * sizeof(IData); break;
|
||||||
|
case VLVT_STRUCT: size = 0; break;
|
||||||
|
case VLVT_UNION: size = 0; break;
|
||||||
default: size = 0; break; // LCOV_EXCL_LINE
|
default: size = 0; break; // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
|
|
@ -4036,6 +4039,33 @@ VerilatedVar* VerilatedScope::varInsert(const char* namep, void* datap, bool isP
|
||||||
return &(m_varsp->find(namep)->second);
|
return &(m_varsp->find(namep)->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VerilatedVar* VerilatedScope::varInsertSized(const char* namep, void* datap, bool isParam,
|
||||||
|
VerilatedVarType vltype, int vlflags, int udims,
|
||||||
|
int pdims, uint32_t entSize...) VL_MT_UNSAFE {
|
||||||
|
if (!m_varsp) m_varsp = new VerilatedVarNameMap;
|
||||||
|
VerilatedVar var(namep, datap, vltype, static_cast<VerilatedVarFlags>(vlflags), udims, pdims,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < pdims; ++i) {
|
||||||
|
const int msb = va_arg(ap, int);
|
||||||
|
const int lsb = va_arg(ap, int);
|
||||||
|
var.m_packed[i].m_left = msb;
|
||||||
|
var.m_packed[i].m_right = lsb;
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
m_varsp->emplace(namep, std::move(var));
|
||||||
|
return &(m_varsp->find(namep)->second);
|
||||||
|
}
|
||||||
|
|
||||||
VerilatedVar*
|
VerilatedVar*
|
||||||
VerilatedScope::forceableVarInsert(const char* namep, void* datap, bool isParam,
|
VerilatedScope::forceableVarInsert(const char* namep, void* datap, bool isParam,
|
||||||
VerilatedVarType vltype, int vlflags, void* forceReadSignalData,
|
VerilatedVarType vltype, int vlflags, void* forceReadSignalData,
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,9 @@ enum VerilatedVarType : uint8_t {
|
||||||
VLVT_UINT64, // AKA QData
|
VLVT_UINT64, // AKA QData
|
||||||
VLVT_WDATA, // AKA VlWide
|
VLVT_WDATA, // AKA VlWide
|
||||||
VLVT_STRING, // C++ string
|
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 {
|
enum VerilatedVarFlags : uint32_t {
|
||||||
|
|
@ -154,7 +156,8 @@ enum VerilatedVarFlags : uint32_t {
|
||||||
VLVF_CONTINUOUSLY = (1 << 11), // Is continously assigned
|
VLVF_CONTINUOUSLY = (1 << 11), // Is continously assigned
|
||||||
VLVF_FORCEABLE = (1 << 12), // Forceable
|
VLVF_FORCEABLE = (1 << 12), // Forceable
|
||||||
VLVF_SIGNED = (1 << 13), // Signed integer
|
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
|
// IEEE 1800-2023 Table 20-6
|
||||||
|
|
@ -762,6 +765,9 @@ public: // But internals only - called from verilated modules, VerilatedSyms
|
||||||
void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE;
|
void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE;
|
||||||
VerilatedVar* varInsert(const char* namep, void* datap, bool isParam, VerilatedVarType vltype,
|
VerilatedVar* varInsert(const char* namep, void* datap, bool isParam, VerilatedVarType vltype,
|
||||||
int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE;
|
int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE;
|
||||||
|
VerilatedVar* varInsertSized(const char* namep, void* datap, bool isParam,
|
||||||
|
VerilatedVarType vltype, int vlflags, int udims, int pdims,
|
||||||
|
uint32_t entSize, ...) VL_MT_UNSAFE;
|
||||||
VerilatedVar* forceableVarInsert(const char* namep, void* datap, bool isParam,
|
VerilatedVar* forceableVarInsert(const char* namep, void* datap, bool isParam,
|
||||||
VerilatedVarType vltype, int vlflags,
|
VerilatedVarType vltype, int vlflags,
|
||||||
void* forceReadSignalData, const char* forceReadSignalName,
|
void* forceReadSignalData, const char* forceReadSignalName,
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ class VerilatedVarProps VL_NOT_FINAL {
|
||||||
const uint32_t m_magic; // Magic number
|
const uint32_t m_magic; // Magic number
|
||||||
const VerilatedVarType m_vltype; // Data type
|
const VerilatedVarType m_vltype; // Data type
|
||||||
const VerilatedVarFlags m_vlflags; // Direction
|
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_unpacked; // Unpacked array ranges
|
||||||
std::vector<VerilatedRange> m_packed; // Packed array ranges
|
std::vector<VerilatedRange> m_packed; // Packed array ranges
|
||||||
VerilatedRange m_packedDpi; // Flattened packed array range
|
VerilatedRange m_packedDpi; // Flattened packed array range
|
||||||
|
|
@ -104,10 +105,12 @@ class VerilatedVarProps VL_NOT_FINAL {
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
protected:
|
protected:
|
||||||
friend class VerilatedScope;
|
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_magic{MAGIC}
|
||||||
, m_vltype{vltype}
|
, m_vltype{vltype}
|
||||||
, m_vlflags{vlflags} {
|
, m_vlflags{vlflags}
|
||||||
|
, m_entSize{entSize} {
|
||||||
// Only preallocate the ranges
|
// Only preallocate the ranges
|
||||||
initUnpacked(udims, nullptr);
|
initUnpacked(udims, nullptr);
|
||||||
initPacked(pdims, nullptr);
|
initPacked(pdims, nullptr);
|
||||||
|
|
@ -119,12 +122,14 @@ public:
|
||||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags)
|
VerilatedVarProps(VerilatedVarType vltype, int vlflags)
|
||||||
: m_magic{MAGIC}
|
: m_magic{MAGIC}
|
||||||
, m_vltype{vltype}
|
, 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)
|
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims)
|
||||||
: m_magic{MAGIC}
|
: m_magic{MAGIC}
|
||||||
, m_vltype{vltype}
|
, 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);
|
initUnpacked(udims, ulims);
|
||||||
}
|
}
|
||||||
// With packed
|
// With packed
|
||||||
|
|
@ -132,14 +137,16 @@ public:
|
||||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pdims, const int* plims)
|
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pdims, const int* plims)
|
||||||
: m_magic{MAGIC}
|
: m_magic{MAGIC}
|
||||||
, m_vltype{vltype}
|
, 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);
|
initPacked(pdims, plims);
|
||||||
}
|
}
|
||||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims,
|
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims,
|
||||||
Packed, int pdims, const int* plims)
|
Packed, int pdims, const int* plims)
|
||||||
: m_magic{MAGIC}
|
: m_magic{MAGIC}
|
||||||
, m_vltype{vltype}
|
, 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);
|
initUnpacked(udims, ulims);
|
||||||
initPacked(pdims, plims);
|
initPacked(pdims, plims);
|
||||||
}
|
}
|
||||||
|
|
@ -164,6 +171,7 @@ public:
|
||||||
bool isDpiCLayout() const { return ((m_vlflags & VLVF_DPI_CLAY) != 0); }
|
bool isDpiCLayout() const { return ((m_vlflags & VLVF_DPI_CLAY) != 0); }
|
||||||
bool isSigned() const { return ((m_vlflags & VLVF_SIGNED) != 0); }
|
bool isSigned() const { return ((m_vlflags & VLVF_SIGNED) != 0); }
|
||||||
bool isBitVar() const { return ((m_vlflags & VLVF_BITVAR) != 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 udims() const VL_MT_SAFE { return m_unpacked.size(); }
|
||||||
int pdims() const VL_MT_SAFE { return m_packed.size(); }
|
int pdims() const VL_MT_SAFE { return m_packed.size(); }
|
||||||
int dims() const VL_MT_SAFE { return pdims() + udims(); }
|
int dims() const VL_MT_SAFE { return pdims() + udims(); }
|
||||||
|
|
@ -265,6 +273,9 @@ protected:
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
|
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
|
||||||
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam);
|
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,
|
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
|
||||||
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam,
|
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam,
|
||||||
std::unique_ptr<const VerilatedForceControlSignals> forceControlSignals);
|
std::unique_ptr<const VerilatedForceControlSignals> forceControlSignals);
|
||||||
|
|
@ -296,6 +307,13 @@ inline VerilatedVar::VerilatedVar(const char* namep, void* datap, VerilatedVarTy
|
||||||
, m_datap{datap}
|
, m_datap{datap}
|
||||||
, m_namep{namep}
|
, m_namep{namep}
|
||||||
, m_isParam{isParam} {}
|
, 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(
|
inline VerilatedVar::VerilatedVar(
|
||||||
const char* namep, void* datap, VerilatedVarType vltype, VerilatedVarFlags vlflags, int udims,
|
const char* namep, void* datap, VerilatedVarType vltype, VerilatedVarFlags vlflags, int udims,
|
||||||
int pdims, bool isParam,
|
int pdims, bool isParam,
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
@ -68,6 +69,25 @@ constexpr unsigned VL_VPI_LINE_SIZE_ = 8192;
|
||||||
//======================================================================
|
//======================================================================
|
||||||
// Implementation
|
// 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
|
// Base VPI handled object
|
||||||
class VerilatedVpio VL_NOT_FINAL {
|
class VerilatedVpio VL_NOT_FINAL {
|
||||||
// CONSTANTS
|
// CONSTANTS
|
||||||
|
|
@ -201,6 +221,9 @@ public:
|
||||||
}
|
}
|
||||||
const VerilatedVar* varp() const { return m_varp; }
|
const VerilatedVar* varp() const { return m_varp; }
|
||||||
const VerilatedScope* scopep() const { return m_scopep; }
|
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).
|
// Returns the number of the currently indexed dimension (starting at -1 for none).
|
||||||
int32_t indexedDim() const { return m_indexedDim; }
|
int32_t indexedDim() const { return m_indexedDim; }
|
||||||
// Returns whether the currently indexed dimension is unpacked.
|
// 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_entSize = 0; // memoized variable size
|
||||||
uint32_t m_bitOffset = 0;
|
uint32_t m_bitOffset = 0;
|
||||||
int32_t m_partselBits = -1; // Part-select width, -1 means no part-select active
|
int32_t m_partselBits = -1; // Part-select width, -1 means no part-select active
|
||||||
|
std::string m_name;
|
||||||
|
std::string m_fullNameOverride;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void* m_varDatap = nullptr; // varp()->datap() adjusted for array entries
|
void* m_varDatap = nullptr; // varp()->datap() adjusted for array entries
|
||||||
|
|
@ -373,6 +398,15 @@ public:
|
||||||
: VerilatedVpioVarBase{varp, scopep} {
|
: VerilatedVpioVarBase{varp, scopep} {
|
||||||
m_entSize = varp->entSize();
|
m_entSize = varp->entSize();
|
||||||
m_varDatap = varp->datap();
|
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)
|
explicit VerilatedVpioVar(const VerilatedVpioVar* vop)
|
||||||
: VerilatedVpioVarBase{vop} {
|
: VerilatedVpioVarBase{vop} {
|
||||||
|
|
@ -380,6 +414,8 @@ public:
|
||||||
m_entSize = vop->m_entSize;
|
m_entSize = vop->m_entSize;
|
||||||
m_varDatap = vop->m_varDatap;
|
m_varDatap = vop->m_varDatap;
|
||||||
m_index = vop->m_index;
|
m_index = vop->m_index;
|
||||||
|
m_name = vop->m_name;
|
||||||
|
m_fullNameOverride = vop->m_fullNameOverride;
|
||||||
m_partselBits = vop->m_partselBits;
|
m_partselBits = vop->m_partselBits;
|
||||||
m_bitOffset = vop->m_bitOffset;
|
m_bitOffset = vop->m_bitOffset;
|
||||||
// Not copying m_prevDatap, must be nullptr
|
// Not copying m_prevDatap, must be nullptr
|
||||||
|
|
@ -395,15 +431,22 @@ public:
|
||||||
uint32_t bitOffset() const override { return m_bitOffset; }
|
uint32_t bitOffset() const override { return m_bitOffset; }
|
||||||
int32_t partselBits() const { return m_partselBits; }
|
int32_t partselBits() const { return m_partselBits; }
|
||||||
uint32_t bitSize() const {
|
uint32_t bitSize() const {
|
||||||
|
if (isStructOrUnion() && !isIndexedDimUnpacked()) return 0;
|
||||||
if (m_partselBits >= 0) return static_cast<uint32_t>(m_partselBits);
|
if (m_partselBits >= 0) return static_cast<uint32_t>(m_partselBits);
|
||||||
return VerilatedVpioVarBase::bitSize();
|
return VerilatedVpioVarBase::bitSize();
|
||||||
}
|
}
|
||||||
uint32_t size() const override {
|
uint32_t size() const override {
|
||||||
|
if (isStructOrUnion() && !isIndexedDimUnpacked()) return 0;
|
||||||
if (m_partselBits >= 0) return static_cast<uint32_t>(m_partselBits);
|
if (m_partselBits >= 0) return static_cast<uint32_t>(m_partselBits);
|
||||||
return VerilatedVpioVarBase::size();
|
return VerilatedVpioVarBase::size();
|
||||||
}
|
}
|
||||||
|
const VerilatedRange* rangep() const override {
|
||||||
|
if (isStructOrUnion() && !isIndexedDimUnpacked()) return nullptr;
|
||||||
|
return VerilatedVpioVarBase::rangep();
|
||||||
|
}
|
||||||
uint32_t entSize() const { return m_entSize; }
|
uint32_t entSize() const { return m_entSize; }
|
||||||
const std::vector<int32_t>& index() const { return m_index; }
|
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].
|
// Create a part-selected view of this variable with the given bit range [hi:lo].
|
||||||
VerilatedVpioVar* withPartSelect(int32_t hi, int32_t lo) const {
|
VerilatedVpioVar* withPartSelect(int32_t hi, int32_t lo) const {
|
||||||
if (isIndexedDimUnpacked()) return nullptr;
|
if (isIndexedDimUnpacked()) return nullptr;
|
||||||
|
|
@ -456,22 +499,46 @@ public:
|
||||||
|
|
||||||
return ret;
|
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);
|
||||||
|
if (memberName.size() <= parentLen || memberName.compare(0, parentLen, parentName) != 0
|
||||||
|
|| memberName[parentLen] != '.')
|
||||||
|
return nullptr; // LCOV_EXCL_LINE: Defensive; callers pass exact synthetic members
|
||||||
|
|
||||||
|
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() const override {
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
// TODO have V3EmitCSyms.cpp put vpiType directly into constant table
|
// TODO have V3EmitCSyms.cpp put vpiType directly into constant table
|
||||||
switch (varp()->vltype()) {
|
switch (varp()->vltype()) {
|
||||||
case VLVT_REAL: type = vpiRealVar; break;
|
case VLVT_REAL: type = vpiRealVar; break;
|
||||||
case VLVT_STRING: type = vpiStringVar; 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;
|
default: type = varp()->isBitVar() ? vpiBitVar : vpiReg; break;
|
||||||
}
|
}
|
||||||
if (isIndexedDimUnpacked()) return vpiRegArray;
|
if (isIndexedDimUnpacked())
|
||||||
|
return isStructOrUnion() && varp()->isNet() ? vpiNetArray : vpiRegArray;
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
const char* fullname() const override {
|
const char* fullname() const override {
|
||||||
static thread_local std::string t_out;
|
m_fullname = m_fullNameOverride.empty() ? std::string{scopep()->name()} + "." + varp()->name()
|
||||||
t_out = std::string{scopep()->name()} + "." + name();
|
: m_fullNameOverride;
|
||||||
for (auto idx : index()) t_out += "[" + std::to_string(idx) + "]";
|
for (auto idx : index()) m_fullname += "[" + std::to_string(idx) + "]";
|
||||||
return t_out.c_str();
|
return m_fullname.c_str();
|
||||||
}
|
}
|
||||||
void* prevDatap() const { return m_prevDatap; }
|
void* prevDatap() const { return m_prevDatap; }
|
||||||
void* varDatap() const override { return m_varDatap; }
|
void* varDatap() const override { return m_varDatap; }
|
||||||
|
|
@ -590,25 +657,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 {
|
class VerilatedVpioModule final : public VerilatedVpioScope {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit VerilatedVpioModule(const VerilatedScope* modulep)
|
explicit VerilatedVpioModule(const VerilatedScope* modulep)
|
||||||
: VerilatedVpioScope{modulep} {
|
: VerilatedVpioScope{modulep} {
|
||||||
// Look for '.' not inside escaped identifier
|
// Look for '.' not inside escaped identifier
|
||||||
const std::string scopename = m_fullname;
|
if (VL_UNLIKELY(!vl_vpi_find_unescaped_dot(m_fullname))) m_toplevel = true;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
// cppcheck-suppress duplInheritedMember
|
// cppcheck-suppress duplInheritedMember
|
||||||
static VerilatedVpioModule* castp(vpiHandle h) {
|
static VerilatedVpioModule* castp(vpiHandle h) {
|
||||||
|
|
@ -1770,6 +1875,8 @@ const char* VerilatedVpiError::strFromVpiObjType(PLI_INT32 vpiVal) VL_PURE {
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
if (VL_UNCOVERABLE(vpiVal < 0)) return names[0];
|
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 <= vpiAutomatics) return names[vpiVal];
|
||||||
if (vpiVal >= vpiPackage && vpiVal <= vpiPropFormalDecl)
|
if (vpiVal >= vpiPackage && vpiVal <= vpiPropFormalDecl)
|
||||||
return sv_names1[(vpiVal - vpiPackage)];
|
return sv_names1[(vpiVal - vpiPackage)];
|
||||||
|
|
@ -2135,6 +2242,7 @@ void VerilatedVpiError::selfTest() VL_MT_UNSAFE_ONE {
|
||||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumVar);
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumVar);
|
||||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructVar);
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructVar);
|
||||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionVar);
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionVar);
|
||||||
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionNet);
|
||||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitVar);
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitVar);
|
||||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassObj);
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassObj);
|
||||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiChandleVar);
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiChandleVar);
|
||||||
|
|
@ -2404,6 +2512,162 @@ static bool vl_vpi_parse_indices(std::string& name, std::vector<PLI_INT32>& indi
|
||||||
return true;
|
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 std::vector<std::string> vl_vpi_split_dotted_name(const std::string& name) {
|
||||||
|
std::vector<std::string> parts;
|
||||||
|
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);
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
const std::vector<std::string> parts = vl_vpi_split_dotted_name(scopeAndName);
|
||||||
|
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
|
// for obtaining handles
|
||||||
|
|
||||||
vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||||
|
|
@ -2425,7 +2689,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||||
|
|
||||||
const VerilatedVar* varp = nullptr;
|
const VerilatedVar* varp = nullptr;
|
||||||
const VerilatedScope* scopep;
|
const VerilatedScope* scopep;
|
||||||
|
std::string fullnameOverride;
|
||||||
const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
|
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."))) {
|
if (0 == std::strncmp(scopeAndName.c_str(), "$root.", std::strlen("$root."))) {
|
||||||
scopeAndName.erase(0, std::strlen("$root."));
|
scopeAndName.erase(0, std::strlen("$root."));
|
||||||
|
|
@ -2433,6 +2699,11 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||||
const bool scopeIsPackage = VerilatedVpioPackage::castp(scope) != nullptr;
|
const bool scopeIsPackage = VerilatedVpioPackage::castp(scope) != nullptr;
|
||||||
scopeAndName
|
scopeAndName
|
||||||
= std::string{voScopep->fullname()} + (scopeIsPackage ? "" : ".") + 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
|
// 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) {
|
if (!varp) {
|
||||||
scopep = Verilated::threadContextp()->scopeFind(scopename.c_str());
|
scopep = Verilated::threadContextp()->scopeFind(scopename.c_str());
|
||||||
if (!scopep) return nullptr;
|
if (scopep) { varp = scopep->varFind(basename.c_str()); }
|
||||||
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;
|
if (!varp) return nullptr;
|
||||||
|
|
@ -2501,6 +2782,11 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||||
vpiHandle resultHandle;
|
vpiHandle resultHandle;
|
||||||
if (varp->isParam()) {
|
if (varp->isParam()) {
|
||||||
resultHandle = (new VerilatedVpioParam{varp, scopep})->castVpiHandle();
|
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 {
|
} else {
|
||||||
resultHandle = (new VerilatedVpioVar{varp, scopep})->castVpiHandle();
|
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());
|
if (vop) return ((new VerilatedVpioRegIter{vop})->castVpiHandle());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
case vpiMember: {
|
||||||
|
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
||||||
|
if (!vop || !vop->isStructOrUnion()) return nullptr;
|
||||||
|
return ((new VerilatedVpioMemberIter{vop})->castVpiHandle());
|
||||||
|
}
|
||||||
case vpiParameter: {
|
case vpiParameter: {
|
||||||
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
||||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||||
|
|
@ -2734,11 +3025,17 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
|
||||||
if (VL_UNLIKELY(!vop)) return vpiUndefined;
|
if (VL_UNLIKELY(!vop)) return vpiUndefined;
|
||||||
return vop->varp()->isSigned();
|
return vop->varp()->isSigned();
|
||||||
}
|
}
|
||||||
default:
|
case vpiPacked: {
|
||||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported property %s, nothing will be returned",
|
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
|
||||||
__func__, VerilatedVpiError::strFromVpiProp(property));
|
if (VL_LIKELY(vop && vop->isStructOrUnion())) return 0;
|
||||||
return vpiUndefined;
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported property %s, nothing will be returned",
|
||||||
|
__func__, VerilatedVpiError::strFromVpiProp(property));
|
||||||
|
return vpiUndefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
PLI_INT64 vpi_get64(PLI_INT32 /*property*/, vpiHandle /*object*/) {
|
PLI_INT64 vpi_get64(PLI_INT32 /*property*/, vpiHandle /*object*/) {
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,7 @@ public:
|
||||||
bool isStreamableFixedAggregate() const;
|
bool isStreamableFixedAggregate() const;
|
||||||
bool containsUnpackedStruct() const;
|
bool containsUnpackedStruct() const;
|
||||||
int widthStream() const;
|
int widthStream() const;
|
||||||
|
string vlEnumType() const; // Return VerilatedVarType: VLVT_UINT32, etc
|
||||||
static int uniqueNumInc() { return ++s_uniqueNum; }
|
static int uniqueNumInc() { return ++s_uniqueNum; }
|
||||||
const char* charIQWN() const {
|
const char* charIQWN() const {
|
||||||
return (isString() ? "N" : isWide() ? "W" : isDouble() ? "D" : isQuad() ? "Q" : "I");
|
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);
|
return ostatic + dtypep()->cType(oname, forFunc, asRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
string AstVar::vlEnumType() const {
|
string AstNodeDType::vlEnumType() const {
|
||||||
string arg;
|
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;
|
const bool strtype = bdtypep && bdtypep->keyword() == VBasicDTypeKwd::STRING;
|
||||||
if (bdtypep && bdtypep->keyword() == VBasicDTypeKwd::CHARPTR) {
|
if (bdtypep && bdtypep->keyword() == VBasicDTypeKwd::CHARPTR) {
|
||||||
return "VLVT_PTR";
|
return "VLVT_PTR";
|
||||||
|
|
@ -715,6 +720,8 @@ string AstVar::vlEnumType() const {
|
||||||
arg += "VLVT_STRING";
|
arg += "VLVT_STRING";
|
||||||
} else if (isDouble()) {
|
} else if (isDouble()) {
|
||||||
arg += "VLVT_REAL";
|
arg += "VLVT_REAL";
|
||||||
|
} else if (sdtypep && !sdtypep->packed()) {
|
||||||
|
arg += VN_IS(sdtypep, StructDType) ? "VLVT_STRUCT" : "VLVT_UNION";
|
||||||
} else if (widthMin() <= 8) {
|
} else if (widthMin() <= 8) {
|
||||||
arg += "VLVT_UINT8";
|
arg += "VLVT_UINT8";
|
||||||
} else if (widthMin() <= 16) {
|
} else if (widthMin() <= 16) {
|
||||||
|
|
@ -730,6 +737,8 @@ string AstVar::vlEnumType() const {
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string AstVar::vlEnumType() const { return dtypep()->vlEnumType(); }
|
||||||
|
|
||||||
string AstVar::vlEnumDir() const {
|
string AstVar::vlEnumDir() const {
|
||||||
string out;
|
string out;
|
||||||
if (isInout()) {
|
if (isInout()) {
|
||||||
|
|
@ -759,6 +768,7 @@ string AstVar::vlEnumDir() const {
|
||||||
if (AstBasicDType* const basicp = dtypep()->skipRefp()->basicp()) {
|
if (AstBasicDType* const basicp = dtypep()->skipRefp()->basicp()) {
|
||||||
if (basicp->keyword() == VBasicDTypeKwd::BIT) out += "|VLVF_BITVAR";
|
if (basicp->keyword() == VBasicDTypeKwd::BIT) out += "|VLVF_BITVAR";
|
||||||
}
|
}
|
||||||
|
if (isNet()) out += "|VLVF_NET";
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -176,26 +176,26 @@ class EmitCSyms final : EmitCBaseVisitorConst {
|
||||||
return pos != std::string::npos ? scpname.substr(pos + 1) : scpname;
|
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 pdim = 0;
|
||||||
int udim = 0;
|
int udim = 0;
|
||||||
std::string bounds;
|
std::string bounds;
|
||||||
if (const AstBasicDType* const basicp = varp->basicp()) {
|
// Range is always first, it's not in "C" order
|
||||||
// Range is always first, it's not in "C" order
|
for (const AstNodeDType* dtypep = rootDtypep; dtypep;) {
|
||||||
for (AstNodeDType* dtypep = varp->dtypep(); dtypep;) {
|
// Skip AstRefDType/AstTypedef, or return same node
|
||||||
// Skip AstRefDType/AstTypedef, or return same node
|
dtypep = dtypep->skipRefp();
|
||||||
dtypep = dtypep->skipRefp();
|
if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
||||||
if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
bounds += " ,";
|
||||||
bounds += " ,";
|
bounds += std::to_string(adtypep->left());
|
||||||
bounds += std::to_string(adtypep->left());
|
bounds += ",";
|
||||||
bounds += ",";
|
bounds += std::to_string(adtypep->right());
|
||||||
bounds += std::to_string(adtypep->right());
|
if (VN_IS(dtypep, PackArrayDType))
|
||||||
if (VN_IS(dtypep, PackArrayDType))
|
pdim++;
|
||||||
pdim++;
|
else
|
||||||
else
|
udim++;
|
||||||
udim++;
|
dtypep = adtypep->subDTypep();
|
||||||
dtypep = adtypep->subDTypep();
|
} else {
|
||||||
} else {
|
if (const AstBasicDType* const basicp = dtypep->basicp()) {
|
||||||
if (basicp->isRanged()) {
|
if (basicp->isRanged()) {
|
||||||
bounds += " ,";
|
bounds += " ,";
|
||||||
bounds += std::to_string(basicp->left());
|
bounds += std::to_string(basicp->left());
|
||||||
|
|
@ -203,13 +203,33 @@ class EmitCSyms final : EmitCBaseVisitorConst {
|
||||||
bounds += std::to_string(basicp->right());
|
bounds += std::to_string(basicp->right());
|
||||||
pdim++;
|
pdim++;
|
||||||
}
|
}
|
||||||
break; // AstBasicDType - nothing below, 1
|
|
||||||
}
|
}
|
||||||
|
break; // Non-array leaf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {pdim, udim, bounds};
|
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 *= std::abs(adtypep->left() - adtypep->right()) + 1;
|
||||||
|
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) {
|
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,
|
// __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.
|
// it should be skipped because forceableVarInsert creates its VerilatedVar.
|
||||||
|
|
@ -225,15 +245,47 @@ class EmitCSyms final : EmitCBaseVisitorConst {
|
||||||
return std::pair<bool, std::string>{false, ""};
|
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);
|
||||||
|
stmt += ", ";
|
||||||
|
stmt += std::to_string(pdim);
|
||||||
|
if (!entSize.empty()) {
|
||||||
|
stmt += ", ";
|
||||||
|
stmt += entSize;
|
||||||
|
}
|
||||||
|
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,
|
static std::string insertVarStatement(const ScopeVarData& svd, const AstScope* const scopep,
|
||||||
const AstVar* const varp, const int udim, const int pdim,
|
const AstVar* const varp, const int udim, const int pdim,
|
||||||
const std::string& bounds) {
|
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())
|
const std::string varName = VIdProtect::protectIf(scopep->nameDotless(), scopep->protect())
|
||||||
+ "." + protect(varp->name());
|
+ "." + 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()) {
|
if (!varp->isParam()) {
|
||||||
stmt += ", &(";
|
stmt += ", &(";
|
||||||
|
|
@ -250,18 +302,86 @@ class EmitCSyms final : EmitCBaseVisitorConst {
|
||||||
stmt += "))), true, ";
|
stmt += "))), true, ";
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt += varp->vlEnumType(); // VLVT_UINT32 etc
|
const std::string entSize = needsEntSize ? "sizeof(" + varName + ") / "
|
||||||
stmt += ", ";
|
+ std::to_string(getUnpackedElements(
|
||||||
stmt += varp->vlEnumDir(); // VLVD_IN etc
|
varp->dtypep()))
|
||||||
stmt += ", ";
|
: "";
|
||||||
stmt += std::to_string(udim);
|
appendVarProperties(stmt, vlEnumType, varp->vlEnumDir(), udim, pdim, bounds, entSize);
|
||||||
stmt += ", ";
|
|
||||||
stmt += std::to_string(pdim);
|
|
||||||
stmt += bounds;
|
|
||||||
stmt += ")";
|
|
||||||
return stmt;
|
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,
|
std::string insertForceableVarStatement(const ScopeVarData& svd, const AstScope* const scopep,
|
||||||
const AstVar* const varp, const int udim,
|
const AstVar* const varp, const int udim,
|
||||||
const int pdim, const std::string& bounds) {
|
const int pdim, const std::string& bounds) {
|
||||||
|
|
@ -1061,6 +1181,16 @@ std::vector<std::string> EmitCSyms::getSymCtorStmts() {
|
||||||
const std::string stmt
|
const std::string stmt
|
||||||
= insertVarStatement(svd, scopep, varp, udim, pdim, bounds) + ";";
|
= insertVarStatement(svd, scopep, varp, udim, pdim, bounds) + ";";
|
||||||
add(stmt);
|
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
|
#endif
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#ifdef VERILATOR
|
#ifdef VERILATOR
|
||||||
#include "verilated.h"
|
#include "verilated.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -258,6 +260,626 @@ int _mon_check_big() {
|
||||||
return 0;
|
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, 2);
|
||||||
|
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, 0);
|
||||||
|
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, 1);
|
||||||
|
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[1].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, 1);
|
||||||
|
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[1].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[1:0].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() {
|
int _mon_check_var() {
|
||||||
TestVpiHandle vh1 = VPI_HANDLE("onebit");
|
TestVpiHandle vh1 = VPI_HANDLE("onebit");
|
||||||
CHECK_RESULT_NZ(vh1);
|
CHECK_RESULT_NZ(vh1);
|
||||||
|
|
@ -1543,6 +2165,9 @@ extern "C" int mon_check() {
|
||||||
printf("-mon_check()\n");
|
printf("-mon_check()\n");
|
||||||
#endif
|
#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_mcd()) return status;
|
||||||
if (int status = _mon_check_callbacks()) return status;
|
if (int status = _mon_check_callbacks()) return status;
|
||||||
if (int status = _mon_check_value_callbacks()) return status;
|
if (int status = _mon_check_value_callbacks()) return status;
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,9 @@ module t (/*AUTOARG*/
|
||||||
// Outputs
|
// Outputs
|
||||||
x,
|
x,
|
||||||
// Inputs
|
// 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
|
`ifdef VERILATOR
|
||||||
|
|
@ -54,6 +56,99 @@ extern "C" int mon_check();
|
||||||
// verilator lint_on ASCRANGE
|
// verilator lint_on ASCRANGE
|
||||||
reg unpacked_only[7:0];
|
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 [1:0];
|
||||||
|
logic [7:0] tail_array [3:0];
|
||||||
|
child_struct_t trailing_children [1:0];
|
||||||
|
} 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 [7:0] text_byte /*verilator public_flat_rw @(posedge clk) */;
|
||||||
reg [15:0] text_half /*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) */;
|
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 (text != "lorem ipsum") $stop;
|
||||||
if (str1 != "something a lot longer than hello") $stop;
|
if (str1 != "something a lot longer than hello") $stop;
|
||||||
if (real1 > 123456.7895 || real1 < 123456.7885 ) $stop;
|
if (real1 > 123456.7895 || real1 < 123456.7885 ) $stop;
|
||||||
|
if (alignment_stride_array[1].tail != 8'hc3) $stop;
|
||||||
end
|
end
|
||||||
|
|
||||||
always @(posedge clk) begin
|
always @(posedge clk) begin
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue