diff --git a/Changes b/Changes index c58cbfca9..b4d770e45 100644 --- a/Changes +++ b/Changes @@ -9,7 +9,8 @@ indicates the contributor was also the author of the fix; Thanks! concatenates and pullup/pulldowns, bug395, bug56, bug54, bug51. [Alex Solomatnikov, Lane Brooks, et al] -** Major internal changes to support future complex data types. +** Support packed structures and unions, bug181. + Note this was a major internal change that may lead to some instability. *** Support tri0 and tri1, bug462. [Alex Solomatnikov] diff --git a/bin/verilator b/bin/verilator index 43a733437..91ef54718 100755 --- a/bin/verilator +++ b/bin/verilator @@ -1747,8 +1747,8 @@ Verilator supports ==? and !=? operators, ++ and -- in some contexts, $bits, $countones, $error, $fatal, $info, $isunknown, $onehot, $onehot0, $unit, $warning, always_comb, always_ff, always_latch, bit, byte, chandle, const, do-while, enum, export, final, import, int, logic, longint, package, -program, shortint, time, typedef, var, void, priority case/if, and unique -case/if. +program, shortint, struct, time, typedef, union, var, void, priority +case/if, and unique case/if. It also supports .name and .* interconnection. @@ -2218,6 +2218,14 @@ Verilator is optimized for edge sensitive (flop based) designs. It will attempt to do the correct thing for latches, but most performance optimizations will be disabled around the latch. +=head2 Structures and Unions + +Verilator only presently supports packed structs and packed unions. Rand +and randc tags on members are simply ignored. All structures and unions +are represented as a single vector, which means that generating one member +of a structure from blocking, and another from non-blocking assignments is +unsupported. + =head2 Time All delays (#) are ignored, as they are in synthesis. diff --git a/src/V3Ast.h b/src/V3Ast.h index 2d6252bda..1fba0213e 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -221,6 +221,8 @@ public: ILLEGAL, EXPR_BITS, // V3Const converts to constant // + MEMBER_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes + // VAR_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes VAR_CLOCK, // V3LinkParse moves to AstVar::attrScClocked VAR_CLOCK_ENABLE, // V3LinkParse moves to AstVar::attrClockEn @@ -235,8 +237,8 @@ public: enum en m_e; const char* ascii() const { static const char* names[] = { - "%E-AT", "EXPR_BITS", "VAR_BASE", - "VAR_CLOCK", "VAR_CLOCK_ENABLE", "VAR_PUBLIC", + "%E-AT", "EXPR_BITS", "MEMBER_BASE", + "VAR_BASE", "VAR_CLOCK", "VAR_CLOCK_ENABLE", "VAR_PUBLIC", "VAR_PUBLIC_FLAT", "VAR_PUBLIC_FLAT_RD","VAR_PUBLIC_FLAT_RW", "VAR_ISOLATE_ASSIGNMENTS", "VAR_SC_BV", "VAR_SFORMAT" }; @@ -1518,6 +1520,40 @@ public: static int uniqueNumInc() { return ++s_uniqueNum; } }; +struct AstNodeClassDType : public AstNodeDType { +private: + // TYPES + typedef map MemberNameMap; + // MEMBERS + bool m_packed; + MemberNameMap m_members; +public: + AstNodeClassDType(FileLine* fl, AstNumeric numericUnpack) + : AstNodeDType(fl) { + // AstNumeric::NOSIGN overloaded to indicate not packed + m_packed = (numericUnpack != AstNumeric::NOSIGN); + numeric(numericUnpack.isSigned() ? AstNumeric::SIGNED : AstNumeric::UNSIGNED); + } + ASTNODE_BASE_FUNCS(NodeClassDType) + virtual bool broken() const; + virtual void dump(ostream& str); + // For basicp() we reuse the size to indicate a "fake" basic type of same size + virtual AstBasicDType* basicp() const { return findLogicDType(width(),width(),numeric())->castBasicDType(); } + virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } + virtual int widthAlignBytes() const; // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) + virtual int widthTotalBytes() const; // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,... + // op1 = members + AstMemberDType* membersp() const { return op1p()->castMemberDType(); } // op1 = AstMember list + void addMembersp(AstNode* nodep) { addNOp1p(nodep); } + bool packed() const { return m_packed; } + void clearCache() { m_members.clear(); } + void repairMemberCache(); + AstMemberDType* findMember(const string& name) const { + MemberNameMap::const_iterator it = m_members.find(name); + return (it==m_members.end()) ? NULL : it->second; + } +}; + struct AstNodeSel : public AstNodeBiop { // Single bit range extraction, perhaps with non-constant selection or array selection AstNodeSel(FileLine* fl, AstNode* fromp, AstNode* bitp) diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 88379135b..05876bab1 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -46,6 +46,28 @@ int AstNodeSel::bitConst() const { AstConst* constp=bitp()->castConst(); return (constp?constp->toSInt():0); } +void AstNodeClassDType::repairMemberCache() { + clearCache(); + for (AstMemberDType* itemp = membersp(); itemp; itemp=itemp->nextp()->castMemberDType()) { + if (m_members.find(itemp->name())!=m_members.end()) { itemp->v3error("Duplicate declaration of member name: "<prettyName()); } + else m_members.insert(make_pair(itemp->name(), itemp)); + } +} + +bool AstNodeClassDType::broken() const { + set exists; + for (AstMemberDType* itemp = membersp(); itemp; itemp=itemp->nextp()->castMemberDType()) { + exists.insert(itemp); + } + for (MemberNameMap::const_iterator it=m_members.begin(); it!=m_members.end(); ++it) { + if (exists.find(it->second) == exists.end()) { + this->v3error("Internal: Structure member broken: "<first); + return true; + } + } + return false; +} + int AstBasicDType::widthAlignBytes() const { if (width()<=8) return 1; else if (width()<=16) return 2; @@ -60,6 +82,22 @@ int AstBasicDType::widthTotalBytes() const { else return widthWords()*(VL_WORDSIZE/8); } +int AstNodeClassDType::widthTotalBytes() const { + if (width()<=8) return 1; + else if (width()<=16) return 2; + else if (isQuad()) return 8; + else return widthWords()*(VL_WORDSIZE/8); +} + +int AstNodeClassDType::widthAlignBytes() const { + // Could do max across members but that would be slow, + // instead intuit based on total structure size + if (width()<=8) return 1; + else if (width()<=16) return 2; + else if (width()<=32) return 4; + else return 8; +} + bool AstVar::isSigPublic() const { return (m_sigPublic || (v3Global.opt.allPublic() && !isTemp() && !isGenVar())); } @@ -285,6 +323,14 @@ AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) { } return NULL; } + else if (AstNodeClassDType* adtypep = dtypep->castNodeClassDType()) { + if (adtypep->packed()) { + if ((dim++) == dimension) { + return adtypep; + } + } + return NULL; + } // Node no ->next in loop; use continue where necessary break; } @@ -679,6 +725,10 @@ void AstRefDType::dump(ostream& str) { if (defp()) { str<<" -> "; defp()->dump(str); } else { str<<" -> UNLINKED"; } } +void AstNodeClassDType::dump(ostream& str) { + this->AstNode::dump(str); + if (packed()) str<<" [PACKED]"; +} void AstNodeDType::dump(ostream& str) { this->AstNode::dump(str); if (generic()) str<<" [GENERIC]"; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 2061b3621..d69d6ccee 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -471,6 +471,68 @@ public: void packagep(AstPackage* nodep) { m_packagep=nodep; } }; +struct AstStructDType : public AstNodeClassDType { + AstStructDType(FileLine* fl, AstNumeric numericUnpack) + : AstNodeClassDType(fl,numericUnpack) {} + ASTNODE_NODE_FUNCS(StructDType, STRUCTDTYPE) + virtual string verilogKwd() const { return "struct"; }; +}; + +struct AstUnionDType : public AstNodeClassDType { + //UNSUP: bool isTagged; + AstUnionDType(FileLine* fl, AstNumeric numericUnpack) + : AstNodeClassDType(fl,numericUnpack) {} + ASTNODE_NODE_FUNCS(UnionDType, UNIONDTYPE) + virtual string verilogKwd() const { return "union"; }; +}; + +struct AstMemberDType : public AstNodeDType { + // A member of a struct/union + // PARENT: AstClassDType +private: + AstNodeDType* m_refDTypep; // Elements of this type (after widthing) + string m_name; // Name of variable + int m_lsb; // Within this level's packed struct, the LSB of the first bit of the member + //UNSUP: int m_randType; // Randomization type (IEEE) +public: + AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp) + : AstNodeDType(fl) + , m_name(name), m_lsb(-1) { + childDTypep(dtp); // Only for parser + dtypep(NULL); // V3Width will resolve + refDTypep(NULL); + } + AstMemberDType(FileLine* fl, const string& name, AstNodeDType* dtp) + : AstNodeDType(fl) + , m_name(name), m_lsb(-1) { + UASSERT(dtp,"AstMember created with no dtype"); + refDTypep(dtp); + dtypep(this); + widthFromSub(subDTypep()); + } + ASTNODE_NODE_FUNCS(MemberDType, MEMBERDTYPE) + virtual string name() const { return m_name; } // * = Var name + virtual bool hasDType() const { return true; } + virtual bool maybePointedTo() const { return true; } + AstNodeDType* getChildDTypep() const { return childDTypep(); } + AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable + void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } + AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } + void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } + virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; } + virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); } + // + virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type (Note don't need virtual - AstVar isn't a NodeDType) + AstNodeDType* dtypeSkipRefp() const { return subDTypep()->skipRefp(); } // op1 = Range of variable (Note don't need virtual - AstVar isn't a NodeDType) + virtual AstNodeDType* skipRefp() const { return dtypeSkipRefp(); } + virtual int widthAlignBytes() const { return subDTypep()->widthAlignBytes(); } // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) + virtual int widthTotalBytes() const { return subDTypep()->widthTotalBytes(); } // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,... + // METHODS + virtual void name(const string& name) { m_name = name; } + int lsb() const { return m_lsb; } + void lsb(int lsb) { m_lsb=lsb; } +}; + struct AstEnumItem : public AstNode { private: string m_name; @@ -699,6 +761,37 @@ struct AstSel : public AstNodeTriop { int msbConst() const { return lsbConst()+widthConst()-1; } }; +struct AstMemberSel : public AstNodeMath { + // Parents: math|stmt + // Children: varref|arraysel, math +private: + // Don't need the class we are extracting from, as the "fromp()"'s datatype can get us to it + string m_name; +public: + AstMemberSel(FileLine* fl, AstNode* fromp, VFlagChildDType, const string& name) + : AstNodeMath(fl), m_name(name) { + setOp1p(fromp); + dtypep(NULL); // V3Width will resolve + } + AstMemberSel(FileLine* fl, AstNode* fromp, AstMemberDType* dtp) + : AstNodeMath(fl) { + setOp1p(fromp); + dtypep(dtp); + m_name = dtp->name(); + } + ASTNODE_NODE_FUNCS(MemberSel, MEMBERSEL) + virtual string name() const { return m_name; } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { + V3ERROR_NA; /* How can from be a const? */ } + virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially + virtual string emitC() { V3ERROR_NA; return ""; } + virtual bool cleanOut() { return false; } + virtual bool same(AstNode* samep) const { return true; } // dtype comparison does it all for us + virtual int instrCount() const { return widthInstrs(); } + AstNode* fromp() const { return op1p()->castNode(); } // op1 = Extracting what (NULL=TBD during parsing) + void fromp(AstNode* nodep) { setOp1p(nodep); } +}; + struct AstVar : public AstNode { // A variable (in/out/wire/reg/param) inside a module private: diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index b5797e24b..2a158e2e2 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -622,6 +622,7 @@ public: virtual void visit(AstTraceDecl*, AstNUser*) {} // Handled outside the Visit class virtual void visit(AstTraceInc*, AstNUser*) {} // Handled outside the Visit class virtual void visit(AstCFile*, AstNUser*) {} // Handled outside the Visit class + virtual void visit(AstTypedef*, AstNUser*) {} // Nothing needed presently // Default virtual void visit(AstNode* nodep, AstNUser*) { puts((string)"\n???? // "+nodep->prettyTypeName()+"\n"); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index fa3111cf7..be4500e73 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -501,6 +501,19 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { nodep->subDTypep()->iterateAndNext(*this); nodep->rangep()->iterateAndNext(*this); } + virtual void visit(AstNodeClassDType* nodep, AstNUser*) { + puts(nodep->verilogKwd()+" "); + if (nodep->packed()) puts("packed "); + puts("\n"); + nodep->membersp()->iterateAndNext(*this); + puts("}"); + } + virtual void visit(AstMemberDType* nodep, AstNUser*) { + nodep->subDTypep()->iterateAndNext(*this); + puts(" "); + puts(nodep->name()); + puts("}"); + } virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { if (nodep->dotted()!="") { putfs(nodep,nodep->dotted()); puts("."); puts(nodep->prettyName()); } else { putfs(nodep,nodep->prettyName()); } diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 1ee33ed15..1f6478c38 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1192,7 +1192,11 @@ private: m_dotSymp = m_curSymp; } if (m_dotPos == DP_MEMBER) { - nodep->v3error("Unsupported: Structs and dotted reference into variable"); + // Found a Var, everything following is membership. {scope}.{var}.HERE {member} + AstNode* varEtcp = m_dotp->lhsp()->unlinkFrBack(); + AstNode* newp = new AstMemberSel(nodep->fileline(), varEtcp, VFlagChildDType(), nodep->name()); + nodep->replaceWith(newp); + pushDeletep(nodep); nodep=NULL; } else { // diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index cab43bd23..cceccd02a 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -200,10 +200,15 @@ private: // So we replicate it in another node // Note that V3Param knows not to replace AstVarRef's under AstAttrOf's AstNode* basefromp = AstArraySel::baseFromp(nodep); - AstNodeVarRef* varrefp = basefromp->castNodeVarRef(); // Maybe varxref - so need to clone - if (!varrefp) nodep->v3fatalSrc("Illegal bit select; no signal being extracted from"); - nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE, - varrefp->cloneTree(false))); + if (AstNodeVarRef* varrefp = basefromp->castNodeVarRef()) { // Maybe varxref - so need to clone + nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE, + varrefp->cloneTree(false))); + } else if (AstMemberSel* fromp = nodep->fromp()->castMemberSel()) { + nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::MEMBER_BASE, + fromp->cloneTree(false))); + } else { + nodep->v3fatalSrc("Illegal bit select; no signal/member being extracted from"); + } } } diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index 728a8aa8e..8e3cfe8b9 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -65,7 +65,9 @@ struct V3ParseBisonYYSType { AstCaseItem* caseitemp; AstCell* cellp; AstConst* constp; + AstMemberDType* memberp; AstNodeModule* modulep; + AstNodeClassDType* classp; AstNodeDType* dtypep; AstNodeFTask* ftaskp; AstNodeFTaskRef* ftaskrefp; diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 224bf2e5b..09283a477 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -118,6 +118,7 @@ private: AstNodeCase* m_casep; // Current case statement CaseItem is under AstFunc* m_funcp; // Current function AstInitial* m_initialp; // Current initial block + AstAttrOf* m_attrp; // Current attribute bool m_doGenerate; // Do errors later inside generate statement // CLASSES @@ -377,6 +378,7 @@ private: virtual void visit(AstSel* nodep, AstNUser* vup) { // Signed: always unsigned; Real: Not allowed + if (nodep->didWidth()) return; if (vup->c()->prelim()) { if (debug()>=9) nodep->dumpTree(cout,"-selWidth: "); nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); @@ -531,7 +533,6 @@ private: AstNode* selp = V3Width::widthSelNoIterEdit(nodep); if (selp!=nodep) { nodep=NULL; selp->iterate(*this,vup); return; } nodep->v3fatalSrc("AstSelExtract should disappear after widthSel"); } - virtual void visit(AstSelPlus* nodep, AstNUser* vup) { nodep->attrp()->iterateAndNext(*this,WidthVP(0,0,FINAL).p()); AstNode* selp = V3Width::widthSelNoIterEdit(nodep); if (selp!=nodep) { nodep=NULL; selp->iterate(*this,vup); return; } @@ -629,6 +630,8 @@ private: nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); } virtual void visit(AstAttrOf* nodep, AstNUser*) { + AstAttrOf* oldAttr = m_attrp; + m_attrp = nodep; nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); // Don't iterate children, don't want to lose VarRef. if (nodep->attrType()==AstAttrType::EXPR_BITS) { @@ -636,12 +639,15 @@ private: V3Number num (nodep->fileline(), 32, nodep->fromp()->width()); nodep->replaceWith(new AstConst(nodep->fileline(), num)); nodep->deleteTree(); nodep=NULL; } else if (nodep->attrType()==AstAttrType::VAR_BASE) { - // Soon to be handled in V3LinkWidth SEL generation + // Soon to be handled in V3LinkWidth SEL generation, under attrp() and newSubLsbOf + } else if (nodep->attrType()==AstAttrType::MEMBER_BASE) { + // Soon to be handled in V3LinkWidth SEL generation, under attrp() and newSubLsbOf } else { // Everything else resolved earlier nodep->dtypeSetLogicSized(32,1,AstNumeric::UNSIGNED); // Approximation, unsized 32 UINFO(1,"Missing ATTR type case node: "<v3fatalSrc("Missing ATTR type case"); } + m_attrp = oldAttr; } virtual void visit(AstText* nodep, AstNUser* vup) { // Only used in CStmts which don't care.... @@ -760,8 +766,9 @@ private: if (nodep->childDTypep()) nodep->dtypep(moveChildDTypeEdit(nodep)); nodep->dtypep(iterateEditDTypep(nodep, nodep->dtypep())); if (!nodep->dtypep()) nodep->v3fatalSrc("No dtype determined for var"); - if (nodep->isIO() && !(nodep->dtypeSkipRefp()->castBasicDType() || - nodep->dtypeSkipRefp()->castArrayDType())) { + if (nodep->isIO() && !(nodep->dtypeSkipRefp()->castBasicDType() + || nodep->dtypeSkipRefp()->castArrayDType() + || nodep->dtypeSkipRefp()->castNodeClassDType())) { nodep->v3error("Unsupported: Inputs and outputs must be simple data types"); } if (nodep->dtypeSkipRefp()->castConstDType()) { @@ -913,6 +920,81 @@ private: } nodep->dtypeFrom(nodep->itemp()); } + virtual void visit(AstNodeClassDType* nodep, AstNUser* vup) { + if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + UINFO(5," NODECLASS "<=9) nodep->dumpTree("-class-in--"); + if (!nodep->packed()) nodep->v3error("Unsupported: Unpacked struct/union"); + nodep->iterateChildren(*this); // First size all members + nodep->repairMemberCache(); + // Determine bit assignments and width + nodep->dtypep(nodep); + int lsb = 0; + int width = 0; + // MSB is first, so go backwards + AstMemberDType* itemp; + for (itemp = nodep->membersp(); itemp && itemp->nextp(); itemp=itemp->nextp()->castMemberDType()) ; + for (AstMemberDType* backip; itemp; itemp=backip) { + backip = itemp->backp()->castMemberDType(); + itemp->lsb(lsb); + if (nodep->castUnionDType()) { + width = max(width, itemp->width()); + } else { + lsb += itemp->width(); + width += itemp->width(); + } + } + nodep->widthForce(width,width); // Signing stays as-is, as parsed from declaration + //if (debug()>=9) nodep->dumpTree("-class-out-"); + } + virtual void visit(AstMemberDType* nodep, AstNUser* vup) { + if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep)); + // Iterate into subDTypep() to resolve that type and update pointer. + nodep->refDTypep(iterateEditDTypep(nodep, nodep->subDTypep())); + nodep->dtypep(nodep); // The member itself, not subDtype + nodep->widthFromSub(nodep->subDTypep()); + } + virtual void visit(AstMemberSel* nodep, AstNUser* vup) { + UINFO(5," MEMBERSEL "<=9) nodep->dumpTree("-ms-in-"); + nodep->iterateChildren(*this,WidthVP(ANYSIZE,0,BOTH).p()); + // Find the fromp dtype - should be a class + AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefp(); + UINFO(9," from dt "<castNodeClassDType(); + AstMemberDType* memberp = NULL; // NULL=error below + if (!fromClassp) { + nodep->v3error("Member selection of non-struct/union object '" + <fromp()->prettyTypeName()<<"' which is a '"<fromp()->dtypep()->prettyTypeName()<<"'"); + } + else { + // No need to width-resolve the fromClassp, as it was done when we did the child + memberp = fromClassp->findMember(nodep->name()); + if (!memberp) { + nodep->v3error("Member '"<prettyName()<<"' not found in structure"); + } + } + if (memberp) { + if (m_attrp) { // Looking for the base of the attribute + nodep->dtypep(memberp); + UINFO(9," MEMBERSEL(attr) -> "<fileline(), nodep->fromp()->unlinkFrBack(), + memberp->lsb(), memberp->width()); + newp->dtypep(memberp); + newp->didWidth(true); // Don't replace dtype with basic type + UINFO(9," MEMBERSEL -> "<replaceWith(newp); + pushDeletep(nodep); nodep=NULL; + // Should be able to treat it as a normal-ish nodesel - maybe. The lhsp() will be strange until this stage; create the number here? + } + } + if (!memberp) { // Very bogus, but avoids core dump + nodep->replaceWith(new AstConst(nodep->fileline(), AstConst::LogicFalse())); + pushDeletep(nodep); nodep=NULL; + } + } virtual void visit(AstPslClocked* nodep, AstNUser*) { nodep->propp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p()); nodep->sensesp()->iterateAndNext(*this); @@ -2117,6 +2199,7 @@ public: m_casep = NULL; m_funcp = NULL; m_initialp = NULL; + m_attrp = NULL; m_doGenerate = doGenerate; } AstNode* mainAcceptEdit(AstNode* nodep) { diff --git a/src/V3WidthCommit.h b/src/V3WidthCommit.h index 76243d8b6..7d975ad11 100644 --- a/src/V3WidthCommit.h +++ b/src/V3WidthCommit.h @@ -113,6 +113,13 @@ private: editDType(nodep); } virtual void visit(AstNodeDType* nodep, AstNUser*) { + visitIterateNodeDType(nodep); + } + virtual void visit(AstNodeClassDType* nodep, AstNUser*) { + visitIterateNodeDType(nodep); + nodep->clearCache(); + } + void visitIterateNodeDType(AstNodeDType* nodep) { // Rather than use dtypeChg which may make new nodes, we simply edit in place, // as we don't need to preserve any widthMin's, and every dtype with the same width // gets an identical edit. diff --git a/src/V3WidthSel.cpp b/src/V3WidthSel.cpp index cd2aa4d93..704c3a54c 100644 --- a/src/V3WidthSel.cpp +++ b/src/V3WidthSel.cpp @@ -85,6 +85,9 @@ private: if (AstArrayDType* adtypep = ddtypep->castArrayDType()) { return adtypep; } + else if (AstNodeClassDType* adtypep = ddtypep->castNodeClassDType()) { + return adtypep; + } else if (AstBasicDType* adtypep = ddtypep->castBasicDType()) { if (!adtypep->isRanged()) { nodep->v3error("Illegal bit select; variable does not have a bit range, or bad dimension: "<prettyName()); @@ -136,13 +139,21 @@ private: AstNodeDType* baseDTypeFrom(AstNode* basefromp, AstNode*& errpr) { errpr = basefromp; - AstNodeVarRef* varrefp = basefromp->castNodeVarRef(); - if (!varrefp) basefromp->v3fatalSrc("Bit/array selection of non-variable"); - AstVar* varp = varrefp->varp(); if (!varp) { varrefp->v3fatalSrc("Signal not linked"); return NULL; } - errpr = varp; - AstNodeDType* bfdtypep = varp->subDTypep(); - if (!bfdtypep) basefromp->v3fatalSrc("No datatype found for variable in select"); - return bfdtypep; + if (AstNodeVarRef* varrefp = basefromp->castNodeVarRef()) { + AstVar* varp = varrefp->varp(); if (!varp) { varrefp->v3fatalSrc("Signal not linked"); return NULL; } + errpr = varp; + AstNodeDType* bfdtypep = varp->subDTypep(); + if (!bfdtypep) basefromp->v3fatalSrc("No datatype found for variable in select"); + return bfdtypep; + } else if (AstMemberSel* selp = basefromp->castMemberSel()) { + errpr = selp; + AstNodeDType* bfdtypep = selp->dtypep(); + if (!bfdtypep) basefromp->v3fatalSrc("No datatype found for variable in select"); + return bfdtypep; + } else { + basefromp->v3fatalSrc("Bit/array selection of non-variable"); + } + return NULL; } AstNode* newSubLsbOf(AstNode* underp, AstNode* basefromp) { @@ -235,6 +246,17 @@ private: UINFO(6," new "<=9) newp->dumpTree(cout,"-vsbnw: "); nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL; } + else if (AstNodeClassDType* adtypep = ddtypep->castNodeClassDType()) { + if (adtypep) {} // unused + // SELBIT(range, index) -> SEL(array, index, 1) + AstSel* newp = new AstSel (nodep->fileline(), + fromp, + newSubLsbOf(rhsp, basefromp), + // Unsized so width from user + new AstConst (nodep->fileline(),AstConst::Unsized32(),1)); + UINFO(6," new "<=9) newp->dumpTree(cout,"-vsbnw: "); + nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL; + } else { // NULL=bad extract, or unknown node type nodep->v3error("Illegal bit or array select; variable already selected, or bad dimension"); // How to recover? We'll strip a dimension. @@ -292,6 +314,22 @@ private: UINFO(6," new "<=9) newp->dumpTree(cout,"--SLEXnew: "); nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL; + } else if (AstNodeClassDType* adtypep = ddtypep->castNodeClassDType()) { + if (adtypep) {} // Unused + // Classes aren't little endian + if (lsb > msb) { + nodep->v3error("["<fileline(), AstConst::Unsized32(), // Unsized so width from user + msb +1-lsb); + AstSel* newp = new AstSel (nodep->fileline(), + fromp, + newSubLsbOf(lsbp, basefromp), + widthp); + UINFO(6," new "<=9) newp->dumpTree(cout,"--SLEXnew: "); + nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL; } else { // NULL=bad extract, or unknown node type nodep->v3error("Illegal range select; variable already selected, or bad dimension"); diff --git a/src/verilog.l b/src/verilog.l index 8b1513da6..172ad167a 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -419,13 +419,17 @@ word [a-zA-Z0-9_]+ "logic" { FL; return yLOGIC; } "longint" { FL; return yLONGINT; } "package" { FL; return yPACKAGE; } + "packed" { FL; return yPACKED; } "priority" { FL; return yPRIORITY; } "program" { FL; return yPROGRAM; } "pure" { FL; return yPURE; } + "rand" { FL; return yRAND; } + "randc" { FL; return yRANDC; } "return" { FL; return yRETURN; } "shortint" { FL; return ySHORTINT; } "static" { FL; return ySTATIC; } "string" { FL; return ySTRING; } + "struct" { FL; return ySTRUCT; } "timeprecision" { FL; return yTIMEPRECISION; } "timeunit" { FL; return yTIMEUNIT; } "typedef" { FL; return yTYPEDEF; } @@ -467,17 +471,13 @@ word [a-zA-Z0-9_]+ "modport" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "new" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "null" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } - "packed" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "protected" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } - "rand" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } - "randc" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "randcase" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "randomize" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "randsequence" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "ref" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "shortreal" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "solve" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } - "struct" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "super" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "tagged" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "this" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } @@ -496,11 +496,11 @@ word [a-zA-Z0-9_]+ "const" { FL; return yCONST__LEX; } "cover" { FL; return yCOVER; } "property" { FL; return yPROPERTY; } + "union" { FL; return yUNION; } /* Generic unsupported warnings */ "assume" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented in non-PSL context: %s",yytext); } "before" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented in non-PSL context: %s",yytext); } "sequence" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented in non-PSL context: %s",yytext); } - "union" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented in non-PSL context: %s",yytext); } "within" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented in non-PSL context: %s",yytext); } } diff --git a/src/verilog.y b/src/verilog.y index 1450fe0ca..22cd4e618 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -57,6 +57,7 @@ public: AstVar* m_varAttrp; // Current variable for attribute adding AstCase* m_caseAttrp; // Current case statement for attribute adding AstNodeDType* m_varDTypep; // Pointer to data type for next signal declaration + AstNodeDType* m_memDTypep; // Pointer to data type for next member declaration int m_pinNum; // Pin number currently parsing string m_instModule; // Name of module referenced for instantiations AstPin* m_instParamp; // Parameters for instantiations @@ -70,6 +71,7 @@ public: m_varDecl = AstVarType::UNKNOWN; m_varIO = AstVarType::UNKNOWN; m_varDTypep = NULL; + m_memDTypep = NULL; m_pinNum = -1; m_instModule = ""; m_instParamp = NULL; @@ -345,6 +347,7 @@ class AstSenTree; %token yOR "or" %token yOUTPUT "output" %token yPACKAGE "package" +%token yPACKED "packed" %token yPARAMETER "parameter" %token yPMOS "pmos" %token yPOSEDGE "posedge" @@ -355,6 +358,8 @@ class AstSenTree; %token yPULLDOWN "pulldown" %token yPULLUP "pullup" %token yPURE "pure" +%token yRAND "rand" +%token yRANDC "randc" %token yRCMOS "rcmos" %token yREAL "real" %token yREALTIME "realtime" @@ -373,6 +378,7 @@ class AstSenTree; %token ySPECPARAM "specparam" %token ySTATIC "static" %token ySTRING "string" +%token ySTRUCT "struct" %token ySUPPLY0 "supply0" %token ySUPPLY1 "supply1" %token yTABLE "table" @@ -388,6 +394,7 @@ class AstSenTree; %token yTRI1 "tri1" %token yTRUE "true" %token yTYPEDEF "typedef" +%token yUNION "union" %token yUNIQUE "unique" %token yUNIQUE0 "unique0" %token yUNSIGNED "unsigned" @@ -1170,10 +1177,8 @@ data_typeBasic: // IEEE: part of data_type data_typeNoRef: // ==IEEE: data_type, excluding class_type etc references data_typeBasic { $$ = $1; } - //UNSUP ySTRUCT packedSigningE '{' struct_union_memberList '}' packed_dimensionListE - //UNSUP { UNSUP } - //UNSUP yUNION taggedE packedSigningE '{' struct_union_memberList '}' packed_dimensionListE - //UNSUP { UNSUP } + | struct_unionDecl packed_dimensionListE { $$ = GRAMMARP->createArray(new AstDefImplicitDType($1->fileline(),"__typeimpsu"+cvtToStr(GRAMMARP->m_modTypeImpNum++), + GRAMMARP->m_modp,VFlagChildDType(),$1),$2,false); } | enumDecl { $$ = new AstDefImplicitDType($1->fileline(),"__typeimpenum"+cvtToStr(GRAMMARP->m_modTypeImpNum++), GRAMMARP->m_modp,VFlagChildDType(),$1); } | ySTRING { $$ = new AstBasicDType($1,AstBasicDTypeKwd::STRING); } @@ -1187,6 +1192,57 @@ data_typeNoRef: // ==IEEE: data_type, excluding class_type etc referenc // // IEEE: ps_covergroup: see data_type above ; +data_type_or_void: // ==IEEE: data_type_or_void + data_type { $$=$1; } + //UNSUP yVOID { UNSUP } // No yTAGGED structures + ; + +struct_unionDecl: // IEEE: part of data_type + // // packedSigningE is NOP for unpacked + ySTRUCT packedSigningE '{' { $$ = new AstStructDType($1, $2); SYMP->pushNew($$); } + /*cont*/ struct_union_memberList '}' + { $$=$4; $$->addMembersp($5); SYMP->popScope($$); } + | yUNION taggedE packedSigningE '{' { $$ = new AstUnionDType($1, $3); SYMP->pushNew($$); } + /*cont*/ struct_union_memberList '}' + { $$=$5; $$->addMembersp($6); SYMP->popScope($$); } + ; + +struct_union_memberList: // IEEE: { struct_union_member } + struct_union_member { $$ = $1; } + | struct_union_memberList struct_union_member { $$ = $1->addNextNull($2); } + ; + +struct_union_member: // ==IEEE: struct_union_member + random_qualifierE data_type_or_void + { GRAMMARP->m_memDTypep = $2; } // As a list follows, need to attach this dtype to each member. + /*cont*/ list_of_member_decl_assignments ';' { $$ = $4; GRAMMARP->m_memDTypep = NULL; } + ; + +list_of_member_decl_assignments: // Derived from IEEE: list_of_variable_decl_assignments + member_decl_assignment { $$ = $1; } + | list_of_member_decl_assignments ',' member_decl_assignment { $$ = $1->addNextNull($3); } + ; + +member_decl_assignment: // Derived from IEEE: variable_decl_assignment + // // At present we allow only packed structures/unions. So this is different from variable_decl_assignment + id variable_dimensionListE + { if ($2) $2->v3error("Unsupported: Unpacked array in packed struct/union"); + $$ = new AstMemberDType($1, *$1, VFlagChildDType(), GRAMMARP->m_memDTypep->cloneTree(true)); } + | id variable_dimensionListE '=' variable_declExpr + { $4->v3error("Unsupported: Initial values in struct/union members."); } + | idSVKwd { $$ = NULL; } + // + // // IEEE: "dynamic_array_variable_identifier '[' ']' [ '=' dynamic_array_new ]" + // // Matches above with variable_dimensionE = "[]" + // // IEEE: "class_variable_identifier [ '=' class_new ]" + // // variable_dimensionE must be empty + // // Pushed into variable_declExpr:dynamic_array_new + // + // // IEEE: "[ covergroup_variable_identifier ] '=' class_new + // // Pushed into variable_declExpr:class_new + //UNSUP '=' class_new { UNSUP } + ; + list_of_variable_decl_assignments: // ==IEEE: list_of_variable_decl_assignments variable_decl_assignment { $$ = $1; } | list_of_variable_decl_assignments ',' variable_decl_assignment { $$ = $1->addNextNull($3); } @@ -1254,6 +1310,27 @@ variable_dimension: // ==IEEE: variable_dimension // // '[' '$' ':' expr ']' -- anyrange:expr:$ ; +random_qualifierE: // IEEE: random_qualifier + empty + /*empty*/ { } + | random_qualifier { } + ; + +random_qualifier: // ==IEEE: random_qualifier + yRAND { } // Ignored until we support randomize() + | yRANDC { } // Ignored until we support randomize() + ; + +taggedE: + /*empty*/ { } + //UNSUP yTAGGED { UNSUP } + ; + +packedSigningE: + // // AstNumeric::NOSIGN overloaded to indicate not packed + /*empty*/ { $$ = signedst_NOSIGN; } + | yPACKED signingE { $$ = $2; if ($$ == signedst_NOSIGN) $$ = signedst_UNSIGNED; } + ; + //************************************************ // enum @@ -1353,8 +1430,8 @@ type_declaration: // ==IEEE: type_declaration // // Verilator: Not important what it is in the AST, just need to make sure the yaID__aTYPE gets returned | yTYPEDEF id ';' { $$ = NULL; $$ = new AstTypedefFwd($1, *$2); SYMP->reinsert($$); } | yTYPEDEF yENUM idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($1, *$3); SYMP->reinsert($$); } - //UNSUP yTYPEDEF ySTRUCT idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($1, *$3); SYMP->reinsert($$); } - //UNSUP yTYPEDEF yUNION idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($1, *$3); SYMP->reinsert($$); } + | yTYPEDEF ySTRUCT idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($1, *$3); SYMP->reinsert($$); } + | yTYPEDEF yUNION idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($1, *$3); SYMP->reinsert($$); } //UNSUP yTYPEDEF yCLASS idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($1, *$3); SYMP->reinsert($$); } ; diff --git a/test_regress/t/t_struct_init.pl b/test_regress/t/t_struct_init.pl index 5d689e401..4f0058853 100755 --- a/test_regress/t/t_struct_init.pl +++ b/test_regress/t/t_struct_init.pl @@ -7,8 +7,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # Lesser General Public License Version 3 or the Perl Artistic License # Version 2.0. -$Self->{vlt} and $Self->unsupported("Verilator unsupported, bug181"); - compile ( ); diff --git a/test_regress/t/t_struct_notfound_bad.pl b/test_regress/t/t_struct_notfound_bad.pl new file mode 100755 index 000000000..702415cb8 --- /dev/null +++ b/test_regress/t/t_struct_notfound_bad.pl @@ -0,0 +1,18 @@ +#!/usr/bin/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. + +compile ( + fails=>1, + expect=> +'%Error: t/t_struct_notfound_bad.v:\d+: Member \'nfmember\' not found in structure +%Error: Exiting due to.*', + ); + +ok(1); +1; diff --git a/test_regress/t/t_struct_notfound_bad.v b/test_regress/t/t_struct_notfound_bad.v new file mode 100644 index 000000000..b029ee6b9 --- /dev/null +++ b/test_regress/t/t_struct_notfound_bad.v @@ -0,0 +1,15 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2012 by Wilson Snyder. + +module t (/*AUTOARG*/); + + typedef struct packed { bit m; } struct_t; + struct_t s; + + initial begin + s.nfmember = 0; // Member not found + $finish; + end +endmodule diff --git a/test_regress/t/t_struct_packed_sysfunct.pl b/test_regress/t/t_struct_packed_sysfunct.pl index 43179dd12..7058e622f 100755 --- a/test_regress/t/t_struct_packed_sysfunct.pl +++ b/test_regress/t/t_struct_packed_sysfunct.pl @@ -7,8 +7,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # Lesser General Public License Version 3 or the Perl Artistic License # Version 2.0. -$Self->{vlt} and $Self->unsupported("Verilator unsupported, bug181"); - compile ( ); diff --git a/test_regress/t/t_struct_packed_write_read.pl b/test_regress/t/t_struct_packed_write_read.pl index 43179dd12..7058e622f 100755 --- a/test_regress/t/t_struct_packed_write_read.pl +++ b/test_regress/t/t_struct_packed_write_read.pl @@ -7,8 +7,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # Lesser General Public License Version 3 or the Perl Artistic License # Version 2.0. -$Self->{vlt} and $Self->unsupported("Verilator unsupported, bug181"); - compile ( ); diff --git a/test_regress/t/t_struct_port.pl b/test_regress/t/t_struct_port.pl index 5d689e401..4f0058853 100755 --- a/test_regress/t/t_struct_port.pl +++ b/test_regress/t/t_struct_port.pl @@ -7,8 +7,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # Lesser General Public License Version 3 or the Perl Artistic License # Version 2.0. -$Self->{vlt} and $Self->unsupported("Verilator unsupported, bug181"); - compile ( ); diff --git a/test_regress/t/t_struct_unpacked_bad.pl b/test_regress/t/t_struct_unpacked_bad.pl new file mode 100755 index 000000000..b58b030b5 --- /dev/null +++ b/test_regress/t/t_struct_unpacked_bad.pl @@ -0,0 +1,18 @@ +#!/usr/bin/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. + +compile ( + fails=>$Self->{v3}, + expect=> +q{%Error: t/t_struct_unpacked_bad.v:8: Unsupported: Unpacked struct/union +.*%Error: Exiting due to.*}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_struct_unpacked_bad.v b/test_regress/t/t_struct_unpacked_bad.v new file mode 100644 index 000000000..d5030de2e --- /dev/null +++ b/test_regress/t/t_struct_unpacked_bad.v @@ -0,0 +1,16 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2009 by Wilson Snyder. + +module x; + + typedef struct { + int a; + } notpacked_t; + + typedef struct packed { + notpacked_t a; + } ispacked_t; + +endmodule