Support unpacked structs (#3802)

This commit is contained in:
Aleksander Kiryk 2022-12-21 01:22:42 +01:00 committed by GitHub
parent c3b4bc85fa
commit c2b09e35f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 541 additions and 95 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View 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

View File

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

View File

@ -0,0 +1,2 @@
hello, world (0, 0)
*-* All Finished *-*

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

View 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

View File

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

View File

@ -0,0 +1,3 @@
( 3, 4) ( 3, 4)
%p='{fst:'h3, snd:'h4}
*-* All Finished *-*

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

View 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

View File

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

View File

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

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

View 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

View File

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

View File

@ -38,9 +38,9 @@ module top();
int A;
struct {
int B, C;
struct{
struct {
int D, E;
struct{
struct {
int F;
shortint G;
} FG1;

View File

@ -12,6 +12,7 @@ scenarios(simulator => 1);
compile(
verilator_flags2 => ['--structs-packed'],
fails => 1,
expect_filename => $Self->{golden_filename},
);

View File

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

View File

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