MAJOR: Support packed structures and unions, bug181.

This commit is contained in:
Wilson Snyder 2012-07-29 10:16:20 -04:00
parent aec019991c
commit 6339159b04
23 changed files with 517 additions and 40 deletions

View File

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

View File

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

View File

@ -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<string,AstMemberDType*> 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)

View File

@ -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: "<<itemp->prettyName()); }
else m_members.insert(make_pair(itemp->name(), itemp));
}
}
bool AstNodeClassDType::broken() const {
set<AstMemberDType*> 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: "<<it->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]";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -65,7 +65,9 @@ struct V3ParseBisonYYSType {
AstCaseItem* caseitemp;
AstCell* cellp;
AstConst* constp;
AstMemberDType* memberp;
AstNodeModule* modulep;
AstNodeClassDType* classp;
AstNodeDType* dtypep;
AstNodeFTask* ftaskp;
AstNodeFTaskRef* ftaskrefp;

View File

@ -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: "<<nodep<<endl);
nodep->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 "<<nodep<<endl);
//if (debug()>=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 "<<nodep<<endl);
if (debug()>=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 "<<fromDtp<<endl);
AstNodeClassDType* fromClassp = fromDtp->castNodeClassDType();
AstMemberDType* memberp = NULL; // NULL=error below
if (!fromClassp) {
nodep->v3error("Member selection of non-struct/union object '"
<<nodep->fromp()->prettyTypeName()<<"' which is a '"<<nodep->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 '"<<nodep->prettyName()<<"' not found in structure");
}
}
if (memberp) {
if (m_attrp) { // Looking for the base of the attribute
nodep->dtypep(memberp);
UINFO(9," MEMBERSEL(attr) -> "<<nodep<<endl);
} else {
AstSel* newp = new AstSel(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
memberp->lsb(), memberp->width());
newp->dtypep(memberp);
newp->didWidth(true); // Don't replace dtype with basic type
UINFO(9," MEMBERSEL -> "<<newp<<endl);
nodep->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) {

View File

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

View File

@ -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: "<<errp->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 "<<newp<<endl); if (debug()>=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 "<<newp<<endl); if (debug()>=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 "<<newp<<endl);
//if (debug()>=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("["<<msb<<":"<<lsb<<"] Range extract has backward bit ordering, perhaps you wanted ["<<lsb<<":"<<msb<<"]");
int x = msb; msb = lsb; lsb = x;
}
AstNode* widthp = new AstConst (msbp->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 "<<newp<<endl);
//if (debug()>=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");

View File

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

View File

@ -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<fl> yOR "or"
%token<fl> yOUTPUT "output"
%token<fl> yPACKAGE "package"
%token<fl> yPACKED "packed"
%token<fl> yPARAMETER "parameter"
%token<fl> yPMOS "pmos"
%token<fl> yPOSEDGE "posedge"
@ -355,6 +358,8 @@ class AstSenTree;
%token<fl> yPULLDOWN "pulldown"
%token<fl> yPULLUP "pullup"
%token<fl> yPURE "pure"
%token<fl> yRAND "rand"
%token<fl> yRANDC "randc"
%token<fl> yRCMOS "rcmos"
%token<fl> yREAL "real"
%token<fl> yREALTIME "realtime"
@ -373,6 +378,7 @@ class AstSenTree;
%token<fl> ySPECPARAM "specparam"
%token<fl> ySTATIC "static"
%token<fl> ySTRING "string"
%token<fl> ySTRUCT "struct"
%token<fl> ySUPPLY0 "supply0"
%token<fl> ySUPPLY1 "supply1"
%token<fl> yTABLE "table"
@ -388,6 +394,7 @@ class AstSenTree;
%token<fl> yTRI1 "tri1"
%token<fl> yTRUE "true"
%token<fl> yTYPEDEF "typedef"
%token<fl> yUNION "union"
%token<fl> yUNIQUE "unique"
%token<fl> yUNIQUE0 "unique0"
%token<fl> yUNSIGNED "unsigned"
@ -1170,10 +1177,8 @@ data_typeBasic<dtypep>: // IEEE: part of data_type
data_typeNoRef<dtypep>: // ==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<dtypep>: // ==IEEE: data_type, excluding class_type etc referenc
// // IEEE: ps_covergroup: see data_type above
;
data_type_or_void<dtypep>: // ==IEEE: data_type_or_void
data_type { $$=$1; }
//UNSUP yVOID { UNSUP } // No yTAGGED structures
;
struct_unionDecl<classp>: // IEEE: part of data_type
// // packedSigningE is NOP for unpacked
ySTRUCT packedSigningE '{' { $<classp>$ = new AstStructDType($1, $2); SYMP->pushNew($<classp>$); }
/*cont*/ struct_union_memberList '}'
{ $$=$<classp>4; $$->addMembersp($5); SYMP->popScope($$); }
| yUNION taggedE packedSigningE '{' { $<classp>$ = new AstUnionDType($1, $3); SYMP->pushNew($<classp>$); }
/*cont*/ struct_union_memberList '}'
{ $$=$<classp>5; $$->addMembersp($6); SYMP->popScope($$); }
;
struct_union_memberList<nodep>: // IEEE: { struct_union_member }
struct_union_member { $$ = $1; }
| struct_union_memberList struct_union_member { $$ = $1->addNextNull($2); }
;
struct_union_member<nodep>: // ==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<nodep>: // 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<memberp>: // 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($<fl>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<nodep>: // ==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<rangep>: // ==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<signstate>:
// // 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<nodep>: // ==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($<fl>1, *$2); SYMP->reinsert($$); }
| yTYPEDEF yENUM idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$3); SYMP->reinsert($$); }
//UNSUP yTYPEDEF ySTRUCT idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$3); SYMP->reinsert($$); }
//UNSUP yTYPEDEF yUNION idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$3); SYMP->reinsert($$); }
| yTYPEDEF ySTRUCT idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$3); SYMP->reinsert($$); }
| yTYPEDEF yUNION idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$3); SYMP->reinsert($$); }
//UNSUP yTYPEDEF yCLASS idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$3); SYMP->reinsert($$); }
;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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