Support public packed struct / union (#860) (#4878)

This commit is contained in:
Kefa Chen 2024-03-03 23:23:04 +08:00 committed by GitHub
parent 5e1fc6e24d
commit 5f1dc73a1b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 1015 additions and 43 deletions

View File

@ -103,6 +103,7 @@ Justin Thiel
Kaleb Barrett
Kamil Rakoczy
Kanad Kanhere
Kefa Chen
Keith Colbert
Kevin Kiningham
Kritik Bhimani

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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