parent
5e1fc6e24d
commit
5f1dc73a1b
|
|
@ -103,6 +103,7 @@ Justin Thiel
|
|||
Kaleb Barrett
|
||||
Kamil Rakoczy
|
||||
Kanad Kanhere
|
||||
Kefa Chen
|
||||
Keith Colbert
|
||||
Kevin Kiningham
|
||||
Kritik Bhimani
|
||||
|
|
|
|||
|
|
@ -1309,6 +1309,26 @@ static inline void _vl_insert_WI(WDataOutP iowp, IData ld, int hbit, int lbit,
|
|||
}
|
||||
}
|
||||
|
||||
// Copy bits from lwp[hbit:lbit] to low bits of lhsr. rbits is real width of lshr
|
||||
static inline void _vl_insert_IW(IData& lhsr, WDataInP const lwp, int hbit, int lbit,
|
||||
int rbits = 0) VL_MT_SAFE {
|
||||
const int hoffset = VL_BITBIT_E(hbit);
|
||||
const int loffset = VL_BITBIT_E(lbit);
|
||||
const int hword = VL_BITWORD_E(hbit);
|
||||
const int lword = VL_BITWORD_E(lbit);
|
||||
const IData cleanmask = VL_MASK_I(rbits);
|
||||
if (hword == lword) {
|
||||
const IData insmask = (VL_MASK_I(hoffset - loffset + 1));
|
||||
lhsr = (lhsr & ~insmask) | ((lwp[lword] >> loffset) & (insmask & cleanmask));
|
||||
} else {
|
||||
const int nbitsonright = VL_IDATASIZE - loffset; // bits that filled by lword
|
||||
const IData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << nbitsonright;
|
||||
const IData linsmask = VL_MASK_E(VL_EDATASIZE - loffset);
|
||||
lhsr = (lhsr & ~linsmask) | ((lwp[lword] >> loffset) & (linsmask & cleanmask));
|
||||
lhsr = (lhsr & ~hinsmask) | ((lwp[hword] << nbitsonright) & (hinsmask & cleanmask));
|
||||
}
|
||||
}
|
||||
|
||||
// INTERNAL: Stuff large LHS bit 0++ into OUTPUT at specified offset
|
||||
// lwp may be "dirty"
|
||||
static inline void _vl_insert_WW(WDataOutP iowp, WDataInP const lwp, int hbit, int lbit,
|
||||
|
|
@ -2083,6 +2103,97 @@ static inline void VL_ASSIGNSEL_WW(int rbits, int obits, int lsb, WDataOutP iowp
|
|||
_vl_insert_WW(iowp, rwp, lsb + obits - 1, lsb, rbits);
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// Range assignments
|
||||
|
||||
// These additional functions copy bits range [obis+roffset-1:roffset] from rhs to lower bits
|
||||
// of lhs(select before assigning). Rhs should always be wider than lhs.
|
||||
static inline void VL_SELASSIGN_II(int rbits, int obits, CData& lhsr, IData rhs,
|
||||
int roffset) VL_PURE {
|
||||
_vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits);
|
||||
}
|
||||
static inline void VL_SELASSIGN_II(int rbits, int obits, SData& lhsr, IData rhs,
|
||||
int roffset) VL_PURE {
|
||||
_vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits);
|
||||
}
|
||||
static inline void VL_SELASSIGN_II(int rbits, int obits, IData& lhsr, IData rhs,
|
||||
int roffset) VL_PURE {
|
||||
_vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits);
|
||||
}
|
||||
static inline void VL_SELASSIGN_IQ(int rbits, int obits, CData& lhsr, QData rhs,
|
||||
int roffset) VL_PURE {
|
||||
// it will be truncated to right CData mask
|
||||
const CData cleanmask = VL_MASK_I(rbits);
|
||||
const CData insmask = VL_MASK_I(obits);
|
||||
lhsr = (lhsr & ~insmask) | (static_cast<CData>(rhs >> roffset) & (insmask & cleanmask));
|
||||
}
|
||||
static inline void VL_SELASSIGN_IQ(int rbits, int obits, SData& lhsr, QData rhs,
|
||||
int roffset) VL_PURE {
|
||||
// it will be truncated to right CData mask
|
||||
const SData cleanmask = VL_MASK_I(rbits);
|
||||
const SData insmask = VL_MASK_I(obits);
|
||||
lhsr = (lhsr & ~insmask) | (static_cast<SData>(rhs >> roffset) & (insmask & cleanmask));
|
||||
}
|
||||
static inline void VL_SELASSIGN_IQ(int rbits, int obits, IData& lhsr, QData rhs,
|
||||
int roffset) VL_PURE {
|
||||
const IData cleanmask = VL_MASK_I(rbits);
|
||||
const IData insmask = VL_MASK_I(obits);
|
||||
lhsr = (lhsr & ~insmask) | (static_cast<IData>(rhs >> roffset) & (insmask & cleanmask));
|
||||
}
|
||||
|
||||
static inline void VL_SELASSIGN_QQ(int rbits, int obits, QData& lhsr, QData rhs,
|
||||
int roffset) VL_PURE {
|
||||
_vl_insert_QQ(lhsr, rhs >> roffset, obits - 1, 0, rbits);
|
||||
}
|
||||
|
||||
static inline void VL_SELASSIGN_IW(int rbits, int obits, CData& lhsr, WDataInP const rhs,
|
||||
int roffset) VL_MT_SAFE {
|
||||
IData l = static_cast<IData>(lhsr);
|
||||
_vl_insert_IW(l, rhs, roffset + obits - 1, roffset, rbits);
|
||||
lhsr = static_cast<CData>(l);
|
||||
}
|
||||
static inline void VL_SELASSIGN_IW(int rbits, int obits, SData& lhsr, WDataInP const rhs,
|
||||
int roffset) VL_MT_SAFE {
|
||||
IData l = static_cast<IData>(lhsr);
|
||||
_vl_insert_IW(l, rhs, roffset + obits - 1, roffset, rbits);
|
||||
lhsr = static_cast<SData>(l);
|
||||
}
|
||||
static inline void VL_SELASSIGN_IW(int rbits, int obits, IData& lhsr, WDataInP const rhs,
|
||||
int roffset) VL_MT_SAFE {
|
||||
_vl_insert_IW(lhsr, rhs, roffset + obits - 1, roffset, rbits);
|
||||
}
|
||||
static inline void VL_SELASSIGN_QW(int rbits, int obits, QData& lhsr, WDataInP const rhs,
|
||||
int roffset) VL_MT_SAFE {
|
||||
// assert VL_QDATASIZE >= rbits > VL_IDATASIZE;
|
||||
IData low = static_cast<IData>(lhsr);
|
||||
IData high = static_cast<IData>(lhsr >> VL_IDATASIZE);
|
||||
if (obits <= VL_IDATASIZE) {
|
||||
_vl_insert_IW(low, rhs, obits + roffset - 1, roffset, VL_IDATASIZE);
|
||||
} else {
|
||||
_vl_insert_IW(low, rhs, roffset + VL_IDATASIZE - 1, roffset, VL_IDATASIZE);
|
||||
_vl_insert_IW(high, rhs, roffset + obits - 1, roffset + VL_IDATASIZE,
|
||||
rbits - VL_IDATASIZE);
|
||||
}
|
||||
lhsr = (static_cast<QData>(high) << VL_IDATASIZE) | low;
|
||||
}
|
||||
|
||||
static inline void VL_SELASSIGN_WW(int rbits, int obits, WDataOutP iowp, WDataInP const rwp,
|
||||
int roffset) VL_MT_SAFE {
|
||||
// assert rbits > VL_QDATASIZE
|
||||
const int wordoff = roffset / VL_EDATASIZE;
|
||||
const int lsb = roffset & VL_SIZEBITS_E;
|
||||
const int upperbits = lsb == 0 ? 0 : VL_EDATASIZE - lsb;
|
||||
// If roffset is not aligned, we copy some bits to align it.
|
||||
if (lsb != 0) {
|
||||
const int w = obits < upperbits ? obits : upperbits;
|
||||
const int insmask = VL_MASK_E(w);
|
||||
iowp[0] = (iowp[0] & ~insmask) | ((rwp[wordoff] >> lsb) & insmask);
|
||||
if (w == obits) return;
|
||||
obits -= w;
|
||||
}
|
||||
_vl_insert_WW(iowp, rwp + wordoff + (lsb != 0), upperbits + obits - 1, upperbits, rbits);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Triops
|
||||
|
||||
|
|
|
|||
|
|
@ -126,13 +126,13 @@ public:
|
|||
const char* charIQWN() const {
|
||||
return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I");
|
||||
}
|
||||
string cType(const string& name, bool forFunc, bool isRef) const;
|
||||
string cType(const string& name, bool forFunc, bool isRef, bool packed=false) const;
|
||||
// Represents a C++ LiteralType? (can be constexpr)
|
||||
bool isLiteralType() const VL_MT_STABLE;
|
||||
|
||||
private:
|
||||
class CTypeRecursed;
|
||||
CTypeRecursed cTypeRecurse(bool compound) const;
|
||||
CTypeRecursed cTypeRecurse(bool compound, bool packed) const;
|
||||
};
|
||||
class AstNodeArrayDType VL_NOT_FINAL : public AstNodeDType {
|
||||
// Array data type, ie "some_dtype var_name [2:0]"
|
||||
|
|
@ -510,11 +510,17 @@ public:
|
|||
int widthAlignBytes() const override { return 8; } // Assume
|
||||
int widthTotalBytes() const override { return 8; } // Assume
|
||||
bool isCompound() const override { return true; }
|
||||
static string typeToHold(uint64_t maxItem) {
|
||||
return (maxItem < (1ULL << 8)) ? "CData"
|
||||
: (maxItem < (1ULL << 16)) ? "SData"
|
||||
: (maxItem < (1ULL << 32)) ? "IData"
|
||||
: "QData";
|
||||
static string typeToHold(int width) {
|
||||
if (width <= 8)
|
||||
return "CData";
|
||||
else if (width <= 16)
|
||||
return "SData";
|
||||
else if (width <= VL_IDATASIZE)
|
||||
return "IData";
|
||||
else if (width <= VL_QUADSIZE)
|
||||
return "QData";
|
||||
else
|
||||
return "VlWide<" + std::to_string(VL_WORDS_I(width)) + ">";
|
||||
}
|
||||
};
|
||||
class AstClassRefDType final : public AstNodeDType {
|
||||
|
|
|
|||
|
|
@ -738,49 +738,66 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const {
|
||||
const CTypeRecursed info = cTypeRecurse(false);
|
||||
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef, bool packed) const {
|
||||
const CTypeRecursed info = cTypeRecurse(false, packed);
|
||||
return info.render(name, isRef);
|
||||
}
|
||||
|
||||
AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packed) const {
|
||||
// Legacy compound argument currently just passed through and unused
|
||||
CTypeRecursed info;
|
||||
|
||||
const AstNodeDType* const dtypep = this->skipRefp();
|
||||
if (const auto* const adtypep = VN_CAST(dtypep, AssocArrayDType)) {
|
||||
const CTypeRecursed key = adtypep->keyDTypep()->cTypeRecurse(true);
|
||||
const CTypeRecursed val = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
const CTypeRecursed key = adtypep->keyDTypep()->cTypeRecurse(true, false);
|
||||
const CTypeRecursed val = adtypep->subDTypep()->cTypeRecurse(true, false);
|
||||
info.m_type = "VlAssocArray<" + key.m_type + ", " + val.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, CDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = adtypep->name();
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true, false);
|
||||
info.m_type = "VlAssocArray<std::string, " + sub.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true, false);
|
||||
info.m_type = "VlQueue<" + sub.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, QueueDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true, false);
|
||||
info.m_type = "VlQueue<" + sub.m_type;
|
||||
// + 1 below as VlQueue uses 0 to mean unlimited, 1 to mean size() max is 1
|
||||
if (adtypep->boundp()) info.m_type += ", " + cvtToStr(adtypep->boundConst() + 1);
|
||||
info.m_type += ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, SampleQueueDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true, false);
|
||||
info.m_type = "VlSampleQueue<" + sub.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = "VlClassRef<" + EmitCBase::prefixNameProtect(adtypep) + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = EmitCBase::prefixNameProtect(adtypep->ifaceViaCellp()) + "*";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
if (adtypep->isCompound()) compound = true;
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound);
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound, false);
|
||||
info.m_type = "VlUnpacked<" + sub.m_type;
|
||||
info.m_type += ", " + cvtToStr(adtypep->declRange().elements());
|
||||
info.m_type += ">";
|
||||
} else if (VN_IS(dtypep, NodeUOrStructDType) && !VN_AS(dtypep, NodeUOrStructDType)->packed()) {
|
||||
const auto* const sdtypep = VN_AS(dtypep, NodeUOrStructDType);
|
||||
} else if (packed && (VN_IS(dtypep, PackArrayDType))) {
|
||||
const AstPackArrayDType* const adtypep = VN_CAST(dtypep, PackArrayDType);
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(false, true);
|
||||
info.m_type = std::move(sub.m_type);
|
||||
info.m_dims = "[" + cvtToStr(adtypep->elementsConst()) + "]" + sub.m_dims;
|
||||
} else if (VN_IS(dtypep, NodeUOrStructDType)
|
||||
&& (!VN_AS(dtypep, NodeUOrStructDType)->packed() || packed)) {
|
||||
const AstNodeUOrStructDType* const sdtypep = VN_AS(dtypep, NodeUOrStructDType);
|
||||
UASSERT_OBJ(!packed || sdtypep->packed(), this,
|
||||
"Unsupported type for packed struct or union");
|
||||
info.m_type = EmitCBase::prefixNameProtect(sdtypep);
|
||||
} else if (const AstBasicDType* const bdtypep = dtypep->basicp()) {
|
||||
// We don't print msb()/lsb() as multidim packed would require recursion,
|
||||
|
|
@ -823,6 +840,13 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
|||
} else if (dtypep->isWide()) {
|
||||
info.m_type = "VlWide<" + cvtToStr(dtypep->widthWords()) + ">" + bitvec;
|
||||
}
|
||||
// CData, SData, IData, QData or VlWide are packed type.
|
||||
const bool packedType = VString::startsWith(info.m_type, "CData")
|
||||
|| VString::startsWith(info.m_type, "SData")
|
||||
|| VString::startsWith(info.m_type, "IData")
|
||||
|| VString::startsWith(info.m_type, "QData")
|
||||
|| VString::startsWith(info.m_type, "VlWide");
|
||||
UASSERT_OBJ(!packed || packedType, this, "Unsupported type for packed struct or union");
|
||||
} else {
|
||||
v3fatalSrc("Unknown data type in var type emitter: " << dtypep->prettyName());
|
||||
}
|
||||
|
|
@ -1728,7 +1752,8 @@ AstNodeUOrStructDType* AstMemberDType::getChildStructp() const {
|
|||
while (AstNodeArrayDType* const asubdtp = VN_CAST(subdtp, NodeArrayDType)) {
|
||||
subdtp = asubdtp->subDTypep();
|
||||
}
|
||||
return VN_CAST(subdtp, NodeUOrStructDType); // Maybe nullptr
|
||||
// It's possible that `subdtp` is still a ref type, so skip it.
|
||||
return VN_CAST(subdtp->skipRefp(), NodeUOrStructDType); // Maybe nullptr
|
||||
}
|
||||
|
||||
bool AstMemberSel::same(const AstNode* samep) const {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@
|
|||
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <queue>
|
||||
#include <set>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
|
|
@ -46,6 +49,11 @@ class ClassVisitor final : public VNVisitor {
|
|||
const AstNodeFTask* m_ftaskp = nullptr; // Current task
|
||||
std::vector<std::pair<AstNode*, AstScope*>> m_toScopeMoves;
|
||||
std::vector<std::pair<AstNode*, AstNodeModule*>> m_toPackageMoves;
|
||||
std::set<AstTypedef*> m_typedefps; // Contains all typedef nodes
|
||||
std::set<AstNodeUOrStructDType*> m_strDtypeps; // Contains all packed structs and unions
|
||||
// Contains all public packed structs and unions, using a queue to
|
||||
// mark embedded struct / union public by BFS
|
||||
std::queue<AstNodeUOrStructDType*> m_pubStrDtypeps;
|
||||
|
||||
// METHODS
|
||||
|
||||
|
|
@ -204,22 +212,27 @@ class ClassVisitor final : public VNVisitor {
|
|||
dtypep->classOrPackagep(m_classPackagep ? m_classPackagep : m_modp);
|
||||
dtypep->name(
|
||||
m_names.get(dtypep->name() + (VN_IS(dtypep, UnionDType) ? "__union" : "__struct")));
|
||||
if (dtypep->packed()) m_strDtypeps.insert(dtypep);
|
||||
|
||||
for (const AstMemberDType* itemp = dtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
AstNodeUOrStructDType* const subp = itemp->getChildStructp();
|
||||
// Recurse only into anonymous unpacked structs inside this definition,
|
||||
// other unpacked structs will be reached from another typedefs
|
||||
if (subp && !subp->packed() && subp->name().empty()) setStructModulep(subp);
|
||||
// Recurse only into anonymous structs inside this definition,
|
||||
// other structs will be reached from another typedefs
|
||||
if (subp && subp->name().empty()) setStructModulep(subp);
|
||||
}
|
||||
}
|
||||
void visit(AstTypedef* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
AstNodeUOrStructDType* const dtypep = VN_CAST(nodep->dtypep(), NodeUOrStructDType);
|
||||
if (dtypep && dtypep->packed()) {
|
||||
m_typedefps.insert(nodep);
|
||||
if (nodep->attrPublic()) m_pubStrDtypeps.push(dtypep);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
if (m_classPackagep) m_classPackagep->addStmtsp(nodep->unlinkFrBack());
|
||||
|
||||
AstNodeUOrStructDType* const dtypep = VN_CAST(nodep->dtypep(), NodeUOrStructDType);
|
||||
if (dtypep && !dtypep->packed()) {
|
||||
if (dtypep) {
|
||||
dtypep->name(nodep->name());
|
||||
setStructModulep(dtypep);
|
||||
}
|
||||
|
|
@ -258,6 +271,28 @@ public:
|
|||
nodep->unlinkFrBack();
|
||||
modp->addStmtsp(nodep);
|
||||
}
|
||||
// BFS to mark public typedefs.
|
||||
std::set<AstNodeUOrStructDType*> pubStrDtypeps;
|
||||
while (!m_pubStrDtypeps.empty()) {
|
||||
AstNodeUOrStructDType* const dtypep = m_pubStrDtypeps.front();
|
||||
m_pubStrDtypeps.pop();
|
||||
if (pubStrDtypeps.insert(dtypep).second) {
|
||||
for (const AstMemberDType* itemp = dtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
if (AstNodeUOrStructDType* const subp = itemp->getChildStructp())
|
||||
m_pubStrDtypeps.push(subp);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (AstTypedef* typedefp : m_typedefps) {
|
||||
AstNodeUOrStructDType* const sdtypep = VN_AS(typedefp->dtypep(), NodeUOrStructDType);
|
||||
if (pubStrDtypeps.count(sdtypep)) typedefp->attrPublic(true);
|
||||
}
|
||||
// Clear package pointer of non-public packed struct / union type, which will never be
|
||||
// exported.
|
||||
for (AstNodeUOrStructDType* sdtypep : m_strDtypeps) {
|
||||
if (!pubStrDtypeps.count(sdtypep)) sdtypep->classOrPackagep(nullptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "V3EmitC.h"
|
||||
#include "V3EmitCConstInit.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
|
@ -31,6 +32,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
|||
// Internal EmitC implementation
|
||||
|
||||
class EmitCHeader final : public EmitCConstInit {
|
||||
V3UniqueNames m_names;
|
||||
// METHODS
|
||||
|
||||
void decorateFirst(bool& first, const string& str) {
|
||||
|
|
@ -223,7 +225,7 @@ class EmitCHeader final : public EmitCConstInit {
|
|||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
AstNodeUOrStructDType* const subp = itemp->getChildStructp();
|
||||
if (subp && !subp->packed()) {
|
||||
if (subp && (!subp->packed() || sdtypep->packed())) {
|
||||
// Recurse if it belongs to the current module
|
||||
if (subp->classOrPackagep() == modp) {
|
||||
emitStructDecl(modp, subp, emitted);
|
||||
|
|
@ -231,6 +233,13 @@ class EmitCHeader final : public EmitCConstInit {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (sdtypep->packed()) {
|
||||
emitPackedUOrSBody(sdtypep);
|
||||
} else {
|
||||
emitUnpackedUOrSBody(sdtypep);
|
||||
}
|
||||
}
|
||||
void emitUnpackedUOrSBody(AstNodeUOrStructDType* sdtypep) {
|
||||
putns(sdtypep, sdtypep->verilogKwd()); // "struct"/"union"
|
||||
puts(" " + EmitCBase::prefixNameProtect(sdtypep) + " {\n");
|
||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||
|
|
@ -254,8 +263,135 @@ class EmitCHeader final : public EmitCConstInit {
|
|||
puts("return !(*this == rhs);\n}\n");
|
||||
puts("};\n");
|
||||
}
|
||||
|
||||
// getfunc: VL_ASSIGNSEL_XX(rbits, obits, off, lhsdata, rhsdata);
|
||||
// !getfunc: VL_SELASSIGN_XX(rbits, obits, lhsdata, rhsdata, off);
|
||||
void emitVlAssign(const AstNodeDType* const lhstype, const AstNodeDType* rhstype,
|
||||
const std::string& off, const std::string& lhsdata,
|
||||
const std::string& rhsdata, bool getfunc) {
|
||||
puts(getfunc ? "VL_ASSIGNSEL_" : "VL_SELASSIGN_");
|
||||
puts(lhstype->charIQWN());
|
||||
puts(rhstype->charIQWN());
|
||||
puts("(" + std::to_string(lhstype->width()) + ", "); // LHS width
|
||||
if (getfunc) {
|
||||
puts(std::to_string(rhstype->width()) + ", "); // Number of copy bits
|
||||
puts(off + ", "); // LHS offset
|
||||
} else {
|
||||
// Number of copy bits. Use widthTototalBytes to
|
||||
// make VL_SELASSIGN_XX clear upper unused bits for us.
|
||||
// puts(std::to_string(lhstype->width()) + ", ");
|
||||
puts(std::to_string(lhstype->widthTotalBytes() * 8) + ", ");
|
||||
}
|
||||
puts(lhsdata + ", "); // LHS data
|
||||
puts(rhsdata); // RHS data
|
||||
if (!getfunc) {
|
||||
puts(", " + off); // RHS offset
|
||||
}
|
||||
puts(");\n");
|
||||
}
|
||||
|
||||
// `retOrArg` should be prefixed by `&` or suffixed by `.data()` depending on its type
|
||||
void emitPackedMember(const AstNodeDType* parentDtypep, const AstNodeDType* dtypep,
|
||||
const std::string& fieldname, const std::string& offset, bool getfunc,
|
||||
const std::string& retOrArg) {
|
||||
dtypep = dtypep->skipRefp();
|
||||
if (const auto* adtypep = VN_CAST(dtypep, PackArrayDType)) {
|
||||
const std::string index = m_names.get("__Vi");
|
||||
puts("for (int " + index + " = 0; " + index + " < "
|
||||
+ std::to_string(adtypep->elementsConst()) + "; ++" + index + ") {\n");
|
||||
|
||||
const std::string offsetInLoop
|
||||
= offset + " + " + index + " * " + std::to_string(adtypep->subDTypep()->width());
|
||||
const std::string newName = fieldname + "[" + index + "]";
|
||||
emitPackedMember(parentDtypep, adtypep->subDTypep(), newName, offsetInLoop, getfunc,
|
||||
retOrArg);
|
||||
puts("}\n");
|
||||
} else if (VN_IS(dtypep, NodeUOrStructDType)) {
|
||||
const std::string tmp = m_names.get("__Vtmp");
|
||||
const std::string suffixName = dtypep->isWide() ? tmp + ".data()" : tmp;
|
||||
if (getfunc) { // Emit `get` func;
|
||||
// auto __tmp = field.get();
|
||||
puts("auto " + tmp + " = " + fieldname + ".get();\n");
|
||||
// VL_ASSIGNSEL_XX(rbits, obits, lsb, lhsdata, rhsdata);
|
||||
emitVlAssign(parentDtypep, dtypep, offset, retOrArg, suffixName, getfunc);
|
||||
} else { // Emit `set` func
|
||||
const std::string tmptype = AstCDType::typeToHold(dtypep->width());
|
||||
// type tmp;
|
||||
puts(tmptype + " " + tmp + ";\n");
|
||||
// VL_SELASSIGN_XX(rbits, obits, lhsdata, rhsdata, roffset);
|
||||
emitVlAssign(dtypep, parentDtypep, offset, suffixName, retOrArg, getfunc);
|
||||
// field.set(__tmp);
|
||||
puts(fieldname + ".set(" + tmp + ");\n");
|
||||
}
|
||||
} else {
|
||||
UASSERT_OBJ(VN_IS(dtypep, EnumDType) || VN_IS(dtypep, BasicDType), dtypep,
|
||||
"Unsupported type in packed struct or union");
|
||||
const std::string suffixName = dtypep->isWide() ? fieldname + ".data()" : fieldname;
|
||||
if (getfunc) { // Emit `get` func;
|
||||
// VL_ASSIGNSEL_XX(rbits, obits, lsb, lhsdata, rhsdata);
|
||||
emitVlAssign(parentDtypep, dtypep, offset, retOrArg, suffixName, getfunc);
|
||||
} else { // Emit `set` func
|
||||
// VL_SELASSIGN_XX(rbits, obits, lhsdata, rhsdata, roffset);
|
||||
emitVlAssign(dtypep, parentDtypep, offset, suffixName, retOrArg, getfunc);
|
||||
}
|
||||
}
|
||||
}
|
||||
void emitPackedUOrSBody(AstNodeUOrStructDType* sdtypep) {
|
||||
putns(sdtypep, sdtypep->verilogKwd()); // "struct"/"union"
|
||||
puts(" " + EmitCBase::prefixNameProtect(sdtypep) + " {\n");
|
||||
|
||||
AstMemberDType* itemp;
|
||||
AstMemberDType* lastItemp;
|
||||
AstMemberDType* witemp = nullptr;
|
||||
// LSB is first field in C, so loop backwards
|
||||
for (lastItemp = sdtypep->membersp(); lastItemp && lastItemp->nextp();
|
||||
lastItemp = VN_AS(lastItemp->nextp(), MemberDType)) {
|
||||
if (lastItemp->width() == sdtypep->width()) witemp = lastItemp;
|
||||
}
|
||||
for (itemp = lastItemp; itemp; itemp = VN_CAST(itemp->backp(), MemberDType)) {
|
||||
putns(itemp, itemp->dtypep()->cType(itemp->nameProtect(), false, false, true));
|
||||
puts(";\n");
|
||||
}
|
||||
|
||||
const std::string retArgName = m_names.get("__v");
|
||||
const std::string suffixName = sdtypep->isWide() ? retArgName + ".data()" : retArgName;
|
||||
const std::string retArgType = AstCDType::typeToHold(sdtypep->width());
|
||||
|
||||
// Emit `get` member function
|
||||
puts(retArgType + " get() const {\n");
|
||||
puts(retArgType + " " + retArgName + ";\n");
|
||||
if (VN_IS(sdtypep, StructDType)) {
|
||||
for (itemp = lastItemp; itemp; itemp = VN_CAST(itemp->backp(), MemberDType)) {
|
||||
emitPackedMember(sdtypep, itemp->dtypep(), itemp->nameProtect(),
|
||||
std::to_string(itemp->lsb()), /*getfunc=*/true, suffixName);
|
||||
}
|
||||
} else {
|
||||
// We only need to fill the widest field of union
|
||||
emitPackedMember(sdtypep, witemp->dtypep(), witemp->nameProtect(),
|
||||
std::to_string(witemp->lsb()), /*getfunc=*/true, suffixName);
|
||||
}
|
||||
puts("return " + retArgName + ";\n");
|
||||
puts("}\n");
|
||||
|
||||
// Emit `set` member function
|
||||
puts("void set(const " + retArgType + "& " + retArgName + ") {\n");
|
||||
if (VN_IS(sdtypep, StructDType)) {
|
||||
for (itemp = lastItemp; itemp; itemp = VN_CAST(itemp->backp(), MemberDType)) {
|
||||
emitPackedMember(sdtypep, itemp->dtypep(), itemp->nameProtect(),
|
||||
std::to_string(itemp->lsb()), /*getfunc=*/false, suffixName);
|
||||
}
|
||||
} else {
|
||||
// We only need to fill the widest field of union
|
||||
emitPackedMember(sdtypep, witemp->dtypep(), witemp->nameProtect(),
|
||||
std::to_string(witemp->lsb()), /*getfunc=*/false, suffixName);
|
||||
}
|
||||
|
||||
puts("}\n");
|
||||
|
||||
puts("};\n");
|
||||
m_names.reset();
|
||||
}
|
||||
void emitStructs(const AstNodeModule* modp) {
|
||||
bool first = true;
|
||||
// Track structs that've been emitted already
|
||||
std::set<AstNodeUOrStructDType*> emitted;
|
||||
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
|
|
@ -264,8 +400,6 @@ class EmitCHeader final : public EmitCConstInit {
|
|||
AstNodeUOrStructDType* const sdtypep
|
||||
= VN_CAST(tdefp->dtypep()->skipRefToEnump(), NodeUOrStructDType);
|
||||
if (!sdtypep) continue;
|
||||
if (sdtypep->packed()) continue;
|
||||
decorateFirst(first, "\n// UNPACKED STRUCT TYPES\n");
|
||||
emitStructDecl(modp, sdtypep, emitted);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@
|
|||
#include "V3Name.h"
|
||||
|
||||
#include "V3LanguageWords.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
|
|
@ -41,6 +44,19 @@ class NameVisitor final : public VNVisitorConst {
|
|||
// STATE - for current visit position (use VL_RESTORER)
|
||||
const AstNodeModule* m_modp = nullptr; // Current module
|
||||
|
||||
// Rename struct / union field properly
|
||||
std::vector<V3UniqueNames> m_nameStack; // Hierarchy-based renames
|
||||
|
||||
void renameKeywordCheck(AstNode* nodep) {
|
||||
const std::string rsvd = V3LanguageWords::isKeyword(nodep->name());
|
||||
if (rsvd != "") {
|
||||
nodep->v3warn(SYMRSVDWORD, "Symbol matches " + rsvd + ": " << nodep->prettyNameQ());
|
||||
const string newname = "__SYM__"s + nodep->name();
|
||||
nodep->name(newname);
|
||||
nodep->editCountInc();
|
||||
}
|
||||
}
|
||||
|
||||
// METHODS
|
||||
void rename(AstNode* nodep, bool addPvt) {
|
||||
if (!nodep->user1()) { // Not already done
|
||||
|
|
@ -50,14 +66,7 @@ class NameVisitor final : public VNVisitorConst {
|
|||
nodep->editCountInc();
|
||||
} else if (VN_IS(nodep, CFunc) && VN_AS(nodep, CFunc)->isConstructor()) {
|
||||
} else {
|
||||
const string rsvd = V3LanguageWords::isKeyword(nodep->name());
|
||||
if (rsvd != "") {
|
||||
nodep->v3warn(SYMRSVDWORD,
|
||||
"Symbol matches " + rsvd + ": " << nodep->prettyNameQ());
|
||||
const string newname = "__SYM__"s + nodep->name();
|
||||
nodep->name(newname);
|
||||
nodep->editCountInc();
|
||||
}
|
||||
renameKeywordCheck(nodep);
|
||||
}
|
||||
nodep->user1(1);
|
||||
}
|
||||
|
|
@ -92,9 +101,24 @@ class NameVisitor final : public VNVisitorConst {
|
|||
iterateChildrenConst(nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstNodeUOrStructDType* nodep) override {
|
||||
if (nodep->packed()) {
|
||||
m_nameStack.emplace_back("", false);
|
||||
m_nameStack.back().get("get");
|
||||
m_nameStack.back().get("set");
|
||||
}
|
||||
iterateChildrenConst(nodep);
|
||||
if (nodep->packed()) m_nameStack.pop_back();
|
||||
}
|
||||
void visit(AstMemberDType* nodep) override {
|
||||
if (!nodep->user1()) {
|
||||
rename(nodep, true);
|
||||
if (!m_nameStack.empty()) { // Packed member field
|
||||
renameKeywordCheck(nodep);
|
||||
nodep->name(m_nameStack.back().get(nodep->name()));
|
||||
nodep->user1(1);
|
||||
} else {
|
||||
rename(nodep, true);
|
||||
}
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -396,6 +396,11 @@ int V3Number::log2b(uint32_t num) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int V3Number::log2bQuad(uint64_t num) {
|
||||
if (num >> 32ULL) return 32 + log2b(num >> 32ULL);
|
||||
return log2b(num);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Setters
|
||||
|
||||
|
|
|
|||
|
|
@ -662,6 +662,7 @@ public:
|
|||
|
||||
// STATICS
|
||||
static int log2b(uint32_t num);
|
||||
static int log2bQuad(uint64_t num);
|
||||
|
||||
// MATH
|
||||
// "this" is the output, as we need the output width before some computations
|
||||
|
|
|
|||
|
|
@ -173,7 +173,8 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
|
||||
AstCDType* findVlRandCDType(FileLine* fl, uint64_t items) {
|
||||
// For 8 items we need to have a 9 item LFSR so items is max count
|
||||
const std::string type = AstCDType::typeToHold(items);
|
||||
// width(items) = log2(items) + 1
|
||||
const std::string type = AstCDType::typeToHold(V3Number::log2bQuad(items) + 1);
|
||||
const std::string name = "VlRandC<" + type + ", " + cvtToStr(items) + "ULL>";
|
||||
// Create or reuse (to avoid duplicates) randomization object dtype
|
||||
const auto pair = m_randcDtypes.emplace(name, nullptr);
|
||||
|
|
|
|||
|
|
@ -24,18 +24,21 @@
|
|||
|
||||
#include "V3Hasher.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class V3UniqueNames final {
|
||||
const std::string m_prefix; // Prefix to attach to all names
|
||||
|
||||
std::unordered_map<std::string, unsigned> m_multiplicity; // Suffix number for given key
|
||||
std::map<std::string, unsigned> m_multiplicity; // Suffix number for given key
|
||||
|
||||
const bool m_addSuffix = true; // Ad suffix or not
|
||||
|
||||
public:
|
||||
V3UniqueNames() = default;
|
||||
explicit V3UniqueNames(const std::string& prefix)
|
||||
: m_prefix{prefix} {
|
||||
explicit V3UniqueNames(const std::string& prefix, bool addSuffix = true)
|
||||
: m_prefix{prefix}
|
||||
, m_addSuffix(addSuffix) {
|
||||
if (!m_prefix.empty()) {
|
||||
UASSERT(VString::startsWith(m_prefix, "__V"), "Prefix must start with '__V'");
|
||||
UASSERT(!VString::endsWith(m_prefix, "_"), "Prefix must not end with '_'");
|
||||
|
|
@ -45,6 +48,15 @@ public:
|
|||
// Return argument, prepended with the prefix if any, then appended with a unique suffix each
|
||||
// time we are called with the same argument.
|
||||
std::string get(const std::string& name) {
|
||||
if (!m_addSuffix) {
|
||||
if (m_multiplicity.count(name) == 0) {
|
||||
m_multiplicity[name] = 0;
|
||||
return name;
|
||||
} else {
|
||||
return get(name + "__" + cvtToStr(m_multiplicity[name]++));
|
||||
}
|
||||
}
|
||||
// NORMAL mode
|
||||
const unsigned num = m_multiplicity[name]++;
|
||||
std::string result;
|
||||
if (!m_prefix.empty()) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,245 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// Copyright 2024 by Kefa Chen. This program is free software; you can
|
||||
// redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//*************************************************************************
|
||||
|
||||
#include <verilated.h>
|
||||
|
||||
#include VM_PREFIX_INCLUDE
|
||||
|
||||
#include "TestCheck.h"
|
||||
|
||||
/*
|
||||
typedef logic [5:0] udata6_t;
|
||||
|
||||
typedef union packed {
|
||||
udata6_t a;
|
||||
logic [2:0] b;
|
||||
} sub_t;
|
||||
|
||||
typedef struct packed {
|
||||
logic [40:0] a;
|
||||
udata6_t [3:0] b;
|
||||
sub_t c;
|
||||
} in_t ;
|
||||
|
||||
typedef struct packed {
|
||||
udata6_t [3:0] b;
|
||||
sub_t c;
|
||||
logic [40:0] a;
|
||||
} out_t ;
|
||||
|
||||
// struct in1_t should cover parts of VL_ASSIGNSEL_II functions
|
||||
typedef struct packed {
|
||||
logic [3:0] a;
|
||||
logic [11:0] b;
|
||||
} in1_t; // 4 + 12 = 16
|
||||
|
||||
typedef struct packed {
|
||||
logic [11:0] b;
|
||||
logic [3:0] a;
|
||||
} out1_t;
|
||||
|
||||
// struct in2_t should cover all VL_ASSIGNSEL_II functions
|
||||
typedef struct packed {
|
||||
logic [2:0] a;
|
||||
logic [8:0] b;
|
||||
logic [18:0] c;
|
||||
} in2_t; // 3 + 9 + 19 = 31
|
||||
|
||||
typedef struct packed {
|
||||
logic [8:0] b;
|
||||
logic [18:0] c;
|
||||
logic [2:0] a;
|
||||
} out2_t;
|
||||
|
||||
// struct in3_t should cover all VL_ASSIGNSEL_XQ functions
|
||||
typedef struct packed {
|
||||
logic [1:0] a;
|
||||
logic [8:0] b;
|
||||
logic [16:0] c;
|
||||
logic [32:0] d;
|
||||
} in3_t; // 33 + 17 + 9 + 2 = 61
|
||||
|
||||
typedef struct packed {
|
||||
logic [8:0] b;
|
||||
logic [1:0] a;
|
||||
logic [32:0] d;
|
||||
logic [16:0] c;
|
||||
} out3_t;
|
||||
|
||||
// struct in4_t should cover all VL_ASSIGNSEL_XW functions
|
||||
typedef struct packed {
|
||||
logic [4:0] a;
|
||||
logic [12:0] b;
|
||||
logic [24:0] c;
|
||||
logic [48:0] d;
|
||||
logic [80:0] e;
|
||||
} in4_t; // 5 + 13 + 25 + 49 + 81 = 173
|
||||
|
||||
typedef struct packed {
|
||||
logic [24:0] c;
|
||||
logic [48:0] d;
|
||||
logic [80:0] e;
|
||||
logic [4:0] a;
|
||||
logic [12:0] b;
|
||||
} out4_t;
|
||||
*/
|
||||
|
||||
#define CONCAT_IMPL(a, b) a##b
|
||||
#define CONCAT(a, b) CONCAT_IMPL(a, b)
|
||||
#define CONCAT5(a, b, c, d, e) CONCAT(CONCAT(CONCAT(CONCAT(a, b), c), d), e)
|
||||
#define EXPORTED_STRUCT_NAME(STRUCT_NAME, NUMBER) \
|
||||
CONCAT5(VM_PREFIX, _, STRUCT_NAME, __struct__, NUMBER)
|
||||
#define EXPORTED_UNION_NAME(UNION_NAME, NUMBER) \
|
||||
CONCAT5(VM_PREFIX, _, UNION_NAME, __union__, NUMBER)
|
||||
#define SUB_T EXPORTED_UNION_NAME(sub_t, 0)
|
||||
#define IN_T EXPORTED_STRUCT_NAME(in_t, 0)
|
||||
#define OUT_T EXPORTED_STRUCT_NAME(out_t, 0)
|
||||
|
||||
#define IN1_T EXPORTED_STRUCT_NAME(in1_t, 0)
|
||||
#define IN2_T EXPORTED_STRUCT_NAME(in2_t, 0)
|
||||
#define IN3_T EXPORTED_STRUCT_NAME(in3_t, 0)
|
||||
#define IN4_T EXPORTED_STRUCT_NAME(in4_t, 0)
|
||||
#define OUT1_T EXPORTED_STRUCT_NAME(out1_t, 0)
|
||||
#define OUT2_T EXPORTED_STRUCT_NAME(out2_t, 0)
|
||||
#define OUT3_T EXPORTED_STRUCT_NAME(out3_t, 0)
|
||||
#define OUT4_T EXPORTED_STRUCT_NAME(out4_t, 0)
|
||||
|
||||
int errors = 0;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
||||
contextp->debug(0);
|
||||
contextp->randReset(2);
|
||||
contextp->commandArgs(argc, argv);
|
||||
|
||||
const std::unique_ptr<VM_PREFIX> adder{new VM_PREFIX{contextp.get()}};
|
||||
|
||||
{
|
||||
IN_T in1, in2;
|
||||
OUT_T out;
|
||||
in1.a = 0x12345678;
|
||||
in1.__SYM__nullptr[0] = 0x1;
|
||||
in1.__SYM__nullptr[1] = 0x2;
|
||||
in1.__SYM__nullptr[2] = 0x3;
|
||||
in1.__SYM__nullptr[3] = 0x4;
|
||||
in1.get__0.a = 0x5;
|
||||
in2.a = 0x11111111;
|
||||
in2.__SYM__nullptr[0] = 0x10;
|
||||
in2.__SYM__nullptr[1] = 0x20;
|
||||
in2.__SYM__nullptr[2] = 0x30;
|
||||
in2.__SYM__nullptr[3] = 0x30;
|
||||
in2.get__0.a = 0x20;
|
||||
|
||||
adder->op1 = in1.get();
|
||||
adder->op2 = in2.get();
|
||||
adder->eval();
|
||||
out.set(adder->out);
|
||||
|
||||
TEST_CHECK_EQ(out.__SYM__nullptr[0], 0x11);
|
||||
TEST_CHECK_EQ(out.__SYM__nullptr[1], 0x22);
|
||||
TEST_CHECK_EQ(out.__SYM__nullptr[2], 0x33);
|
||||
TEST_CHECK_EQ(out.__SYM__nullptr[3], 0x34);
|
||||
TEST_CHECK_EQ(out.get__0.a, 0x25);
|
||||
TEST_CHECK_EQ(out.a, 0x23456789);
|
||||
|
||||
// Additional tests
|
||||
IN1_T op1a, op1b;
|
||||
OUT1_T out1;
|
||||
|
||||
op1a.a = 0x4;
|
||||
op1b.a = 0x5;
|
||||
op1a.b = 0x1fe;
|
||||
op1b.b = 0x1ef;
|
||||
|
||||
adder->op1a = op1a.get();
|
||||
adder->op1b = op1b.get();
|
||||
adder->eval();
|
||||
out1.set(adder->out1);
|
||||
|
||||
TEST_CHECK_EQ(out1.a, op1a.a + op1b.a);
|
||||
TEST_CHECK_EQ(out1.b, op1a.b + op1b.b);
|
||||
|
||||
IN2_T op2a, op2b;
|
||||
OUT2_T out2;
|
||||
|
||||
op2a.a = 0x4;
|
||||
op2b.a = 0x3;
|
||||
op2a.b = 0xff;
|
||||
op2b.b = 0x1;
|
||||
op2a.c = 0x11212;
|
||||
op2b.c = 0x12121;
|
||||
|
||||
adder->op2a = op2a.get();
|
||||
adder->op2b = op2b.get();
|
||||
adder->eval();
|
||||
out2.set(adder->out2);
|
||||
|
||||
TEST_CHECK_EQ(out2.a, op2a.a + op2b.a);
|
||||
TEST_CHECK_EQ(out2.b, op2a.b + op2b.b);
|
||||
TEST_CHECK_EQ(out2.c, op2a.c + op2b.c);
|
||||
|
||||
IN3_T op3a, op3b;
|
||||
OUT3_T out3;
|
||||
|
||||
op3a.a = 0x1;
|
||||
op3b.a = 0x2;
|
||||
op3a.b = 0x155;
|
||||
op3b.b = 0x44;
|
||||
op3a.c = 0xff;
|
||||
op3b.c = 0xff00;
|
||||
op3a.d = 0x123232323ULL;
|
||||
op3b.d = 0x32323232ULL;
|
||||
|
||||
adder->op3a = op3a.get();
|
||||
adder->op3b = op3b.get();
|
||||
adder->eval();
|
||||
out3.set(adder->out3);
|
||||
|
||||
TEST_CHECK_EQ(out3.a, op3a.a + op3b.a);
|
||||
TEST_CHECK_EQ(out3.b, op3a.b + op3b.b);
|
||||
TEST_CHECK_EQ(out3.c, op3a.c + op3b.c);
|
||||
TEST_CHECK_EQ(out3.d, op3a.d + op3b.d);
|
||||
|
||||
IN4_T op4a, op4b;
|
||||
OUT4_T out4;
|
||||
|
||||
op4a.a = 0xf;
|
||||
op4b.a = 0x2;
|
||||
op4a.b = 0x123;
|
||||
op4b.b = 0x432;
|
||||
op4a.c = 0x123456;
|
||||
op4b.c = 0x654321;
|
||||
op4a.d = 0x123456789ULL;
|
||||
op4b.d = 0x987654321ULL;
|
||||
op4a.e[0] = 0x12345678;
|
||||
op4b.e[0] = 0x87654321;
|
||||
op4a.e[1] = 0xabcde000;
|
||||
op4b.e[1] = 0x000cdeba;
|
||||
op4a.e[2] = 0xe;
|
||||
op4b.e[2] = 0xf;
|
||||
|
||||
adder->op4a = op4a.get();
|
||||
adder->op4b = op4b.get();
|
||||
adder->eval();
|
||||
out4.set(adder->out4);
|
||||
|
||||
TEST_CHECK_EQ(out4.a, op4a.a + op4b.a);
|
||||
TEST_CHECK_EQ(out4.b, op4a.b + op4b.b);
|
||||
TEST_CHECK_EQ(out4.c, op4a.c + op4b.c);
|
||||
TEST_CHECK_EQ(out4.d, op4a.d + op4b.d);
|
||||
TEST_CHECK_EQ(out4.e[0], op4a.e[0] + op4b.e[0]);
|
||||
TEST_CHECK_EQ(out4.e[1], op4a.e[1] + op4b.e[1]);
|
||||
TEST_CHECK_EQ(out4.e[2], op4a.e[2] + op4b.e[2]);
|
||||
}
|
||||
|
||||
printf("*-* All Finished *-*\n");
|
||||
return errors;
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2024 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
scenarios(vlt => 1);
|
||||
|
||||
compile(
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
verilator_flags2 => ["--exe $Self->{t_dir}/$Self->{name}.cpp -Wno-SYMRSVDWORD"],
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2024 by Kefa Chen.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
typedef logic [5:0] udata6_t;
|
||||
|
||||
typedef union packed {
|
||||
udata6_t a;
|
||||
logic [2:0] b;
|
||||
} sub_t;
|
||||
|
||||
typedef struct packed {
|
||||
logic [40:0] a;
|
||||
udata6_t [3:0] nullptr; // name confict test
|
||||
sub_t get; // name confict test
|
||||
} in_t /*verilator public*/;
|
||||
|
||||
typedef struct packed {
|
||||
udata6_t [3:0] nullptr;
|
||||
sub_t get;
|
||||
logic [40:0] a;
|
||||
} out_t /*verilator public*/;
|
||||
|
||||
// struct in1_t should cover parts of VL_ASSIGNSEL_II functions
|
||||
typedef struct packed {
|
||||
logic [3:0] a;
|
||||
logic [11:0] b;
|
||||
} in1_t /*verilator public*/; // 4 + 12 = 16
|
||||
|
||||
typedef struct packed {
|
||||
logic [11:0] b;
|
||||
logic [3:0] a;
|
||||
} out1_t /*verilator public*/;
|
||||
|
||||
// struct in2_t should cover all VL_ASSIGNSEL_II functions
|
||||
typedef struct packed {
|
||||
logic [2:0] a;
|
||||
logic [8:0] b;
|
||||
logic [18:0] c;
|
||||
} in2_t /*verilator public*/; // 3 + 9 + 19 = 31
|
||||
|
||||
typedef struct packed {
|
||||
logic [8:0] b;
|
||||
logic [18:0] c;
|
||||
logic [2:0] a;
|
||||
} out2_t /*verilator public*/;
|
||||
|
||||
// struct in3_t should cover all VL_ASSIGNSEL_XQ functions
|
||||
typedef struct packed {
|
||||
logic [1:0] a;
|
||||
logic [8:0] b;
|
||||
logic [16:0] c;
|
||||
logic [32:0] d;
|
||||
} in3_t /*verilator public*/; // 33 + 17 + 9 + 2 = 61
|
||||
|
||||
typedef struct packed {
|
||||
logic [8:0] b;
|
||||
logic [1:0] a;
|
||||
logic [32:0] d;
|
||||
logic [16:0] c;
|
||||
} out3_t /*verilator public*/;
|
||||
|
||||
// struct in4_t should cover all VL_ASSIGNSEL_XW functions
|
||||
typedef struct packed {
|
||||
logic [4:0] a;
|
||||
logic [12:0] b;
|
||||
logic [24:0] c;
|
||||
logic [48:0] d;
|
||||
logic [80:0] e;
|
||||
} in4_t /*verilator public*/; // 5 + 13 + 25 + 49 + 81 = 173
|
||||
|
||||
typedef struct packed {
|
||||
logic [24:0] c;
|
||||
logic [48:0] d;
|
||||
logic [80:0] e;
|
||||
logic [4:0] a;
|
||||
logic [12:0] b;
|
||||
} out4_t /*verilator public*/;
|
||||
|
||||
module add (
|
||||
input in_t op1,
|
||||
input in_t op2,
|
||||
output out_t out,
|
||||
// Add some extra ports to test all VL_ASSIGNSEL_XX functions
|
||||
input in1_t op1a,
|
||||
input in1_t op1b,
|
||||
output out1_t out1,
|
||||
// Add some extra ports to test all VL_ASSIGNSEL_XX functions
|
||||
input in2_t op2a,
|
||||
input in2_t op2b,
|
||||
output out2_t out2,
|
||||
// Add some extra ports to test all VL_ASSIGNSEL_XX functions
|
||||
input in3_t op3a,
|
||||
input in3_t op3b,
|
||||
output out3_t out3,
|
||||
// Add some extra ports to test all VL_ASSIGNSEL_XX functions
|
||||
input in4_t op4a,
|
||||
input in4_t op4b,
|
||||
output out4_t out4
|
||||
);
|
||||
assign out.a = op1.a + op2.a;
|
||||
generate
|
||||
for (genvar i = 0; i < 4; ++i) begin
|
||||
assign out.nullptr[i] = op1.nullptr[i] + op2.nullptr[i];
|
||||
end
|
||||
endgenerate
|
||||
assign out.get.a = op1.get.a + op2.get.a;
|
||||
|
||||
// out1
|
||||
assign out1.a = op1a.a + op1b.a;
|
||||
assign out1.b = op1a.b + op1b.b;
|
||||
|
||||
// out2
|
||||
assign out2.a = op2a.a + op2b.a;
|
||||
assign out2.b = op2a.b + op2b.b;
|
||||
assign out2.c = op2a.c + op2b.c;
|
||||
|
||||
// out3
|
||||
assign out3.a = op3a.a + op3b.a;
|
||||
assign out3.b = op3a.b + op3b.b;
|
||||
assign out3.c = op3a.c + op3b.c;
|
||||
assign out3.d = op3a.d + op3b.d;
|
||||
|
||||
// out4
|
||||
assign out4.a = op4a.a + op4b.a;
|
||||
assign out4.b = op4a.b + op4b.b;
|
||||
assign out4.c = op4a.c + op4b.c;
|
||||
assign out4.d = op4a.d + op4b.d;
|
||||
assign out4.e = op4a.e + op4b.e;
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// Copyright 2024 by Kefa Chen. This program is free software; you can
|
||||
// redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//*************************************************************************
|
||||
|
||||
#include <verilated.h>
|
||||
|
||||
#include <ios>
|
||||
#include <iostream>
|
||||
|
||||
#include VM_PREFIX_INCLUDE
|
||||
#include VM_PREFIX_ROOT_INCLUDE
|
||||
|
||||
#include "TestCheck.h"
|
||||
#include "Vt_export_packed_struct2___024unit__03a__03acls_in__Vclpkg.h"
|
||||
|
||||
/*
|
||||
// Packed struct in package
|
||||
package TEST_TYPES;
|
||||
typedef union packed {
|
||||
logic [64:0] a;
|
||||
logic [2:0] b;
|
||||
} sub_t;
|
||||
typedef struct packed {
|
||||
struct packed { // Anonymous packed struct
|
||||
logic a;
|
||||
} anon;
|
||||
TEST_TYPES::sub_t [2:0][2:0][2:0] b;
|
||||
} in_t;
|
||||
typedef struct packed {
|
||||
TEST_TYPES::sub_t [2:0][2:0][2:0] b;
|
||||
struct packed {logic a;} anon;
|
||||
} out_t;
|
||||
endpackage
|
||||
|
||||
// Packed struct in class
|
||||
class cls_in;
|
||||
typedef struct packed {
|
||||
logic a;
|
||||
TEST_TYPES::sub_t [2:0][2:0][2:0] b;
|
||||
} in_t;
|
||||
in_t in;
|
||||
endclass //cls
|
||||
*/
|
||||
|
||||
#define CONCAT_IMPL(a, b) a##b
|
||||
#define CONCAT(a, b) CONCAT_IMPL(a, b)
|
||||
#define CONCAT5(a, b, c, d, e) CONCAT(CONCAT(CONCAT(CONCAT(a, b), c), d), e)
|
||||
#define EXPORTED_STRUCT_NAME(STRUCT_NAME, NUMBER) \
|
||||
CONCAT5(VM_PREFIX, _, STRUCT_NAME, __struct__, NUMBER)
|
||||
#define EXPORTED_UNION_NAME(UNION_NAME, NUMBER) \
|
||||
CONCAT5(VM_PREFIX, _, UNION_NAME, __union__, NUMBER)
|
||||
#define SUB_T EXPORTED_UNION_NAME(sub_t, 0)
|
||||
#define IN_T EXPORTED_STRUCT_NAME(in_t, 0)
|
||||
#define IN2_T EXPORTED_STRUCT_NAME(in_t, 1)
|
||||
#define OUT_T EXPORTED_STRUCT_NAME(out_t, 0)
|
||||
|
||||
int errors = 0;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
||||
contextp->debug(0);
|
||||
contextp->randReset(2);
|
||||
contextp->commandArgs(argc, argv);
|
||||
|
||||
const std::unique_ptr<VM_PREFIX> adder{new VM_PREFIX{contextp.get()}};
|
||||
|
||||
{
|
||||
IN_T in;
|
||||
IN2_T tmp;
|
||||
OUT_T out;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
VL_SET_WQ(in.b[i][j][k].a, 0x1234123412341234UL);
|
||||
// Set last bit zero and upper bits one
|
||||
in.b[i][j][k].a[2] = 0xfe;
|
||||
}
|
||||
}
|
||||
}
|
||||
in.anon.a = 0x1;
|
||||
|
||||
adder->op1 = in.get();
|
||||
adder->eval();
|
||||
out.set(adder->out);
|
||||
|
||||
std::memset(reinterpret_cast<void*>(&tmp), 0xff, sizeof(tmp));
|
||||
// `set` function should clear upper bits of `tmp.a`
|
||||
tmp.set(adder->rootp->add__DOT__op2->__PVT__in);
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
TEST_CHECK_EQ(tmp.b[i][j][k].a[0], 0x12341234);
|
||||
TEST_CHECK_EQ(tmp.b[i][j][k].a[1], 0x12341234);
|
||||
TEST_CHECK_EQ(tmp.b[i][j][k].a[2], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
TEST_CHECK_EQ(tmp.a, 0x1);
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
TEST_CHECK_EQ(out.b[i][j][k].a[0], 0x24682468);
|
||||
TEST_CHECK_EQ(out.b[i][j][k].a[1], 0x24682468);
|
||||
TEST_CHECK_EQ(out.b[i][j][k].a[2], 0x0);
|
||||
}
|
||||
}
|
||||
}
|
||||
TEST_CHECK_EQ(out.anon.a, 0x0);
|
||||
}
|
||||
|
||||
printf("*-* All Finished *-*\n");
|
||||
return errors;
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2024 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
scenarios(vlt => 1);
|
||||
|
||||
compile(
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
verilator_flags2 => ["--exe $Self->{t_dir}/$Self->{name}.cpp"],
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2024 by Kefa Chen.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Packed struct in package
|
||||
package TEST_TYPES;
|
||||
typedef union packed {
|
||||
logic [64:0] a;
|
||||
logic [2:0] b;
|
||||
} sub_t;
|
||||
typedef struct packed {
|
||||
struct packed { // Anonymous packed struct
|
||||
logic a;
|
||||
} anon;
|
||||
TEST_TYPES::sub_t [2:0][2:0][2:0] b;
|
||||
} in_t /*verilator public*/;
|
||||
typedef struct packed {
|
||||
TEST_TYPES::sub_t [2:0][2:0][2:0] b;
|
||||
struct packed {logic a;} anon;
|
||||
} out_t /*verilator public*/;
|
||||
endpackage
|
||||
|
||||
// Packed struct in class
|
||||
class cls_in;
|
||||
typedef struct packed {
|
||||
logic a;
|
||||
TEST_TYPES::sub_t [2:0][2:0][2:0] b;
|
||||
} in_t /*verilator public*/;
|
||||
in_t in;
|
||||
endclass //cls
|
||||
|
||||
module add (
|
||||
input TEST_TYPES::in_t op1,
|
||||
//input cls_in op2,
|
||||
output TEST_TYPES::out_t out
|
||||
);
|
||||
cls_in op2 /*verilator public_flat*/;
|
||||
|
||||
initial begin
|
||||
if(op2 != null) $stop;
|
||||
op2 = new();
|
||||
if(!op2) $stop;
|
||||
end
|
||||
|
||||
assign op2.in.a = op1.anon.a;
|
||||
generate
|
||||
for (genvar i = 0; i < 3; ++i) begin
|
||||
for (genvar j = 0; j < 3; ++j) begin
|
||||
for (genvar k = 0; k < 3; ++k) begin
|
||||
assign op2.in.b[i][j][k] = op1.b[i][j][k];
|
||||
end
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
assign out.anon.a = op1.anon.a + op2.in.a;
|
||||
generate
|
||||
for (genvar i = 0; i < 3; ++i) begin
|
||||
for (genvar j = 0; j < 3; ++j) begin
|
||||
for (genvar k = 0; k < 3; ++k) begin
|
||||
assign out.b[i][j][k] = op1.b[i][j][k] + op2.in.b[i][j][k];
|
||||
end
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule
|
||||
Loading…
Reference in New Issue