mirror of
https://github.com/verilator/verilator.git
synced 2024-12-29 10:47:34 +00:00
Support unpacked structs (#3802)
This commit is contained in:
parent
c3b4bc85fa
commit
c2b09e35f8
@ -416,6 +416,7 @@ detailed descriptions of these arguments.
|
||||
--no-skip-identical Disable skipping identical output
|
||||
--stats Create statistics file
|
||||
--stats-vars Provide statistics on variables
|
||||
--structs-packed Convert all unpacked structures to packed structures
|
||||
-sv Enable SystemVerilog parsing
|
||||
+systemverilogext+<ext> Synonym for +1800-2017ext+<ext>
|
||||
--threads <threads> Enable multithreading
|
||||
|
@ -1203,11 +1203,10 @@ Summary:
|
||||
|
||||
.. option:: --structs-packed
|
||||
|
||||
Converts all unpacked structures to packed structures and issues an
|
||||
:option:`UNPACKED` warning. Currently, this is the default, and
|
||||
:vlopt:`--no-structs-packed <--structs-packed>` will not work.
|
||||
Specifying this option allows forward compatibility when a future
|
||||
version of Verilator no longer always packs unpacked structures.
|
||||
Converts all unpacked structures to packed structures, and issues an
|
||||
:option:`UNPACKED` warning. Specifying this option allows for backward
|
||||
compatibility with versions before Verilator 5.006, when Verilator would
|
||||
always pack unpacked structures.
|
||||
|
||||
.. option:: -sv
|
||||
|
||||
|
@ -219,7 +219,7 @@ public:
|
||||
int uniqueNum() const { return m_uniqueNum; }
|
||||
const char* broken() const override;
|
||||
void dump(std::ostream& str) const override;
|
||||
bool isCompound() const override { return false; } // Because don't support unpacked
|
||||
bool isCompound() const override { return !packed(); }
|
||||
// For basicp() we reuse the size to indicate a "fake" basic type of same size
|
||||
AstBasicDType* basicp() const override {
|
||||
return (isFourstate()
|
||||
@ -241,6 +241,7 @@ public:
|
||||
string name() const override { return m_name; }
|
||||
void name(const string& flag) override { m_name = flag; }
|
||||
bool packed() const VL_MT_SAFE { return m_packed; }
|
||||
void packed(bool flag) { m_packed = flag; }
|
||||
// packed() but as don't support unpacked, presently all structs
|
||||
static bool packedUnsup() { return true; }
|
||||
void isFourstate(bool flag) { m_isFourstate = flag; }
|
||||
@ -1274,12 +1275,15 @@ public:
|
||||
|
||||
// === AstNodeUOrStructDType ===
|
||||
class AstStructDType final : public AstNodeUOrStructDType {
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
|
||||
public:
|
||||
// VSigning below is mispurposed to indicate if packed or not
|
||||
AstStructDType(FileLine* fl, VSigning numericUnpack)
|
||||
: ASTGEN_SUPER_StructDType(fl, numericUnpack) {}
|
||||
ASTGEN_MEMBERS_AstStructDType;
|
||||
string verilogKwd() const override { return "struct"; }
|
||||
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstNodeModule* classpackagep) { m_classOrPackagep = classpackagep; }
|
||||
};
|
||||
class AstUnionDType final : public AstNodeUOrStructDType {
|
||||
public:
|
||||
|
@ -1778,6 +1778,31 @@ public:
|
||||
bool cleanOut() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstStructSel final : public AstNodeExpr {
|
||||
// Unpacked struct member access
|
||||
// Parents: math|stmt
|
||||
// Children: varref, math
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
private:
|
||||
string m_name; // Name of the member
|
||||
public:
|
||||
AstStructSel(FileLine* fl, AstNodeExpr* fromp, const string& name)
|
||||
: ASTGEN_SUPER_StructSel(fl)
|
||||
, m_name{name} {
|
||||
this->fromp(fromp);
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
}
|
||||
ASTGEN_MEMBERS_AstStructSel;
|
||||
string name() const override { return m_name; }
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstStructSel* const sp = static_cast<const AstStructSel*>(samep);
|
||||
return m_name == sp->m_name;
|
||||
}
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
};
|
||||
class AstSysIgnore final : public AstNodeExpr {
|
||||
// @astgen op1 := exprsp : List[AstNode] // Expressions to output (???)
|
||||
public:
|
||||
|
@ -734,6 +734,9 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
info.m_type = "VlUnpacked<" + sub.m_type;
|
||||
info.m_type += ", " + cvtToStr(adtypep->declRange().elements());
|
||||
info.m_type += ">";
|
||||
} else if (VN_IS(dtypep, StructDType) && !VN_AS(dtypep, StructDType)->packed()) {
|
||||
const auto* const sdtypep = VN_AS(dtypep, StructDType);
|
||||
info.m_type = EmitCBaseVisitor::prefixNameProtect(sdtypep);
|
||||
} else if (const AstBasicDType* const bdtypep = dtypep->basicp()) {
|
||||
// We don't print msb()/lsb() as multidim packed would require recursion,
|
||||
// and may confuse users as C++ data is stored always with bit 0 used
|
||||
|
@ -73,6 +73,13 @@ class CUseVisitor final : public VNVisitor {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep());
|
||||
if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p());
|
||||
|
||||
// Add a CUse for every struct that requires a declaration
|
||||
AstStructDType* const stypep = VN_CAST(nodep->skipRefp(), StructDType);
|
||||
if (stypep && stypep->classOrPackagep()) {
|
||||
addNewUse(nodep, VUseType::INT_INCLUDE, stypep->classOrPackagep()->name());
|
||||
iterateChildren(stypep);
|
||||
}
|
||||
}
|
||||
void visit(AstNode* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
|
@ -159,7 +159,7 @@ private:
|
||||
const AstNode* const backp = nodep->backp();
|
||||
if (nodep->access().isReadOnly() && VN_IS(backp, NodeExpr) && !VN_IS(backp, CCast)
|
||||
&& !VN_IS(backp, NodeCCall) && !VN_IS(backp, CMethodHard) && !VN_IS(backp, SFormatF)
|
||||
&& !VN_IS(backp, ArraySel) && !VN_IS(backp, RedXor)
|
||||
&& !VN_IS(backp, ArraySel) && !VN_IS(backp, StructSel) && !VN_IS(backp, RedXor)
|
||||
&& (nodep->varp()->basicp() && !nodep->varp()->basicp()->isTriggerVec()
|
||||
&& !nodep->varp()->basicp()->isForkSync())
|
||||
&& backp->width() && castSize(nodep) != castSize(nodep->varp())) {
|
||||
|
@ -41,6 +41,7 @@ private:
|
||||
|
||||
// MEMBERS
|
||||
string m_prefix; // String prefix to add to name based on hier
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstNodeModule* m_classPackagep = nullptr; // Package moving into
|
||||
const AstScope* m_classScopep = nullptr; // Package moving scopes into
|
||||
AstScope* m_packageScopep = nullptr; // Class package scope
|
||||
@ -92,7 +93,9 @@ private:
|
||||
VL_RESTORER(m_classPackagep);
|
||||
VL_RESTORER(m_classScopep);
|
||||
VL_RESTORER(m_packageScopep);
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
m_modp = nodep;
|
||||
m_classPackagep = packagep;
|
||||
m_classScopep = classScopep;
|
||||
m_packageScopep = scopep;
|
||||
@ -104,7 +107,9 @@ private:
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
// Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule)
|
||||
VL_RESTORER(m_prefix);
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
m_modp = nodep;
|
||||
m_prefix = nodep->name() + "__03a__03a"; // ::
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
@ -166,6 +171,30 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void setStructModulep(AstStructDType* const dtypep) {
|
||||
// Give it a pointer to its package and a final name
|
||||
dtypep->classOrPackagep(m_modp);
|
||||
dtypep->name(dtypep->name() + "__struct" + cvtToStr(dtypep->uniqueNum()));
|
||||
|
||||
for (const AstMemberDType* itemp = dtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
AstStructDType* const subp = VN_CAST(itemp->skipRefp(), StructDType);
|
||||
// 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); }
|
||||
}
|
||||
}
|
||||
void visit(AstTypedef* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
|
||||
AstStructDType* const dtypep = VN_CAST(nodep->dtypep(), StructDType);
|
||||
if (dtypep && !dtypep->packed()) {
|
||||
dtypep->name(nodep->name());
|
||||
setStructModulep(dtypep);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstNodeExpr* nodep) override {} // Short circuit
|
||||
void visit(AstNodeStmt* nodep) override {} // Short circuit
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
@ -97,7 +97,9 @@ private:
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) {
|
||||
} else {
|
||||
setCppWidth(nodep);
|
||||
const AstStructDType* const dtypep
|
||||
= VN_CAST(nodep->dtypep()->skipRefp(), StructDType);
|
||||
if (!dtypep || dtypep->packed()) { setCppWidth(nodep); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +61,37 @@ static void makeVlToString(AstIface* nodep) {
|
||||
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
|
||||
nodep->addStmtsp(funcp);
|
||||
}
|
||||
static void makeVlToString(AstStructDType* nodep) {
|
||||
AstNodeModule* const modp = nodep->classOrPackagep();
|
||||
AstCFunc* const funcp
|
||||
= new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"};
|
||||
funcp->argTypes("const " + EmitCBaseVisitor::prefixNameProtect(nodep) + "& obj");
|
||||
funcp->isMethod(false);
|
||||
funcp->isConst(false);
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "std::string out;\n"});
|
||||
for (const AstMemberDType* itemp = nodep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
std::string stmt = "out += \"";
|
||||
if (itemp == nodep->membersp()) {
|
||||
stmt += "'{";
|
||||
} else {
|
||||
stmt += ", ";
|
||||
}
|
||||
stmt += itemp->nameProtect() + ":\" + ";
|
||||
if (VN_IS(itemp->dtypep()->skipRefp(), BasicDType) && itemp->isWide()) {
|
||||
stmt += "VL_TO_STRING_W";
|
||||
} else {
|
||||
stmt += "VL_TO_STRING";
|
||||
}
|
||||
stmt += "(obj." + itemp->nameProtect() + ");\n";
|
||||
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
|
||||
}
|
||||
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "out += \"}\";\n"});
|
||||
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "return out;\n"});
|
||||
modp->addStmtsp(funcp);
|
||||
}
|
||||
static void makeToString(AstClass* nodep) {
|
||||
AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "to_string", nullptr, "std::string"};
|
||||
funcp->isConst(true);
|
||||
@ -135,5 +166,11 @@ void V3Common::commonAll() {
|
||||
makeVlToString(ifacep);
|
||||
}
|
||||
}
|
||||
for (AstNode* nodep = v3Global.rootp()->typeTablep()->typesp(); nodep;
|
||||
nodep = nodep->nextp()) {
|
||||
if (AstStructDType* const dtypep = VN_CAST(nodep, StructDType)) {
|
||||
if (!dtypep->packed()) makeVlToString(dtypep);
|
||||
}
|
||||
}
|
||||
V3Global::dumpCheckGlobalTree("common", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
@ -354,18 +354,34 @@ private:
|
||||
newent.cleanup();
|
||||
}
|
||||
} else if (const AstStructDType* const adtypep = VN_CAST(dtypep, StructDType)) {
|
||||
// For now it's packed, so similar to array
|
||||
for (AstMemberDType* itemp = adtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
AstNodeDType* const subtypep = itemp->subDTypep()->skipRefp();
|
||||
const int index_code = itemp->lsb();
|
||||
ToggleEnt newent{above.m_comment + std::string{"."} + itemp->name(),
|
||||
new AstSel{varp->fileline(), above.m_varRefp->cloneTree(true),
|
||||
index_code, subtypep->width()},
|
||||
new AstSel{varp->fileline(), above.m_chgRefp->cloneTree(true),
|
||||
index_code, subtypep->width()}};
|
||||
toggleVarRecurse(subtypep, depth + 1, newent, varp, chgVarp);
|
||||
newent.cleanup();
|
||||
if (adtypep->packed()) {
|
||||
for (AstMemberDType* itemp = adtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
AstNodeDType* const subtypep = itemp->subDTypep()->skipRefp();
|
||||
const int index_code = itemp->lsb();
|
||||
ToggleEnt newent{above.m_comment + std::string{"."} + itemp->name(),
|
||||
new AstSel{varp->fileline(), above.m_varRefp->cloneTree(true),
|
||||
index_code, subtypep->width()},
|
||||
new AstSel{varp->fileline(), above.m_chgRefp->cloneTree(true),
|
||||
index_code, subtypep->width()}};
|
||||
toggleVarRecurse(subtypep, depth + 1, newent, varp, chgVarp);
|
||||
newent.cleanup();
|
||||
}
|
||||
} else {
|
||||
for (AstMemberDType* itemp = adtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
AstNodeDType* const subtypep = itemp->subDTypep()->skipRefp();
|
||||
AstNodeExpr* const varRefp = new AstStructSel{
|
||||
varp->fileline(), above.m_varRefp->cloneTree(true), itemp->name()};
|
||||
AstNodeExpr* const chgRefp = new AstStructSel{
|
||||
varp->fileline(), above.m_varRefp->cloneTree(true), itemp->name()};
|
||||
varRefp->dtypep(subtypep);
|
||||
chgRefp->dtypep(subtypep);
|
||||
ToggleEnt newent{above.m_comment + std::string{"."} + itemp->name(), varRefp,
|
||||
chgRefp};
|
||||
toggleVarRecurse(subtypep, depth + 1, newent, varp, chgVarp);
|
||||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
} else if (const AstUnionDType* const adtypep = VN_CAST(dtypep, UnionDType)) {
|
||||
// Arbitrarily handle only the first member of the union
|
||||
|
@ -72,6 +72,7 @@ private:
|
||||
std::vector<AstScope*> m_scopesp;
|
||||
std::vector<AstCell*> m_cellsp;
|
||||
std::vector<AstClass*> m_classesp;
|
||||
std::vector<AstTypedef*> m_typedefsp;
|
||||
|
||||
AssignMap m_assignMap; // List of all simple assignments for each variable
|
||||
const bool m_elimUserVars; // Allow removal of user's vars
|
||||
@ -234,6 +235,11 @@ private:
|
||||
if (nodep->fromp()->dtypep()) nodep->fromp()->dtypep()->user1Inc(); // classref
|
||||
checkAll(nodep);
|
||||
}
|
||||
void visit(AstStructSel* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (nodep->fromp()->dtypep()) nodep->fromp()->dtypep()->user1Inc(); // structdtype
|
||||
checkAll(nodep);
|
||||
}
|
||||
void visit(AstModport* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_elimCells) {
|
||||
@ -253,11 +259,8 @@ private:
|
||||
}
|
||||
void visit(AstTypedef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_elimCells && !nodep->attrPublic()) {
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
checkAll(nodep);
|
||||
m_typedefsp.push_back(nodep);
|
||||
|
||||
// Don't let packages with only public variables disappear
|
||||
// Normal modules may disappear, e.g. if they are parameterized then removed
|
||||
if (nodep->attrPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc();
|
||||
@ -306,6 +309,22 @@ private:
|
||||
}
|
||||
|
||||
// METHODS
|
||||
void deadCheckTypedefs() {
|
||||
for (AstTypedef* typedefp : m_typedefsp) {
|
||||
if (shouldDeleteTypedef(typedefp)) {
|
||||
VL_DO_DANGLING(pushDeletep(typedefp->unlinkFrBack()), typedefp);
|
||||
continue;
|
||||
}
|
||||
checkAll(typedefp);
|
||||
}
|
||||
}
|
||||
bool shouldDeleteTypedef(AstTypedef* typedefp) {
|
||||
if (auto* structp = VN_CAST(typedefp->subDTypep(), StructDType)) {
|
||||
if (structp->user1() && !structp->packed()) return false;
|
||||
}
|
||||
return m_elimCells && !typedefp->attrPublic();
|
||||
}
|
||||
|
||||
void deadCheckMod() {
|
||||
// Kill any unused modules
|
||||
// V3LinkCells has a graph that is capable of this too, but we need to do it
|
||||
@ -490,6 +509,7 @@ public:
|
||||
vscp->varp()->user1Inc();
|
||||
}
|
||||
|
||||
deadCheckTypedefs();
|
||||
deadCheckVar();
|
||||
// We only eliminate scopes when in a flattened structure
|
||||
// Otherwise we have no easy way to know if a scope is used
|
||||
|
@ -670,6 +670,17 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
|
||||
depth + 1, suffix + "[" + ivar + "]");
|
||||
const string post = "}\n";
|
||||
return below.empty() ? "" : pre + below + post;
|
||||
} else if (VN_IS(dtypep, StructDType) && !VN_AS(dtypep, StructDType)->packed()) {
|
||||
const auto* const sdtypep = VN_AS(dtypep, StructDType);
|
||||
string literal;
|
||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
const std::string line
|
||||
= emitVarResetRecurse(varp, varNameProtected + suffix + "." + itemp->nameProtect(),
|
||||
itemp->dtypep(), depth + 1, "");
|
||||
if (!line.empty()) literal += line;
|
||||
}
|
||||
return literal;
|
||||
} else if (basicp && basicp->keyword() == VBasicDTypeKwd::STRING) {
|
||||
// String's constructor deals with it
|
||||
return "";
|
||||
|
@ -1066,6 +1066,11 @@ public:
|
||||
putbs("->");
|
||||
puts(nodep->varp()->nameProtect());
|
||||
}
|
||||
void visit(AstStructSel* nodep) override {
|
||||
iterateAndNextNull(nodep->fromp());
|
||||
putbs(".");
|
||||
puts(nodep->nameProtect());
|
||||
}
|
||||
void visit(AstNullCheck* nodep) override {
|
||||
puts("VL_NULL_CHECK(");
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
|
@ -208,6 +208,44 @@ class EmitCHeader final : public EmitCConstInit {
|
||||
}
|
||||
}
|
||||
}
|
||||
void emitStructDecl(const AstNodeModule* modp, AstStructDType* sdtypep,
|
||||
std::set<AstStructDType*>& emitted) {
|
||||
if (emitted.count(sdtypep) > 0) return;
|
||||
emitted.insert(sdtypep);
|
||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
AstStructDType* subp = VN_CAST(itemp->skipRefp(), StructDType);
|
||||
if (subp && !subp->packed()) {
|
||||
// Recurse if it belongs to the current module
|
||||
if (subp->classOrPackagep() == modp) {
|
||||
emitStructDecl(modp, subp, emitted);
|
||||
puts("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
puts("struct " + EmitCBaseVisitor::prefixNameProtect(sdtypep) + " {\n");
|
||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
puts(itemp->dtypep()->cType(itemp->nameProtect(), false, false));
|
||||
puts(";\n");
|
||||
}
|
||||
puts("};\n");
|
||||
}
|
||||
void emitStructs(const AstNodeModule* modp) {
|
||||
bool first = true;
|
||||
// Track structs that've been emitted already
|
||||
std::set<AstStructDType*> emitted;
|
||||
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
const AstTypedef* const tdefp = VN_CAST(nodep, Typedef);
|
||||
if (!tdefp) continue;
|
||||
AstStructDType* const sdtypep
|
||||
= VN_CAST(tdefp->dtypep()->skipRefToEnump(), StructDType);
|
||||
if (!sdtypep) continue;
|
||||
if (sdtypep->packed()) continue;
|
||||
decorateFirst(first, "\n// UNPACKED STRUCT TYPES\n");
|
||||
emitStructDecl(modp, sdtypep, emitted);
|
||||
}
|
||||
}
|
||||
void emitFuncDecls(const AstNodeModule* modp, bool inClassBody) {
|
||||
std::vector<const AstCFunc*> funcsp;
|
||||
|
||||
@ -252,6 +290,8 @@ class EmitCHeader final : public EmitCConstInit {
|
||||
// From `systemc_header
|
||||
emitTextSection(modp, VNType::atScHdr);
|
||||
|
||||
emitStructs(modp);
|
||||
|
||||
// Open class body {{{
|
||||
puts("\nclass ");
|
||||
puts(prefixNameProtect(modp));
|
||||
|
@ -50,6 +50,11 @@ class EmitCGatherDependencies final : VNVisitor {
|
||||
if (const AstClassRefDType* const dtypep = VN_CAST(nodep, ClassRefDType)) {
|
||||
m_dependencies.insert(
|
||||
EmitCBaseVisitor::prefixNameProtect(dtypep->classp()->classOrPackagep()));
|
||||
} else if (const AstStructDType* const dtypep = VN_CAST(nodep, StructDType)) {
|
||||
if (!dtypep->packed()) {
|
||||
m_dependencies.insert(
|
||||
EmitCBaseVisitor::prefixNameProtect(dtypep->classOrPackagep()));
|
||||
}
|
||||
}
|
||||
}
|
||||
void addSelfDependency(const string& selfPointer, AstNode* nodep) {
|
||||
@ -92,6 +97,10 @@ class EmitCGatherDependencies final : VNVisitor {
|
||||
addDTypeDependency(nodep->fromp()->dtypep());
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstStructSel* nodep) override {
|
||||
addDTypeDependency(nodep->fromp()->dtypep());
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
addSelfDependency(nodep->selfPointer(), nodep->varp());
|
||||
iterateChildrenConst(nodep);
|
||||
|
@ -1406,7 +1406,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
m_statsVars = flag;
|
||||
m_stats |= flag;
|
||||
});
|
||||
DECL_OPTION("-structs-unpacked", OnOff, &m_structsPacked);
|
||||
DECL_OPTION("-structs-packed", OnOff, &m_structsPacked);
|
||||
DECL_OPTION("-sv", CbCall, [this]() { m_defaultLanguage = V3LangCode::L1800_2017; });
|
||||
|
||||
DECL_OPTION("-threads-coarsen", OnOff, &m_threadsCoarsen).undocumented(); // Debug
|
||||
|
@ -267,7 +267,7 @@ private:
|
||||
bool m_relativeIncludes = false; // main switch: --relative-includes
|
||||
bool m_reportUnoptflat = false; // main switch: --report-unoptflat
|
||||
bool m_savable = false; // main switch: --savable
|
||||
bool m_structsPacked = true; // main switch: --structs-packed
|
||||
bool m_structsPacked = false; // main switch: --structs-packed
|
||||
bool m_systemC = false; // main switch: --sc: System C instead of simple C++
|
||||
bool m_stats = false; // main switch: --stats
|
||||
bool m_statsVars = false; // main switch: --stats-vars
|
||||
|
@ -421,7 +421,28 @@ private:
|
||||
// a much faster way to trace
|
||||
addTraceDecl(VNumRange{}, nodep->width());
|
||||
} else if (!nodep->packed()) {
|
||||
addIgnore("Unsupported: Unpacked struct/union");
|
||||
if (VN_IS(nodep, UnionDType)) {
|
||||
addIgnore("Unsupported: Unpacked union");
|
||||
} else {
|
||||
FileLine* const flp = nodep->fileline();
|
||||
VL_RESTORER(m_traName);
|
||||
string prefix{m_traName};
|
||||
prefix += getScopeChar(VLT_TRACE_SCOPE_STRUCT);
|
||||
addToSubFunc(new AstTracePushNamePrefix{flp, prefix + ' '});
|
||||
for (const AstMemberDType* itemp = nodep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
AstNodeDType* const subtypep = itemp->subDTypep()->skipRefToEnump();
|
||||
m_traName = itemp->prettyName();
|
||||
VL_RESTORER(m_traValuep);
|
||||
m_traValuep = m_traValuep->cloneTree(false);
|
||||
m_traName = itemp->prettyName();
|
||||
m_traValuep = new AstStructSel{flp, m_traValuep, itemp->name()};
|
||||
m_traValuep->dtypep(subtypep);
|
||||
iterate(subtypep);
|
||||
VL_DO_CLEAR(m_traValuep->deleteTree(), m_traValuep = nullptr);
|
||||
}
|
||||
addToSubFunc(new AstTracePopNamePrefix{flp, 1});
|
||||
}
|
||||
} else {
|
||||
FileLine* const flp = nodep->fileline();
|
||||
const bool isStruct = VN_IS(nodep, StructDType); // Otherwise union
|
||||
|
@ -222,6 +222,7 @@ private:
|
||||
const AstWith* m_withp = nullptr; // Current 'with' statement
|
||||
const AstFunc* m_funcp = nullptr; // Current function
|
||||
const AstAttrOf* m_attrp = nullptr; // Current attribute
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
const bool m_paramsOnly; // Computing parameter value; limit operation
|
||||
const bool m_doGenerate; // Do errors later inside generate statement
|
||||
int m_dtTables = 0; // Number of created data type tables
|
||||
@ -2478,34 +2479,42 @@ private:
|
||||
UINFO(5, " NODECLASS " << nodep << endl);
|
||||
// if (debug() >= 9) nodep->dumpTree("- class-in: ");
|
||||
if (!nodep->packed()) {
|
||||
nodep->v3warn(UNPACKED, "Unsupported: Unpacked struct/union");
|
||||
if (!v3Global.opt.structsPacked()) {
|
||||
nodep->v3warn(UNPACKED, "Unsupported: --no-structs-packed");
|
||||
if (VN_IS(nodep, UnionDType)) {
|
||||
nodep->v3warn(UNPACKED, "Unsupported: Unpacked union");
|
||||
} else if (v3Global.opt.structsPacked()) {
|
||||
nodep->packed(true);
|
||||
}
|
||||
}
|
||||
userIterateChildren(nodep, nullptr); // First size all members
|
||||
nodep->repairMemberCache();
|
||||
// Determine bit assignments and width
|
||||
nodep->dtypep(nodep);
|
||||
int lsb = 0;
|
||||
int width = 0;
|
||||
nodep->isFourstate(false);
|
||||
// MSB is first, so go backwards
|
||||
AstMemberDType* itemp;
|
||||
for (itemp = nodep->membersp(); itemp && itemp->nextp();
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {}
|
||||
for (AstMemberDType* backip; itemp; itemp = backip) {
|
||||
if (itemp->isFourstate()) nodep->isFourstate(true);
|
||||
backip = VN_CAST(itemp->backp(), MemberDType);
|
||||
itemp->lsb(lsb);
|
||||
if (VN_IS(nodep, UnionDType)) {
|
||||
width = std::max(width, itemp->width());
|
||||
} else {
|
||||
lsb += itemp->width();
|
||||
width += itemp->width();
|
||||
// Determine bit assignments and width
|
||||
if (VN_IS(nodep, UnionDType) || nodep->packed()) {
|
||||
int lsb = 0;
|
||||
int width = 0;
|
||||
// MSB is first, so go backwards
|
||||
AstMemberDType* itemp;
|
||||
for (itemp = nodep->membersp(); itemp && itemp->nextp();
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {}
|
||||
for (AstMemberDType* backip; itemp; itemp = backip) {
|
||||
if (itemp->skipRefp()->isCompound())
|
||||
itemp->v3error(
|
||||
"Unpacked data type in packed struct/union (IEEE 1800-2017 7.2.1)");
|
||||
if (itemp->isFourstate()) nodep->isFourstate(true);
|
||||
backip = VN_CAST(itemp->backp(), MemberDType);
|
||||
itemp->lsb(lsb);
|
||||
if (VN_IS(nodep, UnionDType)) {
|
||||
width = std::max(width, itemp->width());
|
||||
} else {
|
||||
lsb += itemp->width();
|
||||
width += itemp->width();
|
||||
}
|
||||
}
|
||||
nodep->widthForce(width, width); // Signing stays as-is, as parsed from declaration
|
||||
} else {
|
||||
nodep->widthForce(1, 1);
|
||||
}
|
||||
nodep->widthForce(width, width); // Signing stays as-is, as parsed from declaration
|
||||
// if (debug() >= 9) nodep->dumpTree("- class-out: ");
|
||||
}
|
||||
void visit(AstClass* nodep) override {
|
||||
@ -2526,6 +2535,11 @@ private:
|
||||
// though causes problems with t_class_forward.v, so for now avoided
|
||||
// userIterateChildren(nodep->classp(), nullptr);
|
||||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
m_modp = nodep;
|
||||
userIterateChildren(nodep, nullptr);
|
||||
}
|
||||
void visit(AstClassOrPackageRef* nodep) override {
|
||||
if (nodep->didWidthAndSet()) return;
|
||||
userIterateChildren(nodep, nullptr);
|
||||
@ -2550,6 +2564,9 @@ private:
|
||||
nodep->dtypep(nodep); // The member itself, not subDtype
|
||||
nodep->widthFromSub(nodep->subDTypep());
|
||||
}
|
||||
void visit(AstStructSel* nodep) override {
|
||||
userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
|
||||
}
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
UINFO(5, " MEMBERSEL " << nodep << endl);
|
||||
if (debug() >= 9) nodep->dumpTree("- mbs-in: ");
|
||||
@ -2668,7 +2685,7 @@ private:
|
||||
nodep->dtypep(memberp);
|
||||
UINFO(9, " MEMBERSEL(attr) -> " << nodep << endl);
|
||||
UINFO(9, " dt-> " << nodep->dtypep() << endl);
|
||||
} else {
|
||||
} else if (adtypep->packed()) {
|
||||
AstSel* const newp = new AstSel{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
|
||||
memberp->lsb(), memberp->width()};
|
||||
// Must skip over the member to find the union; as the member may disappear later
|
||||
@ -2680,6 +2697,18 @@ private:
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
// Should be able to treat it as a normal-ish nodesel - maybe.
|
||||
// The lhsp() will be strange until this stage; create the number here?
|
||||
} else {
|
||||
AstStructSel* const newp = new AstStructSel{
|
||||
nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name()};
|
||||
// Must skip over the member to find the union; as the member may disappear later
|
||||
newp->dtypep(memberp->subDTypep()->skipRefToEnump());
|
||||
newp->didWidth(true); // Don't replace dtype with basic type
|
||||
UINFO(9, " MEMBERSEL -> " << newp << endl);
|
||||
UINFO(9, " dt-> " << newp->dtypep() << endl);
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
// Should be able to treat it as a normal-ish nodesel - maybe.
|
||||
// The lhsp() will be strange until this stage; create the number here?
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -4561,7 +4590,9 @@ private:
|
||||
|| VN_IS(dtypep, WildcardArrayDType) //
|
||||
|| VN_IS(dtypep, ClassRefDType) //
|
||||
|| VN_IS(dtypep, DynArrayDType) //
|
||||
|| VN_IS(dtypep, QueueDType)) {
|
||||
|| VN_IS(dtypep, QueueDType)
|
||||
|| (VN_IS(dtypep, StructDType)
|
||||
&& !VN_AS(dtypep, StructDType)->packed())) {
|
||||
added = true;
|
||||
newFormat += "%@";
|
||||
VNRelinker handle;
|
||||
|
@ -2104,10 +2104,8 @@ member_decl_assignment<memberDTypep>: // Derived from IEEE: variable_decl_assi
|
||||
// // At present we allow only packed structures/unions.
|
||||
// // So this is different from variable_decl_assignment
|
||||
id variable_dimensionListE
|
||||
{ if ($2) $2->v3warn(UNPACKED, "Unsupported: Unpacked array in packed struct/union"
|
||||
" (struct/union converted to unpacked)");
|
||||
$$ = new AstMemberDType{$<fl>1, *$1, VFlagChildDType{},
|
||||
AstNodeDType::cloneTreeNull(GRAMMARP->m_memDTypep, true)};
|
||||
{ $$ = new AstMemberDType{$<fl>1, *$1, VFlagChildDType{},
|
||||
GRAMMARP->createArray(AstNodeDType::cloneTreeNull(GRAMMARP->m_memDTypep, true), $2, false)};
|
||||
PARSEP->tagNodep($$);
|
||||
}
|
||||
| id variable_dimensionListE '=' variable_declExpr
|
||||
|
@ -32,7 +32,7 @@ module Vt_debug_emitv_t;
|
||||
???? // UNSIZEDARRAYDTYPE
|
||||
|
||||
???? // DYNARRAYDTYPE
|
||||
signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] logic [2:0] logic [0:0] e_t;
|
||||
signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] bit [0:0] logic [0:0] e_t;
|
||||
typedef struct packed
|
||||
{
|
||||
???? // REFDTYPE 'e_t'
|
||||
@ -56,7 +56,7 @@ module Vt_debug_emitv_t;
|
||||
???? // UNSIZEDARRAYDTYPE
|
||||
|
||||
???? // DYNARRAYDTYPE
|
||||
signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] logic [2:0] logic [0:0] ps_t;
|
||||
signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] bit [0:0] logic [0:0] ps_t;
|
||||
typedef struct
|
||||
{signed logic [2:0] a}logicunion
|
||||
{logic a}
|
||||
@ -77,7 +77,7 @@ module Vt_debug_emitv_t;
|
||||
???? // UNSIZEDARRAYDTYPE
|
||||
|
||||
???? // DYNARRAYDTYPE
|
||||
signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] logic [2:0] logic [0:0] us_t;
|
||||
signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] bit [0:0] logic [0:0] us_t;
|
||||
typedef union
|
||||
{logic a}
|
||||
???? // REFDTYPE 'ps_t'
|
||||
@ -97,7 +97,7 @@ module Vt_debug_emitv_t;
|
||||
???? // UNSIZEDARRAYDTYPE
|
||||
|
||||
???? // DYNARRAYDTYPE
|
||||
signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] logic [2:0] logic [0:0] union_t;
|
||||
signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed int [31:0] signed realstringIData [31:0] signed logic [31:0] signed int [31:0] bit [0:0] logic [0:0] union_t;
|
||||
struct packed
|
||||
{
|
||||
???? // REFDTYPE 'e_t'
|
||||
|
22
test_regress/t/t_flag_structs_packed.pl
Executable file
22
test_regress/t/t_flag_structs_packed.pl
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2022 by Antmicro Ltd. 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(linter => 1);
|
||||
|
||||
compile(
|
||||
verilator_flags2 => ['--structs-packed'],
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
5
test_regress/t/t_flag_structs_packed_bad.out
Normal file
5
test_regress/t/t_flag_structs_packed_bad.out
Normal file
@ -0,0 +1,5 @@
|
||||
%Error: t/t_flag_structs_packed.v:14:19: Unpacked data type in packed struct/union (IEEE 1800-2017 7.2.1)
|
||||
: ... In instance x
|
||||
14 | notpacked_t b;
|
||||
| ^
|
||||
%Error: Exiting due to
|
@ -8,6 +8,8 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
top_filename("t/t_flag_structs_packed.v");
|
||||
|
||||
scenarios(linter => 1);
|
||||
|
||||
lint(
|
2
test_regress/t/t_package_struct.out
Normal file
2
test_regress/t/t_package_struct.out
Normal file
@ -0,0 +1,2 @@
|
||||
hello, world (0, 0)
|
||||
*-* All Finished *-*
|
22
test_regress/t/t_package_struct.pl
Executable file
22
test_regress/t/t_package_struct.pl
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2022 by Antmicro Ltd. 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(simulator => 1);
|
||||
|
||||
compile(
|
||||
);
|
||||
|
||||
execute(
|
||||
expect_filename => $Self->{golden_filename},
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
25
test_regress/t/t_package_struct.v
Normal file
25
test_regress/t/t_package_struct.v
Normal file
@ -0,0 +1,25 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2022 by Antmicro Ltd.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
package pkg;
|
||||
typedef struct {
|
||||
string a, b;
|
||||
struct {
|
||||
bit a, b;
|
||||
} has;
|
||||
} strings;
|
||||
endpackage
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
initial begin
|
||||
pkg::strings stct;
|
||||
stct.a = "hello";
|
||||
stct.b = "world";
|
||||
$display("%s, %s (%1b, %1b)", stct.a, stct.b, stct.has.a, stct.has.b);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
@ -25,10 +25,16 @@ module secret_sub
|
||||
|
||||
// verilator no_inline_module
|
||||
|
||||
integer secret_cyc;
|
||||
real secret_cyc_r;
|
||||
integer secret_o;
|
||||
real secret_r;
|
||||
typedef struct {
|
||||
integer secret_field;
|
||||
integer secret_field_r;
|
||||
} secret_st;
|
||||
|
||||
integer secret_cyc;
|
||||
real secret_cyc_r;
|
||||
integer secret_o;
|
||||
real secret_r;
|
||||
secret_st secret_pair;
|
||||
|
||||
export "DPI-C" task dpix_a_task;
|
||||
task dpix_a_task(input int i, output int o); o = i + 1; endtask
|
||||
@ -40,6 +46,8 @@ module secret_sub
|
||||
|
||||
// Test loop
|
||||
always @ (posedge clk) begin
|
||||
secret_pair.secret_field += 1;
|
||||
secret_pair.secret_field_r += 2;
|
||||
secret_cyc_r = $itor(secret_cyc)/10.0 - 5.0;
|
||||
secret_cyc <= dpii_a_func(secret_cyc);
|
||||
secret_r += 1.0 + $cos(secret_cyc_r);
|
||||
|
3
test_regress/t/t_struct_assign.out
Normal file
3
test_regress/t/t_struct_assign.out
Normal file
@ -0,0 +1,3 @@
|
||||
( 3, 4) ( 3, 4)
|
||||
%p='{fst:'h3, snd:'h4}
|
||||
*-* All Finished *-*
|
22
test_regress/t/t_struct_assign.pl
Executable file
22
test_regress/t/t_struct_assign.pl
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2022 by Antmicro Ltd. 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(simulator => 1);
|
||||
|
||||
compile(
|
||||
);
|
||||
|
||||
execute(
|
||||
expect_filename => $Self->{golden_filename},
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
27
test_regress/t/t_struct_assign.v
Normal file
27
test_regress/t/t_struct_assign.v
Normal file
@ -0,0 +1,27 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2022 by Antmicro Ltd.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
typedef struct {
|
||||
int fst, snd;
|
||||
} pair_t;
|
||||
|
||||
pair_t a, b;
|
||||
|
||||
initial begin
|
||||
a.fst = 1;
|
||||
a.snd = 2;
|
||||
b.fst = 3;
|
||||
b.snd = 4;
|
||||
|
||||
a = b;
|
||||
|
||||
$display("(%d, %d) (%d, %d)", a.fst, a.snd, b.fst, b.snd);
|
||||
$display("%%p=%p", a);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
@ -5,22 +5,24 @@
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module x;
|
||||
|
||||
// verilator lint_off UNPACKED
|
||||
typedef struct {
|
||||
int a;
|
||||
} notpacked_t;
|
||||
// verilator lint_on UNPACKED
|
||||
int a, b;
|
||||
logic [3:0] c;
|
||||
} embedded_t;
|
||||
|
||||
typedef struct packed {
|
||||
notpacked_t b;
|
||||
} ispacked_t;
|
||||
typedef struct {
|
||||
embedded_t b;
|
||||
embedded_t tab [3:0];
|
||||
} notembedded_t;
|
||||
|
||||
ispacked_t p;
|
||||
notembedded_t p;
|
||||
embedded_t t [1:0];
|
||||
|
||||
initial begin
|
||||
p.b = 1;
|
||||
if (p.b != 1) $stop;
|
||||
t[1].a = 2;
|
||||
p.b.a = 1;
|
||||
if (t[1].a != 2) $stop;
|
||||
if (p.b.a != 1) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
@ -1,10 +0,0 @@
|
||||
%Warning-UNPACKED: t/t_struct_unpacked2.v:10:13: Unsupported: Unpacked array in packed struct/union (struct/union converted to unpacked)
|
||||
10 | int b [2];
|
||||
| ^
|
||||
... For warning description see https://verilator.org/warn/UNPACKED?v=latest
|
||||
... Use "/* verilator lint_off UNPACKED */" and lint_on around source to disable this message.
|
||||
%Warning-UNPACKED: t/t_struct_unpacked2.v:9:12: Unsupported: Unpacked struct/union
|
||||
: ... In instance x
|
||||
9 | typedef struct {
|
||||
| ^~~~~~
|
||||
%Error: Exiting due to
|
33
test_regress/t/t_struct_unused.pl
Executable file
33
test_regress/t/t_struct_unused.pl
Executable file
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 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_all => 1);
|
||||
|
||||
# Use --debug-protect to assist debug
|
||||
|
||||
compile(
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
if ($Self->{vlt_all}) {
|
||||
# Check for unused structs in any outputs
|
||||
my $any;
|
||||
foreach my $filename (glob $Self->{obj_dir} . "/*.[ch]*") {
|
||||
file_grep_not($filename, qr/useless/i);
|
||||
$any = 1;
|
||||
}
|
||||
$any or $Self->error("No outputs found");
|
||||
}
|
||||
|
||||
ok(1);
|
||||
1;
|
23
test_regress/t/t_struct_unused.v
Normal file
23
test_regress/t/t_struct_unused.v
Normal file
@ -0,0 +1,23 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2022 by Antmicro Ltd.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module x;
|
||||
typedef struct {
|
||||
int fst, snd;
|
||||
} uselessA_t;
|
||||
|
||||
typedef struct {
|
||||
bit [3:0] n;
|
||||
uselessA_t b;
|
||||
} uselessB_t;
|
||||
|
||||
uselessA_t useless;
|
||||
|
||||
initial begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
@ -11,6 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
||||
scenarios(simulator => 1);
|
||||
|
||||
compile(
|
||||
verilator_flags2 => ['--structs-packed'],
|
||||
);
|
||||
|
||||
execute(
|
||||
|
@ -38,9 +38,9 @@ module top();
|
||||
int A;
|
||||
struct {
|
||||
int B, C;
|
||||
struct{
|
||||
struct {
|
||||
int D, E;
|
||||
struct{
|
||||
struct {
|
||||
int F;
|
||||
shortint G;
|
||||
} FG1;
|
||||
|
@ -12,6 +12,7 @@ scenarios(simulator => 1);
|
||||
|
||||
|
||||
compile(
|
||||
verilator_flags2 => ['--structs-packed'],
|
||||
fails => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
%Warning-UNPACKED: t/t_struct_unpacked_bad.v:9:12: Unsupported: Unpacked struct/union
|
||||
: ... In instance x
|
||||
9 | typedef struct {
|
||||
| ^~~~~~
|
||||
%Warning-UNPACKED: t/t_union_unpacked_bad.v:9:12: Unsupported: Unpacked union
|
||||
: ... In instance x
|
||||
9 | typedef union {
|
||||
| ^~~~~
|
||||
... For warning description see https://verilator.org/warn/UNPACKED?v=latest
|
||||
... Use "/* verilator lint_off UNPACKED */" and lint_on around source to disable this message.
|
||||
%Error: Exiting due to
|
@ -6,15 +6,15 @@
|
||||
|
||||
module x;
|
||||
|
||||
typedef struct {
|
||||
int b [2];
|
||||
} notpacked_t;
|
||||
typedef union {
|
||||
int a;
|
||||
} union_t;
|
||||
|
||||
notpacked_t n;
|
||||
union_t b;
|
||||
|
||||
initial begin
|
||||
n.b[0] = 1;
|
||||
if (n.b[0] != 1) $stop;
|
||||
b = 1;
|
||||
if (b != 1) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
Loading…
Reference in New Issue
Block a user