Support little endian bit vectors ("reg [0:2] x;").

This commit is contained in:
Wilson Snyder 2009-10-25 16:53:55 -04:00
parent 350028553b
commit 39444d83c5
25 changed files with 785 additions and 287 deletions

View File

@ -5,6 +5,8 @@ indicates the contributor was also the author of the fix; Thanks!
* Verilator 3.71****
** Support little endian bit vectors ("reg [0:2] x;").
**** Fix writing to out-of-bounds arrays writing element 0.
**** Fix core dump with SystemVerilog var declarations under unnamed begins.

View File

@ -667,8 +667,8 @@ Disable the specified warning message.
Disable all lint related warning messages. This is equivalent to
"-Wno-CASEINCOMPLETE -Wno-CASEOVERLAP -Wno-CASEX -Wno-CASEWITHX
-Wno-CMPCONST -Wno-IMPLICIT -Wno-UNDRIVEN -Wno-UNSIGNED -Wno-UNUSED
-Wno-VARHIDDEN -Wno-WIDTH".
-Wno-CMPCONST -Wno-IMPLICIT -Wno-LITENDIAN -Wno-UNDRIVEN -Wno-UNSIGNED
-Wno-UNUSED -Wno-VARHIDDEN -Wno-WIDTH".
It is strongly recommended you cleanup your code rather than using this
option, it is only intended to be use when running test-cases of code
@ -1954,6 +1954,16 @@ Verilator cannot schedule these variables correctly.
Ignoring this warning may make Verilator simulations differ from other
simulators.
=item LITENDIAN
Warns that a vector is declared with little endian bit numbering
(i.e. [0:7]). Big endian bit numbering is now the overwhelming standard,
and little numbering is now thus often due to simple oversight instead of
intent.
Ignoring this warning will only suppress the lint check, it will simulate
correctly.
=item MULTIDRIVEN
Warns that the specified signal comes from multiple always blocks. This is

View File

@ -204,6 +204,7 @@ RAW_OBJS = \
V3Unknown.o \
V3Unroll.o \
V3Width.o \
V3WidthSel.o \
# Non-concatable
OBJS += \

View File

@ -170,9 +170,8 @@ class AstAttrType {
public:
enum en {
BITS, // V3Const converts to constant
RANGE_LSB, // V3Const converts to constant
ARRAY_LSB, // V3Const converts to constant
//
VAR_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes
VAR_CLOCK, // V3LinkParse moves to AstVar::attrScClocked
VAR_CLOCK_ENABLE, // V3LinkParse moves to AstVar::attrClockEn
VAR_PUBLIC, // V3LinkParse moves to AstVar::sigPublic
@ -182,7 +181,7 @@ public:
enum en m_e;
const char* ascii() const {
static const char* names[] = {
"BITS", "RANGE_LSB", "ARRAY_LSB",
"BITS", "VAR_BASE",
"VAR_CLOCK", "VAR_CLOCK_ENABLE", "VAR_PUBLIC", "VAR_PUBLIC_FLAT",
"VAR_ISOLATE_ASSIGNMENTS"
};
@ -942,11 +941,14 @@ struct AstNodePreSel : public AstNode {
setOp1p(lhs); setOp2p(rhs); setNOp3p(ths); }
ASTNODE_BASE_FUNCS(NodePreSel)
AstNode* lhsp() const { return op1p()->castNode(); }
AstNode* fromp() const { return lhsp(); }
AstNode* rhsp() const { return op2p()->castNode(); }
AstNode* thsp() const { return op3p()->castNode(); }
AstAttrOf* attrp() const { return op4p()->castAttrOf(); }
void lhsp(AstNode* nodep) { return setOp1p(nodep); }
void rhsp(AstNode* nodep) { return setOp2p(nodep); }
void thsp(AstNode* nodep) { return setOp3p(nodep); }
void attrp(AstAttrOf* nodep) { return setOp4p((AstNode*)nodep); }
// METHODS
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(AstNode*) const { return true; }

View File

@ -141,6 +141,14 @@ AstRange* AstVar::arrayp(int dimension) const {
return NULL;
}
int AstVar::arrayDimensions() const {
int entries=0;
for (AstRange* arrayp=this->arraysp(); arrayp; arrayp = arrayp->nextp()->castRange()) {
entries++;
}
return entries;
}
uint32_t AstVar::arrayElements() const {
uint32_t entries=1;
for (AstRange* arrayp=this->arraysp(); arrayp; arrayp = arrayp->nextp()->castRange()) {
@ -149,6 +157,37 @@ uint32_t AstVar::arrayElements() const {
return entries;
}
// Special operators
int AstArraySel::dimension(AstNode* nodep) { ///< How many dimensions is this reference from the base variable?
// Only called after V3Param; so only ArraySel's need to be recursed
int dim = 0;
while (nodep) {
if (nodep->castNodeSel()) { dim++; nodep=nodep->castNodeSel()->fromp(); continue; }
if (nodep->castNodePreSel()) { dim++; nodep=nodep->castNodePreSel()->fromp(); continue; }
break;
}
return dim;
}
AstNode* AstArraySel::baseFromp(AstNode* nodep) { ///< What is the base variable (or const) this dereferences?
// Else AstArraySel etc; search for the base
while (nodep) {
if (nodep->castArraySel()) { nodep=nodep->castArraySel()->fromp(); continue; }
else if (nodep->castSel()) { nodep=nodep->castSel()->fromp(); continue; }
// AstNodeSelPre stashes the associated variable under a ATTROF so it isn't constified
else if (nodep->castAttrOf()) { nodep=nodep->castAttrOf()->fromp(); continue; }
else if (nodep->castNodePreSel()) {
if (nodep->castNodePreSel()->attrp()) {
nodep=nodep->castNodePreSel()->attrp();
} else {
nodep=nodep->castNodePreSel()->lhsp();
}
continue;
}
else break;
}
return nodep;
}
bool AstScope::broken() const {
return ((m_aboveScopep && !m_aboveScopep->brokeExists())
|| (m_aboveCellp && !m_aboveCellp->brokeExists())
@ -303,6 +342,10 @@ void AstPin::dump(ostream& str) {
else { str<<" ->UNLINKED"; }
if (svImplicit()) str<<" [.SV]";
}
void AstRange::dump(ostream& str) {
this->AstNode::dump(str);
if (littleEndian()) str<<" [LITTLE]";
}
void AstVarXRef::dump(ostream& str) {
this->AstNode::dump(str);
if (lvalue()) str<<" [LV] => ";

View File

@ -76,20 +76,30 @@ public:
struct AstRange : public AstNode {
// Range specification, for use under variables and cells
private:
bool m_littleEndian:1; // Bit vector is little endian
public:
AstRange(FileLine* fl, AstNode* msbp, AstNode* lsbp)
:AstNode(fl) {
m_littleEndian = false;
setOp2p(msbp); setOp3p(lsbp); }
AstRange(FileLine* fl, int msb, int lsb)
:AstNode(fl) {
m_littleEndian = false;
setOp2p(new AstConst(fl,msb)); setOp3p(new AstConst(fl,lsb));
width(msb-lsb+1,msb-lsb+1);
}
ASTNODE_NODE_FUNCS(Range, RANGE)
AstNode* msbp() const { return op2p()->castNode(); } // op2 = Msb expression
AstNode* lsbp() const { return op3p()->castNode(); } // op3 = Lsb expression
AstNode* msbp() const { return op2p()->castNode(); } // op2 = Msb expression
AstNode* lsbp() const { return op3p()->castNode(); } // op3 = Lsb expression
AstNode* msbEndianedp() const { return littleEndian()?lsbp():msbp(); } // How to show a declaration
AstNode* lsbEndianedp() const { return littleEndian()?msbp():lsbp(); }
int msbConst() const { AstConst* constp=msbp()->castConst(); return (constp?constp->toSInt():0); }
int lsbConst() const { AstConst* constp=lsbp()->castConst(); return (constp?constp->toSInt():0); }
int elementsConst() const { return msbConst()-lsbConst()+1; }
int elementsConst() const { return (msbConst()>lsbConst()) ? msbConst()-lsbConst()+1 : lsbConst()-msbConst()+1; }
bool littleEndian() const { return m_littleEndian; }
void littleEndian(bool flag) { m_littleEndian=flag; }
virtual void dump(ostream& str);
virtual string emitC() { V3ERROR_NA; return ""; }
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(AstNode* samep) const { return true; }
@ -120,15 +130,8 @@ struct AstArraySel : public AstNodeSel {
virtual bool same(AstNode* samep) const { return true; }
virtual int instrCount() const { return widthInstrs(); }
// Special operators
static int dimension(AstNode* nodep) { ///< How many dimensions is this reference from the base variable?
int dim = 0;
while (nodep && nodep->castArraySel()) { dim++; nodep=nodep->castArraySel()->fromp(); }
return dim;
}
static AstNode* baseFromp(AstNode* nodep) { ///< What is the base variable (or const) this dereferences?
while (nodep && nodep->castArraySel()) { nodep=nodep->castArraySel()->fromp(); }
return nodep;
}
static int dimension(AstNode* nodep); ///< How many dimensions is this reference from the base variable?
static AstNode* baseFromp(AstNode* nodep); ///< What is the base variable (or const) this dereferences?
};
struct AstWordSel : public AstNodeSel {
@ -153,6 +156,8 @@ struct AstSelExtract : public AstNodePreSel {
AstSelExtract(FileLine* fl, AstNode* fromp, AstNode* msbp, AstNode* lsbp)
: AstNodePreSel(fl, fromp, msbp, lsbp) {}
ASTNODE_NODE_FUNCS(SelExtract, SELEXTRACT)
AstNode* msbp() const { return rhsp(); }
AstNode* lsbp() const { return thsp(); }
};
struct AstSelBit : public AstNodePreSel {
@ -163,6 +168,7 @@ struct AstSelBit : public AstNodePreSel {
width(1,1);
}
ASTNODE_NODE_FUNCS(SelBit, SELBIT)
AstNode* bitp() const { return rhsp(); }
};
struct AstSelPlus : public AstNodePreSel {
@ -171,6 +177,8 @@ struct AstSelPlus : public AstNodePreSel {
AstSelPlus(FileLine* fl, AstNode* fromp, AstNode* bitp, AstNode* widthp)
:AstNodePreSel(fl, fromp, bitp, widthp) {}
ASTNODE_NODE_FUNCS(SelPlus, SELPLUS)
AstNode* bitp() const { return rhsp(); }
AstNode* widthp() const { return thsp(); }
};
struct AstSelMinus : public AstNodePreSel {
@ -179,6 +187,8 @@ struct AstSelMinus : public AstNodePreSel {
AstSelMinus(FileLine* fl, AstNode* fromp, AstNode* bitp, AstNode* widthp)
:AstNodePreSel(fl, fromp, bitp, widthp) {}
ASTNODE_NODE_FUNCS(SelMinus, SELMINUS)
AstNode* bitp() const { return rhsp(); }
AstNode* widthp() const { return thsp(); }
};
struct AstSel : public AstNodeTriop {
@ -358,7 +368,11 @@ public:
int widthTotalBytes() const; // Width in bytes rounding up 1,2,4,8,12,...
int msb() const { if (!rangep()) return 0; return rangep()->msbConst(); }
int lsb() const { if (!rangep()) return 0; return rangep()->lsbConst(); }
int msbEndianed() const { if (!rangep()) return 0; return littleEndian()?rangep()->lsbConst():rangep()->msbConst(); }
int lsbEndianed() const { if (!rangep()) return 0; return littleEndian()?rangep()->msbConst():rangep()->lsbConst(); }
int msbMaxSelect() const { return (lsb()<0 ? msb()-lsb() : msb()); } // Maximum value a [] select may index
bool littleEndian() const { return (rangep() && rangep()->littleEndian()); }
int arrayDimensions() const;
uint32_t arrayElements() const; // 1, or total multiplication of all dimensions
virtual string verilogKwd() const;
void propagateAttrFrom(AstVar* fromp) {
@ -1620,7 +1634,7 @@ public:
widthSignedFrom(varp);
m_code = 0;
m_codeInc = varp->arrayElements() * varp->widthWords();
m_lsb = varp->lsb(); m_msb = varp->msb();
m_lsb = varp->lsbEndianed(); m_msb = varp->msbEndianed();
m_arrayLsb = varp->arrayp(0) ? varp->arrayp(0)->lsbConst() : 0;
m_arrayMsb = varp->arrayp(0) ? varp->arrayp(0)->msbConst() : 0;
}
@ -1634,8 +1648,8 @@ public:
uint32_t code() const { return m_code; }
void code(uint32_t code) { m_code=code; }
uint32_t codeInc() const { return m_codeInc; }
int msb() const { return m_msb; }
int lsb() const { return m_lsb; }
int msbEndianed() const { return m_msb; } // Note msb maybe < lsb if little endian
int lsbEndianed() const { return m_lsb; }
uint32_t arrayMsb() const { return m_arrayMsb; }
uint32_t arrayLsb() const { return m_arrayLsb; }
uint32_t arrayWidth() const { if (!arrayMsb()) return 0; return arrayMsb()-arrayLsb()+1; }
@ -1717,15 +1731,14 @@ struct AstAttrOf : public AstNode {
private:
// Return a value of a attribute, for example a LSB or array LSB of a signal
AstAttrType m_attrType; // What sort of extraction
int m_dimension; // Dimension number (0 is leftmost), for ARRAY_LSB extractions
public:
AstAttrOf(FileLine* fl, AstAttrType attrtype, AstNode* fromp=NULL, int dimension=0)
AstAttrOf(FileLine* fl, AstAttrType attrtype, AstNode* fromp=NULL)
: AstNode(fl) {
setNOp1p(fromp); m_attrType = attrtype; m_dimension = dimension; }
setNOp1p(fromp);
m_attrType = attrtype; }
ASTNODE_NODE_FUNCS(AttrOf, ATTROF)
AstNode* fromp() const { return op1p(); }
AstAttrType attrType() const { return m_attrType; }
int dimension() const { return m_dimension; }
virtual void dump(ostream& str=cout);
};

View File

@ -1053,7 +1053,7 @@ private:
nodep->iterateChildren(*this);
if (!nodep->varp()) nodep->v3fatalSrc("Not linked");
bool did=false;
if (!m_cpp && nodep->varp()->hasSimpleInit()) {
if (!m_cpp && nodep->varp()->hasSimpleInit() && !nodep->backp()->castAttrOf()) {
//if (debug()) nodep->varp()->initp()->dumpTree(cout," visitvaref: ");
nodep->varp()->initp()->iterateAndNext(*this);
if (operandConst(nodep->varp()->initp())
@ -1079,23 +1079,7 @@ private:
V3Number num (nodep->fileline(), 32, nodep->fromp()->widthMin());
replaceNum(nodep, num); nodep=NULL;
} else {
if (!nodep->fromp()->castNodeVarRef()) nodep->v3fatalSrc("Not linked");
AstVar* varp = nodep->fromp()->castNodeVarRef()->varp();
if (!varp) nodep->v3fatalSrc("Not linked");
if (nodep->attrType()==AstAttrType::RANGE_LSB) {
if (!varp->rangep()) nodep->v3fatalSrc("RANGE_LSB on vec w/o range\n");
if (operandConst(varp->rangep()->lsbp())) {
V3Number num (nodep->fileline(), 32, varp->lsb());
replaceNum(nodep, num); nodep=NULL;
}
} else if (nodep->attrType()==AstAttrType::ARRAY_LSB) {
AstRange* arrayp=varp->arrayp(nodep->dimension());
if (!arrayp) nodep->v3fatalSrc("ARRAY_LSB on vec w/o range or right # dimensions\n");
if (operandConst(arrayp->lsbp())) {
V3Number num (nodep->fileline(), 32, arrayp->lsbConst());
replaceNum(nodep, num); nodep=NULL;
}
} else nodep->v3fatalSrc("Missing ATTR type case\n");
nodep->v3fatalSrc("Missing ATTR type case");
}
}
bool onlySenItemInSenTree(AstNodeSenItem* nodep) {
@ -1471,6 +1455,10 @@ private:
}
}
// These are converted by V3Param. Don't constify as we don't want the from() VARREF to disappear, if any
// If output of a presel didn't get consted, chances are V3Param didn't visit properly
virtual void visit(AstNodePreSel* nodep, AstNUser*) {}
//-----
// Below lines are magic expressions processed by astgen
// "AstNODETYPE { # bracket not paren
@ -1746,7 +1734,9 @@ public:
AstNode* V3Const::constifyParamsEdit(AstNode* nodep) {
//if (debug()>0) nodep->dumpTree(cout," forceConPRE : ");
nodep = V3Width::widthParamsEditIfNeed(nodep); // Make sure we've sized everything first
// Resize even if the node already has a width, because burried in the treee we may
// have a node we just created with signing, etc, that isn't sized yet.
nodep = V3Width::widthParamsEdit(nodep); // Make sure we've sized everything first
ConstVisitor visitor (true,false,false,false);
if (AstVar* varp=nodep->castVar()) {
// If a var wants to be constified, it's really a param, and

View File

@ -1839,7 +1839,7 @@ class EmitCTrace : EmitCStmts {
puts("vcdp->declArray");
} else if (nodep->isQuad()) {
puts("vcdp->declQuad ");
} else if (nodep->msb() || nodep->lsb()) {
} else if (nodep->msbEndianed() || nodep->lsbEndianed()) {
puts("vcdp->declBus ");
} else {
puts("vcdp->declBit ");
@ -1853,8 +1853,8 @@ class EmitCTrace : EmitCStmts {
} else {
puts(",-1");
}
if (nodep->msb() || nodep->lsb()) {
puts(","+cvtToStr(nodep->msb())+","+cvtToStr(nodep->lsb()));
if (nodep->msbEndianed() || nodep->lsbEndianed()) {
puts(","+cvtToStr(nodep->msbEndianed())+","+cvtToStr(nodep->lsbEndianed()));
}
puts(");");
}
@ -1868,7 +1868,7 @@ class EmitCTrace : EmitCStmts {
puts("vcdp->"+full+"Array");
} else if (nodep->isQuad()) {
puts("vcdp->"+full+"Quad ");
} else if (nodep->declp()->msb() || nodep->declp()->lsb()) {
} else if (nodep->declp()->msbEndianed() || nodep->declp()->lsbEndianed()) {
puts("vcdp->"+full+"Bus ");
} else {
puts("vcdp->"+full+"Bit ");
@ -1877,7 +1877,7 @@ class EmitCTrace : EmitCStmts {
+ ((arrayindex<0) ? 0 : (arrayindex*nodep->declp()->widthWords()))));
puts(",");
emitTraceValue(nodep, arrayindex);
if (nodep->declp()->msb() || nodep->declp()->lsb()) {
if (nodep->declp()->msbEndianed() || nodep->declp()->lsbEndianed()) {
puts(","+cvtToStr(nodep->declp()->widthMin()));
}
puts(");\n");

View File

@ -368,8 +368,8 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
}
virtual void visit(AstRange* nodep, AstNUser*) {
puts("[");
nodep->msbp()->iterateAndNext(*this); puts(":");
nodep->lsbp()->iterateAndNext(*this); puts("]");
nodep->msbEndianedp()->iterateAndNext(*this); puts(":");
nodep->lsbEndianedp()->iterateAndNext(*this); puts("]");
}
virtual void visit(AstSel* nodep, AstNUser*) {
nodep->fromp()->iterateAndNext(*this); puts("[");

View File

@ -64,6 +64,7 @@ public:
IMPERFECTSCH, // Imperfect schedule (disabled by default)
IMPLICIT, // Implicit wire
IMPURE, // Impure function not being inlined
LITENDIAN, // Little bit endian vector
MULTIDRIVEN, // Driven from multiple blocks
REDEFMACRO, // Redefining existing define macro
UNDRIVEN, // No drivers
@ -96,6 +97,7 @@ public:
"BLKANDNBLK",
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CMPCONST",
"COMBDLY", "STMTDLY", "SYMRSVDWORD", "GENCLK", "IMPERFECTSCH", "IMPLICIT", "IMPURE",
"LITENDIAN",
"MULTIDRIVEN", "REDEFMACRO",
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNSIGNED", "UNUSED",
"VARHIDDEN", "WIDTH", "WIDTHCONCAT",
@ -118,6 +120,7 @@ public:
|| m_e==CASEWITHX || m_e==CASEX
|| m_e==CMPCONST
|| m_e==IMPLICIT
|| m_e==LITENDIAN
|| m_e==UNDRIVEN || m_e==UNSIGNED
|| m_e==UNUSED || m_e==VARHIDDEN
|| m_e==WIDTH); }

View File

@ -160,6 +160,11 @@ private:
selp->replaceWith(fromp); selp->deleteTree(); selp=NULL;
did=1;
}
if (AstNodePreSel* selp = nodep->sensp()->castNodePreSel()) {
AstNode* fromp = selp->lhsp()->unlinkFrBack();
selp->replaceWith(fromp); selp->deleteTree(); selp=NULL;
did=1;
}
}
}
if (!nodep->sensp()->castNodeVarRef()) {
@ -171,151 +176,19 @@ private:
nodep->v3fatalSrc("SenGates shouldn't be in tree yet");
}
void iterateSelTriop(AstNodePreSel* nodep) {
nodep->iterateChildren(*this);
}
AstNode* newSubAttrOf(AstNode* underp, AstNode* fromp, AstAttrType attrType) {
// Account for a variable's LSB in bit selections
// Replace underp with a SUB(underp, ATTROF(varp, attrType))
int dimension=0;
while (fromp && !fromp->castNodeVarRef() && (fromp->castSel() || fromp->castArraySel())) {
if (fromp->castSel()) fromp = fromp->castSel()->fromp();
else fromp = fromp->castArraySel()->fromp();
dimension++;
virtual void visit(AstNodePreSel* nodep, AstNUser*) {
if (!nodep->attrp()) {
nodep->iterateChildren(*this);
// Constification may change the fromp() to a constant, which will loose the
// variable we're extracting from (to determine MSB/LSB/endianness/etc.)
// 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)));
}
AstNodeVarRef* varrefp = fromp->castNodeVarRef();
if (!varrefp) fromp->v3fatalSrc("Bit/array selection of non-variable");
if (!varrefp->varp()) varrefp->v3fatalSrc("Signal not linked");
AstRange* vararrayp = varrefp->varp()->arrayp(dimension);
AstRange* varrangep = varrefp->varp()->rangep();
if ((attrType==AstAttrType::ARRAY_LSB
// SUB #'s Not needed because LSB==0? (1D only, else we'll constify it later)
? (vararrayp && !vararrayp->lsbp()->isZero())
: (varrangep && !varrangep->lsbp()->isZero()))) {
AstNode* newrefp;
if (varrefp->castVarXRef()) {
newrefp = new AstVarXRef(underp->fileline(),
varrefp->varp(), varrefp->castVarXRef()->dotted(), false);
} else {
newrefp = new AstVarRef (underp->fileline(),
varrefp->varp(), false);
}
AstNode* newp = new AstSub (underp->fileline(),
underp,
new AstAttrOf(underp->fileline(),
attrType, newrefp, dimension));
return newp;
} else {
return underp;
}
}
void selCheckDimension(AstSel* nodep) {
// Perform error checks on the node
AstNode* fromp = nodep->lhsp();
AstNode* basefromp = AstArraySel::baseFromp(fromp);
AstNodeVarRef* varrefp = basefromp->castNodeVarRef();
AstVar* varp = varrefp ? varrefp->varp() : NULL;
if (fromp->castSel()
|| (varp && !varp->rangep() && !varp->isParam())) {
nodep->v3error("Illegal bit select; variable already selected, or bad dimension");
}
}
virtual void visit(AstSelBit* nodep, AstNUser*) {
// Couldn't tell until link time if [#] references a bit or an array
iterateSelTriop(nodep);
AstNode* fromp = nodep->lhsp()->unlinkFrBack();
AstNode* bitp = nodep->rhsp()->unlinkFrBack();
AstNode* basefromp = AstArraySel::baseFromp(fromp);
int dimension = AstArraySel::dimension(fromp);
AstNodeVarRef* varrefp = basefromp->castNodeVarRef();
if (varrefp
&& varrefp->varp()
&& varrefp->varp()->arrayp(dimension)) {
// SELBIT(array, index) -> ARRAYSEL(array, index)
AstNode* newp = new AstArraySel
(nodep->fileline(),
fromp,
newSubAttrOf(bitp, fromp, AstAttrType::ARRAY_LSB));
nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL;
}
else {
// SELBIT(range, index) -> SEL(array, index, 1)
V3Number one (nodep->fileline(),32,1); one.width(32,false); // Unsized so width from user
AstSel* newp = new AstSel
(nodep->fileline(),
fromp,
newSubAttrOf(bitp, fromp, AstAttrType::RANGE_LSB),
new AstConst (nodep->fileline(),one));
selCheckDimension(newp);
nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL;
}
}
virtual void visit(AstSelExtract* nodep, AstNUser*) {
// SELEXTRACT(from,msb,lsb) -> SEL(from, lsb, 1+msb-lsb)
iterateSelTriop(nodep);
AstNode* fromp = nodep->lhsp()->unlinkFrBack();
AstNode* msbp = nodep->rhsp()->unlinkFrBack();
AstNode* lsbp = nodep->thsp()->unlinkFrBack();
AstNode* widthp;
if (msbp->castConst() && lsbp->castConst()) {
// Quite common, save V3Const some effort
V3Number widnum (msbp->fileline(),32,msbp->castConst()->toSInt() +1-lsbp->castConst()->toSInt());
widnum.width(32,false); // Unsized so width from user
widthp = new AstConst (msbp->fileline(), widnum);
pushDeletep(msbp);
} else {
V3Number one (nodep->fileline(),32,1); one.width(32,false); // Unsized so width from user
widthp = new AstSub (lsbp->fileline(),
new AstAdd(msbp->fileline(),
new AstConst(msbp->fileline(),one),
msbp),
lsbp->cloneTree(true));
}
AstSel* newp = new AstSel
(nodep->fileline(),
fromp,
newSubAttrOf(lsbp, fromp, AstAttrType::RANGE_LSB),
widthp);
selCheckDimension(newp);
nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL;
}
virtual void visit(AstSelPlus* nodep, AstNUser*) {
// SELPLUS -> SEL
iterateSelTriop(nodep);
AstNode* fromp = nodep->lhsp()->unlinkFrBack();
AstNode* lsbp = nodep->rhsp()->unlinkFrBack();
AstNode* widthp = nodep->thsp()->unlinkFrBack();
AstSel* newp = new AstSel
(nodep->fileline(),
fromp,
newSubAttrOf(lsbp, fromp, AstAttrType::RANGE_LSB),
widthp);
selCheckDimension(newp);
nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL;
}
virtual void visit(AstSelMinus* nodep, AstNUser*) {
// SELMINUS(from,msb,width) -> SEL(from, msb-(width-1)-lsb#)
iterateSelTriop(nodep);
AstNode* fromp = nodep->lhsp()->unlinkFrBack();
AstNode* msbp = nodep->rhsp()->unlinkFrBack();
AstNode* widthp = nodep->thsp()->unlinkFrBack();
V3Number one (msbp->fileline(),32,1); one.width(32,false); // Unsized so width from user
AstSel* newp = new AstSel
(nodep->fileline(),
fromp,
newSubAttrOf(new AstSub (msbp->fileline(),
msbp,
new AstSub (msbp->fileline(),
widthp->cloneTree(true),
new AstConst (msbp->fileline(), one))),
fromp, AstAttrType::RANGE_LSB),
widthp);
selCheckDimension(newp);
nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL;
}
virtual void visit(AstCaseItem* nodep, AstNUser*) {

View File

@ -55,7 +55,9 @@ private:
// NODE STATE
// AstModule::user4() // bool True if parameters numbered
// AstVar::user4() // int Global parameter number (for naming new module)
// // (0=not processed, 1=iterated, but no number, 65+ parameter numbered)
AstUser4InUse m_inuser4;
// User1/2/3 used by constant function simulations
// STATE
typedef std::map<AstVar*,AstVar*> VarCloneMap;
@ -93,7 +95,7 @@ private:
}
}
string paramSmallName(AstModule* modp, AstVar* varp) {
if (!varp->user4()) {
if (varp->user4()<=1) {
makeSmallNames(modp);
}
int index = varp->user4()/256;
@ -129,11 +131,19 @@ private:
// Make sure all parameters are constantified
virtual void visit(AstVar* nodep, AstNUser*) {
if (nodep->isParam()) {
if (!nodep->hasSimpleInit()) { nodep->v3fatalSrc("Parameter without initial value"); }
V3Const::constifyParamsEdit(nodep); // The variable, not just the var->init()
if (!nodep->user4()) {
nodep->user4(1); // Mark done - Note values >1 used for letter numbering
nodep->iterateChildren(*this);
if (nodep->isParam()) {
if (!nodep->hasSimpleInit()) { nodep->v3fatalSrc("Parameter without initial value"); }
V3Const::constifyParamsEdit(nodep); // The variable, not just the var->init()
}
}
}
// Make sure varrefs cause vars to constify before things above
virtual void visit(AstVarRef* nodep, AstNUser*) {
if (nodep->varp()) nodep->varp()->iterate(*this);
}
// Generate Statements
virtual void visit(AstGenerate* nodep, AstNUser*) {
@ -151,6 +161,7 @@ private:
nodep->deleteTree(); nodep=NULL;
}
virtual void visit(AstGenIf* nodep, AstNUser*) {
nodep->condp()->iterateAndNext(*this);
V3Width::widthParamsEdit(nodep); // Param typed widthing will NOT recurse the body
V3Const::constifyParamsEdit(nodep->condp()); // condp may change
if (AstConst* constp = nodep->condp()->castConst()) {
@ -173,10 +184,12 @@ private:
// We parse a very limited form of FOR, so we don't need to do a full
// simulation to unroll the loop
V3Width::widthParamsEdit(nodep); // Param typed widthing will NOT recurse the body
// Note V3Unroll will replace some AstVarRef's to the loop variable with constants
V3Unroll::unrollGen(nodep); nodep=NULL;
}
virtual void visit(AstGenCase* nodep, AstNUser*) {
AstNode* keepp = NULL;
nodep->exprp()->iterateAndNext(*this);
V3Case::caseLint(nodep);
V3Width::widthParamsEdit(nodep); // Param typed widthing will NOT recurse the body
V3Const::constifyParamsEdit(nodep->exprp()); // exprp may change
@ -185,6 +198,7 @@ private:
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
for (AstNode* ep = itemp->condsp(); ep; ) {
AstNode* nextp = ep->nextp(); //May edit list
ep->iterateAndNext(*this);
V3Const::constifyParamsEdit(ep); ep=NULL; // ep may change
ep = nextp;
}
@ -240,6 +254,7 @@ public:
void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
// Cell: Check for parameters in the instantiation.
nodep->iterateChildren(*this);
if (!nodep->modp()) { nodep->dumpTree(cerr,"error:"); nodep->v3fatalSrc("Not linked?"); }
if (nodep->paramsp()) {
UINFO(4,"De-parameterize: "<<nodep<<endl);

View File

@ -503,7 +503,7 @@ private:
UINFO(5," FUNCREF "<<nodep<<endl);
if (!m_params) { badNodeType(nodep); return; }
AstFunc* funcp = nodep->taskp()->castFunc(); if (!funcp) nodep->v3fatalSrc("Not linked");
V3Width::widthParamsEditIfNeed(funcp); funcp=NULL; // Make sure we've sized the function
if (m_params) { V3Width::widthParamsEdit(funcp); } funcp=NULL; // Make sure we've sized the function
funcp = nodep->taskp()->castFunc(); if (!funcp) nodep->v3fatalSrc("Not linked");
// Apply function call values to function
// Note we'd need a stack if we allowed recursive functions!

View File

@ -388,7 +388,8 @@ private:
if (m_varModeReplace
&& nodep->varp() == m_forVarp
&& nodep->varScopep() == m_forVscp
&& !nodep->lvalue()) {
&& !nodep->lvalue()
&& !nodep->backp()->castAttrOf()) { // Most likely under a select
AstNode* newconstp = m_varValuep->cloneTree(false);
nodep->replaceWith(newconstp);
pushDeletep(nodep);

View File

@ -249,39 +249,20 @@ private:
}
}
virtual void visit(AstRange* nodep, AstNUser* vup) {
// If there's an edit, then processes the edit'ee (can't just rely on iterateChildren because sometimes we for(...) here ourself
AstNode* selp = V3Width::widthSelNoIterEdit(nodep); if (selp!=nodep) { nodep=NULL; selp->iterate(*this,vup); return; }
if (vup->c()->prelim()) {
nodep->msbp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
nodep->lsbp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
V3Const::constifyParamsEdit(nodep->msbp()); // msbp may change
V3Const::constifyParamsEdit(nodep->lsbp()); // lsbp may change
AstConst* msbConstp = nodep->msbp()->castConst();
AstConst* lsbConstp = nodep->lsbp()->castConst();
if (!msbConstp || !lsbConstp) {
if (!msbConstp) nodep->v3error("MSB of bit range isn't a constant");
if (!lsbConstp) nodep->v3error("LSB of bit range isn't a constant");
nodep->width(1,1); return;
}
int msb = msbConstp->toSInt();
int lsb = lsbConstp->toSInt();
if (msb > (1<<28)) nodep->v3error("MSB of bit range is huge; vector of over 1billion bits: 0x"<<hex<<msb);
if (msb<lsb) {
// If it's a array, ok to have either ordering, we'll just correct
// So, see if we're sitting under a variable's arrayp.
AstNode* huntbackp = nodep;
while (huntbackp->backp()->castRange()) huntbackp=huntbackp->backp();
if (huntbackp->backp()->castVar()
&& huntbackp->backp()->castVar()->arraysp()==huntbackp) {
} else {
nodep->v3error("Unsupported: MSB < LSB of bit range: "<<msb<<"<"<<lsb);
}
// Correct it.
msbConstp->swapWith(lsbConstp);
int x=msb; msb=lsb; lsb=x;
}
int width = msb-lsb+1;
int width = nodep->elementsConst();
if (width > (1<<28)) nodep->v3error("Width of bit range is huge; vector of over 1billion bits: 0x"<<hex<<width);
nodep->width(width,width);
if (nodep->littleEndian()) {
nodep->v3warn(LITENDIAN,"Little bit endian vector: MSB < LSB of bit range: "<<nodep->lsbConst()<<":"<<nodep->msbConst());
}
}
}
virtual void visit(AstSel* nodep, AstNUser* vup) {
if (vup->c()->prelim()) {
nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p());
@ -383,6 +364,54 @@ private:
widthCheck(nodep,"Extract Range",nodep->bitp(),selwidth,selwidth,true);
}
}
virtual void visit(AstSelBit* nodep, AstNUser* vup) {
// Just a quick check as after V3Param these nodes instead are AstSel's
AstNode* selp = V3Width::widthSelNoIterEdit(nodep); if (selp!=nodep) { nodep=NULL; selp->iterate(*this,vup); return; }
nodep->v3fatalSrc("AstSelBit should disappear after widthSel");
if (vup->c()->prelim()) {
nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); // from
nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); // bit
}
}
virtual void visit(AstSelExtract* nodep, AstNUser* vup) {
// Just a quick check as after V3Param these nodes instead are AstSel's
AstNode* selp = V3Width::widthSelNoIterEdit(nodep); if (selp!=nodep) { nodep=NULL; selp->iterate(*this,vup); return; }
nodep->v3fatalSrc("AstSelExtract should disappear after widthSel");
if (vup->c()->prelim()) {
nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
nodep->msbp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
nodep->lsbp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
}
}
virtual void visit(AstSelPlus* nodep, AstNUser* vup) {
AstNode* selp = V3Width::widthSelNoIterEdit(nodep); if (selp!=nodep) { nodep=NULL; selp->iterate(*this,vup); return; }
nodep->v3fatalSrc("AstSelPlus should disappear after widthSel");
if (vup->c()->prelim()) {
nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
nodep->bitp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
nodep->widthp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
if (AstConst* constp = nodep->widthp()->castConst()) {
int width = constp->toSInt();
nodep->width(width,width);
}
}
}
virtual void visit(AstSelMinus* nodep, AstNUser* vup) {
AstNode* selp = V3Width::widthSelNoIterEdit(nodep); if (selp!=nodep) { nodep=NULL; selp->iterate(*this,vup); return; }
nodep->v3fatalSrc("AstSelMinus should disappear after widthSel");
if (vup->c()->prelim()) {
nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
nodep->bitp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
nodep->widthp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
if (AstConst* constp = nodep->widthp()->castConst()) {
int width = constp->toSInt();
nodep->width(width,width);
}
}
}
virtual void visit(AstExtend* nodep, AstNUser* vup) {
// Only created by this process, so we know width from here down it is correct.
}
@ -433,8 +462,8 @@ private:
int selwidth = V3Number::log2b(nodep->lhsp()->width())+1;
nodep->width(selwidth,selwidth);
}
}
virtual void visit(AstAttrOf* nodep, AstNUser*) {
}
virtual void visit(AstAttrOf* nodep, AstNUser*) {
nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
nodep->width(32,1); // Approximation, unsized 32
}
@ -526,8 +555,9 @@ private:
AstNodeCase* lastCasep = m_casep;
m_casep = nodep;
nodep->exprp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p());
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
itemp->iterate(*this,WidthVP(ANYSIZE,0,PRELIM).p());
for (AstCaseItem* nextp, *itemp = nodep->itemsp(); itemp; itemp=nextp) {
nextp = itemp->nextp()->castCaseItem(); // Prelim may cause the node to get replaced
itemp->iterate(*this,WidthVP(ANYSIZE,0,PRELIM).p()); itemp=NULL;
}
int width = nodep->exprp()->width();
int mwidth = nodep->exprp()->widthMin();
@ -552,8 +582,11 @@ private:
// Need to look across multiple case values for one set of statements
int width = nodep->condsp()->width();
int mwidth = nodep->condsp()->widthMin();
for (AstNode* nextp, *condp = nodep->condsp(); condp; condp=nextp) {
nextp = condp->nextp(); // Prelim may cause the node to get replaced
condp->iterate(*this,vup); condp=NULL;
}
for (AstNode* condp = nodep->condsp(); condp; condp=condp->nextp()) {
condp->iterate(*this,vup);
width = max(width,condp->width());
mwidth = max(mwidth,condp->widthMin());
if (vup->c()->final()) {
@ -804,19 +837,25 @@ private:
m_taskDepth--;
}
// And do the arguments to the task/function too
AstNode* pinp = nodep->pinsp();
AstNode* stmt_nextp; // List may change, so need to keep pointer
for (AstNode* stmtp = nodep->taskp()->stmtsp(); stmtp; stmtp=stmt_nextp) {
stmt_nextp = stmtp->nextp();
if (AstVar* portp = stmtp->castVar()) {
if (portp->isIO()
&& pinp!=NULL) { // Else argument error we'll find later
AstNode* pin_nextp = pinp->nextp(); // List may change, so remember nextp
int width = portp->width();
int ewidth = portp->widthMin();
pinp->accept(*this,WidthVP(width,ewidth,BOTH).p());
widthCheck(nodep,"Function Argument",pinp,width,ewidth);
pinp = pin_nextp;
for (int accept_mode=1; accept_mode>=0; accept_mode--) { // Avoid duplicate code; just do inner stuff twice
AstNode* pinp = nodep->pinsp();
for (AstNode* stmt_nextp, *stmtp = nodep->taskp()->stmtsp(); stmtp; stmtp=stmt_nextp) {
stmt_nextp = stmtp->nextp();
if (AstVar* portp = stmtp->castVar()) {
if (portp->isIO()
&& pinp!=NULL) { // Else argument error we'll find later
AstNode* pin_nextp = pinp->nextp(); // List may change, so remember nextp
if (accept_mode) {
// Prelim may cause the node to get replaced; we've lost our
// pointer, so need to iterate separately later
pinp->accept(*this,WidthVP(portp->width(),portp->widthMin(),PRELIM).p()); pinp=NULL;
} else {
// Do PRELIM again, because above accept may have exited early due to node replacement
pinp->accept(*this,WidthVP(portp->width(),portp->widthMin(),BOTH).p());
widthCheck(nodep,"Function Argument",pinp,portp->width(),portp->widthMin());
}
pinp = pin_nextp;
}
}
}
}
@ -1193,6 +1232,10 @@ private:
nodep->width(nodep->width(),nodep->width());
nodep->iterateChildren(*this);
}
virtual void visit(AstNodePreSel* nodep, AstNUser*) {
// This check could go anywhere after V3Param
nodep->v3fatalSrc("Presels should have been removed before this point");
}
public:
// CONSTUCTORS
WidthCommitVisitor(AstNetlist* nodep) {

View File

@ -37,6 +37,11 @@ public:
static AstNode* widthParamsEditIfNeed(AstNode* nodep);
// Final step... Mark all widths as equal
static void widthCommit(AstNetlist* nodep);
// For use only in WidthVisitor
// Replace AstSelBit, etc with AstSel/AstArraySel
// Returns replacement node if nodep was deleted, or null if none.
static AstNode* widthSelNoIterEdit(AstNode* nodep);
};
#endif // Guard

345
src/V3WidthSel.cpp Normal file
View File

@ -0,0 +1,345 @@
//*************************************************************************
// DESCRIPTION: Verilator: Expression width calculations
//
// Code available from: http://www.veripool.org/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2009 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.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3WidthSel's Transformations:
// Top down traversal:
// Replace SELPLUS/SELMINUS with SEL
// Replace SELEXTRACT with SEL
// Replace SELBIT with SEL or ARRAYSEL
//
// This code was once in V3LinkResolve, but little endian bit vectors won't
// work that early. It was considered for V3Width and V3Param, but is
// fairly ugly both places as the nodes change in too strongly
// interconnected ways.
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include <cstdio>
#include <cstdarg>
#include <unistd.h>
#include "V3Global.h"
#include "V3Width.h"
#include "V3Const.h"
//######################################################################
// Width state, as a visitor of each AstNode
class WidthSelVisitor : public AstNVisitor {
private:
// IMPORTANT
//**** This is not a normal visitor, in that all iteration is instead
// done by the caller (V3Width). This avoids duplicating much of the
// complicated GenCase/GenFor/Cell/Function call logic that all depends
// on if widthing top-down or just for parameters.
#define iterateChildren DO_NOT_iterateChildern_IN_V3WidthSel
//
// NODE STATE
// isBelowSeled() used insead of userp, as we're out of
// non-conflicting users, and having a persistent variable means this
// code can be skipped during most later stage constification calls.
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
void checkConstantOrReplace(AstNode* nodep, const string& message) {
// Note can't call V3Const::constifyParam(nodep) here, as constify may change nodep on us!
if (!nodep->castConst()) {
nodep->v3error(message);
nodep->replaceWith(new AstConst(nodep->fileline(), AstConst::Unsized32(), 1));
pushDeletep(nodep); nodep=NULL;
}
}
void selCheckDimension(AstNode* nodep, AstNode* basefromp, int dimension, bool rangedSelect) {
// Perform error checks on the node
AstVar* varp = varFromBasefrom(basefromp);
//UINFO(9,"SCD\n"); if (debug()>=9) nodep->backp()->dumpTree(cout,"-selcheck: ");
int dimensions = varp->arrayDimensions();
if (dimension < dimensions) {
if (rangedSelect) {
nodep->v3error("Illegal bit select; can't bit extract from arrayed dimension: "<<varp->prettyName());
}
} else if (dimension > dimensions) { // Too many indexes provided
nodep->v3error("Illegal bit or array select; variable already selected, or bad dimension: "<<varp->prettyName());
} else if (dimension == dimensions) { // Right number, but...
if (!varp->rangep()) {
nodep->v3error("Illegal bit select; variable does not have a bit range, or bad dimension: "<<varp->prettyName());
}
}
}
AstNode* newSubNeg(AstNode* lhsp, vlsint32_t rhs) {
// Return lhs-rhs, but if rhs is negative use an add, so we won't
// have to deal with signed math and related 32bit sign extension problems
if (rhs == 0) {
return lhsp;
} else if (rhs > 0) {
// We must make sure sub gets sign of original value
AstNode* newp = new AstSub(lhsp->fileline(), lhsp,
new AstConst(lhsp->fileline(), AstConst::Unsized32(), rhs));
newp->signedFrom(lhsp);
return newp;
} else { // rhs < 0;
AstNode* newp = new AstAdd(lhsp->fileline(), lhsp,
new AstConst(lhsp->fileline(), AstConst::Unsized32(), -rhs));
newp->signedFrom(lhsp);
return newp;
}
}
AstNode* newSubNeg(vlsint32_t lhs, AstNode* rhsp) {
// Return lhs-rhs
// We must make sure sub gets sign of original value
AstNode* newp = new AstSub(rhsp->fileline(),
new AstConst(rhsp->fileline(), AstConst::Unsized32(), lhs),
rhsp);
newp->signedFrom(rhsp); // Important as AstSub default is lhs's sign
return newp;
}
AstVar* varFromBasefrom(AstNode* 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 varp;
}
AstNode* newSubLsbOf(AstNode* underp, AstNode* basefromp) {
// Account for a variable's LSB in bit selections
// Will likely become SUB(underp, lsb_of_signal)
// Don't report WIDTH warnings etc here, as may be inside a generate branch that will be deleted
AstVar* varp = varFromBasefrom(basefromp);
// SUB #'s Not needed when LSB==0 and MSB>=0 (ie [0:-13] must still get added!)
if (!varp->rangep()) {
// vector without range is ok, for example a INTEGER x; y = x[21:0];
return underp;
} else {
if (!varp->rangep()->msbp()->castConst()
|| !varp->rangep()->lsbp()->castConst())
varp->v3fatalSrc("Non-constant variable range; errored earlier"); // in constifyParam(varp)
if (varp->littleEndian()) {
// reg [1:3] was swapped to [3:1] (lsbEndianedp==3) and needs a SUB(3,under)
AstNode* newp = newSubNeg(varp->msb(), underp);
return newp;
} else {
// reg [3:1] needs a SUB(under,1)
AstNode* newp = newSubNeg(underp, varp->lsb());
return newp;
}
}
}
// VISITORS
// If adding new visitors, insure V3Width's visit(TYPE) calls into here
virtual void visit(AstRange* nodep, AstNUser*) {
// Convert all range values to constants
UINFO(6,"RANGE "<<nodep<<endl);
V3Const::constifyParamsEdit(nodep->msbp()); // May relink pointed to node
V3Const::constifyParamsEdit(nodep->lsbp()); // May relink pointed to node
checkConstantOrReplace(nodep->msbp(), "MSB of bit range isn't a constant");
checkConstantOrReplace(nodep->lsbp(), "LSB of bit range isn't a constant");
int msb = nodep->msbConst();
int lsb = nodep->lsbConst();
if (msb<lsb) {
// If it's an array, ok to have either ordering, we'll just correct
// So, see if we're sitting under a variable's arrayp.
AstNode* huntbackp = nodep;
while (huntbackp->backp()->castRange()) huntbackp=huntbackp->backp();
if (huntbackp->backp()->castVar()
&& huntbackp->backp()->castVar()->arraysp()==huntbackp) {
} else {
// Little endian bits are legal, just remember to swap
// Warning is in V3Width to avoid false warnings when in "off" generate if's
nodep->littleEndian(!nodep->littleEndian());
}
// Internally we'll always have msb() be the greater number
// We only need to correct when doing [] AstSel extraction,
// and when tracing the vector.
nodep->msbp()->swapWith(nodep->lsbp());
}
}
virtual void visit(AstSelBit* nodep, AstNUser*) {
UINFO(6,"SELBIT "<<nodep<<endl);
if (debug()>=9) nodep->backp()->dumpTree(cout,"-vsbin(-1): ");
// lhsp/rhsp do not need to be constant
AstNode* basefromp = AstArraySel::baseFromp(nodep->attrp());
int dimension = AstArraySel::dimension(nodep->fromp()); // Not attrp as need hierarchy
selCheckDimension(nodep, basefromp, dimension, false);
AstNode* fromp = nodep->lhsp()->unlinkFrBack();
AstNode* bitp = nodep->rhsp()->unlinkFrBack();
AstVar* varp = varFromBasefrom(basefromp);
if (debug()>=9) nodep->dumpTree(cout,"-vsbmd: ");
if (varp->arrayp(dimension)) {
// SELBIT(array, index) -> ARRAYSEL(array, index)
AstRange* arrayp = varp->arrayp(dimension); // NULL checked above
AstNode* subp = bitp;
if (!arrayp->lsbp()->isZero() || arrayp->msbConst()<0) {
subp = newSubNeg (subp, arrayp->lsbConst());
}
AstArraySel* newp = new AstArraySel
(nodep->fileline(),
fromp,
subp);
UINFO(6," newd"<<dimension<<" "<<newp<<endl);
nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL;
}
else {
// SELBIT(range, index) -> SEL(array, index, 1)
AstSel* newp = new AstSel (nodep->fileline(),
fromp,
newSubLsbOf(bitp, 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;
}
}
virtual void visit(AstSelExtract* nodep, AstNUser*) {
// SELEXTRACT(from,msb,lsb) -> SEL(from, lsb, 1+msb-lsb)
UINFO(6,"SELEXTRACT "<<nodep<<endl);
//if (debug()>=9) nodep->dumpTree(cout,"--SLEX0: ");
// Below 2 lines may change nodep->widthp()
V3Const::constifyParamsEdit(nodep->lsbp()); // May relink pointed to node
V3Const::constifyParamsEdit(nodep->msbp()); // May relink pointed to node
//if (debug()>=9) nodep->dumpTree(cout,"--SLEX3: ");
checkConstantOrReplace(nodep->lsbp(), "First value of [a:b] isn't a constant, maybe you want +: or -:");
checkConstantOrReplace(nodep->msbp(), "Second value of [a:b] isn't a constant, maybe you want +: or -:");
AstNode* basefromp = AstArraySel::baseFromp(nodep->attrp());
int dimension = AstArraySel::dimension(nodep->fromp()); // Not attrp as need hierarchy
AstNode* fromp = nodep->lhsp()->unlinkFrBack();
AstNode* msbp = nodep->rhsp()->unlinkFrBack();
AstNode* lsbp = nodep->thsp()->unlinkFrBack();
AstVar* varp = varFromBasefrom(basefromp);
vlsint32_t msb = msbp->castConst()->toSInt();
vlsint32_t lsb = lsbp->castConst()->toSInt();
selCheckDimension(nodep, basefromp, dimension, msb!=lsb);
if (varp->rangep() && varp->rangep()->littleEndian()) {
// Below code assumes big bit endian; just works out if we swap
int x = msb; msb = lsb; lsb = x;
}
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;
pushDeletep(msbp); msbp=NULL;
}
void replaceSelPlusMinus(AstNodePreSel* nodep) {
UINFO(6,"SELPLUS/MINUS "<<nodep<<endl);
// Below 2 lines may change nodep->widthp()
V3Const::constifyParamsEdit(nodep->thsp()); // May relink pointed to node
checkConstantOrReplace(nodep->thsp(), "Width of :+ or :- bit extract isn't a constant");
// Now replace it with a AstSel
AstNode* basefromp = AstArraySel::baseFromp(nodep->attrp());
int dimension = AstArraySel::dimension(nodep->fromp()); // Not attrp as need hierarchy
AstNode* fromp = nodep->lhsp()->unlinkFrBack();
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
AstNode* widthp = nodep->thsp()->unlinkFrBack();
int width = widthp->castConst()->toSInt();
AstVar* varp = varFromBasefrom(basefromp);
if (width > (1<<28)) nodep->v3error("Width of :+ or :- is huge; vector of over 1billion bits: "<<widthp->prettyName());
if (width<0) nodep->v3error("Width of :+ or :- is < 0: "<<widthp->prettyName());
selCheckDimension(nodep, basefromp, dimension, width!=1);
AstSel* newp = NULL;
if (nodep->castSelPlus()) {
if (varp->rangep() && varp->rangep()->littleEndian()) {
// SELPLUS(from,lsb,width) -> SEL(from, (vector_msb-width+1)-sel, width)
newp = new AstSel (nodep->fileline(),
fromp,
newSubNeg((varp->msb()-width+1), rhsp),
widthp);
} else {
// SELPLUS(from,lsb,width) -> SEL(from, lsb-vector_lsb, width)
newp = new AstSel (nodep->fileline(),
fromp,
newSubLsbOf(rhsp, basefromp),
widthp);
}
} else if (nodep->castSelMinus()) {
if (varp->rangep() && varp->rangep()->littleEndian()) {
// SELMINUS(from,msb,width) -> SEL(from, msb-[bit])
newp = new AstSel (nodep->fileline(),
fromp,
newSubNeg(varp->msb(), rhsp),
widthp);
} else {
// SELMINUS(from,msb,width) -> SEL(from, msb-(width-1)-lsb#)
newp = new AstSel (nodep->fileline(),
fromp,
newSubNeg(rhsp, varp->lsb()+(width-1)),
widthp);
}
} else {
nodep->v3fatalSrc("Bad Case");
}
UINFO(6," new "<<newp<<endl);
nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL;
}
virtual void visit(AstSelPlus* nodep, AstNUser*) {
replaceSelPlusMinus(nodep);
}
virtual void visit(AstSelMinus* nodep, AstNUser*) {
replaceSelPlusMinus(nodep);
}
// If adding new visitors, insure V3Width's visit(TYPE) calls into here
//--------------------
// Default
virtual void visit(AstNode* nodep, AstNUser*) {
// See notes above; we never iterate
}
public:
// CONSTUCTORS
WidthSelVisitor() {}
AstNode* mainAcceptEdit(AstNode* nodep) {
return nodep->acceptSubtreeReturnEdits(*this);
}
virtual ~WidthSelVisitor() {}
};
//######################################################################
// Width class functions
AstNode* V3Width::widthSelNoIterEdit(AstNode* nodep) {
WidthSelVisitor visitor;
nodep = visitor.mainAcceptEdit(nodep);
return nodep;
}
#undef iterateChildren

View File

@ -11,11 +11,14 @@ compile (
fails=>$Self->{v3},
nc=>0, # Need to get it not to give the prompt
expect=>
'%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit select; variable already selected, or bad dimension
%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit select; variable already selected, or bad dimension
%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit select; variable already selected, or bad dimension
%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit select; variable already selected, or bad dimension
%Error: Exiting due to.*',
q{%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit select; variable does not have a bit range, or bad dimension: dimn
.*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit or array select; variable already selected, or bad dimension: dim0
.*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit or array select; variable already selected, or bad dimension: dim1
.*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit select; can't bit extract from arrayed dimension: dim2
.*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit select; can't bit extract from arrayed dimension: dim2
.*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit select; can't bit extract from arrayed dimension: dim0nv
.*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit select; variable does not have a bit range, or bad dimension: dim0nv
.*%Error: Exiting due to.*},
);
ok(1);

View File

@ -4,15 +4,19 @@
// without warranty, 2005 by Wilson Snyder.
module t (/*AUTOARG*/);
reg dimn;
reg [1:0] dim0;
reg [1:0] dim1 [1:0];
reg [1:0] dim2 [1:0][1:0];
reg dim0nv[1:0];
initial begin
dimn[1:0] = 0; // Bad: Not ranged
dim0[1][1] = 0; // Bad: Not arrayed
dim1[1][1][1] = 0; // Bad: Not arrayed to right depth
dim2[1][1][1] = 0; // OK
dim2[0 +: 1][1] = 0; // Bad: Range on non-bits
dim2[1 : 0][1] = 0; // Bad: Range on non-bits
dim2[1][1:0] = 0; // Bad: Bitsel too soon
dim0nv[1:0] = 0; // Bad: Not vectored
dim0nv[1][1] = 0; // Bad: Not arrayed to right depth

View File

@ -10,8 +10,8 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
compile (
fails=>1,
expect=>
'%Error: t/t_select_bad_msb.v:\d+: Unsupported: MSB < LSB of bit range: 0<22
%Error: t/t_select_bad_msb.v:\d+: Unsupported: MSB < LSB of bit extract: 1<4
'%Warning-LITENDIAN: t/t_select_bad_msb.v:\d+: Little bit endian vector: MSB < LSB of bit range: 0:22
.*
%Error: Exiting due to.*',
) if $Self->{v3};

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 (
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,74 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2009 by Wilson Snyder.
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc=0;
reg [63:0] crc;
reg [63:0] sum;
// verilator lint_off LITENDIAN
wire [10:41] sel2 = crc[31:0];
wire [10:100] sel3 = {crc[26:0],crc};
wire out20 = sel2[{1'b0,crc[3:0]} + 11];
wire [3:0] out21 = sel2[13 : 16];
wire [3:0] out22 = sel2[{1'b0,crc[3:0]} + 20 +: 4];
wire [3:0] out23 = sel2[{1'b0,crc[3:0]} + 20 -: 4];
wire out30 = sel3[{2'b0,crc[3:0]} + 11];
wire [3:0] out31 = sel3[13 : 16];
wire [3:0] out32 = sel3[crc[5:0] + 20 +: 4];
wire [3:0] out33 = sel3[crc[5:0] + 20 -: 4];
// Aggregate outputs into a single result vector
wire [63:0] result = {38'h0, out20, out21, out22, out23, out30, out31, out32, out33};
reg [19:50] sel1;
initial begin
// Path clearing
// 122333445
// 826048260
sel1 = 32'h12345678;
if (sel1 != 32'h12345678) $stop;
if (sel1[47 : 50] != 4'h8) $stop;
if (sel1[31 : 34] != 4'h4) $stop;
if (sel1[27 +: 4] != 4'h3) $stop; //==[27:30], in memory as [23:20]
if (sel1[26 -: 4] != 4'h2) $stop; //==[23:26], in memory as [27:24]
end
// Test loop
always @ (posedge clk) begin
`ifdef TEST_VERBOSE
$write("[%0t] sels=%x,%x,%x,%x %x,%x,%x,%x\n",$time, out20,out21,out22,out23, out30,out31,out32,out33);
$write("[%0t] cyc==%0d crc=%x result=%x\n",$time, cyc, crc, result);
`endif
cyc <= cyc + 1;
crc <= {crc[62:0], crc[63]^crc[2]^crc[0]};
sum <= result ^ {sum[62:0],sum[63]^sum[2]^sum[0]};
if (cyc==0) begin
// Setup
crc <= 64'h5aef0c8d_d70a4497;
end
else if (cyc<10) begin
sum <= 64'h0;
end
else if (cyc<90) begin
end
else if (cyc==99) begin
$write("[%0t] cyc==%0d crc=%x sum=%x\n",$time, cyc, crc, sum);
if (crc !== 64'hc77bb9b3784ea091) $stop;
`define EXPECTED_SUM 64'h28bf65439eb12c00
if (sum !== `EXPECTED_SUM) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -9,6 +9,7 @@ module t (
);
neg neg (.clk(CLK));
little little (.clk(CLK));
glbl glbl ();
initial RESET = 1'b1;
@ -43,5 +44,21 @@ module neg (
i48 <= ~i48;
i128 <= ~i128;
end
endmodule
module little (
input clk
);
// verilator lint_off LITENDIAN
reg [0:7] i8; initial i8 = '0;
reg [1:49] i48; initial i48 = '0;
reg [63:190] i128; initial i128 = '0;
// verilator lint_on LITENDIAN
always @ (posedge clk) begin
i8 <= ~i8;
i48 <= ~i48;
i128 <= ~i128;
end
endmodule

View File

@ -1,19 +1,25 @@
$version Generated by SpTraceVcd $end
$date Thu Sep 18 07:54:41 2008
$date Sat Sep 26 15:04:50 2009
$end
$timescale 1ns $end
$scope module TOP $end
$var wire 1 , CLK $end
$var wire 1 - RESET $end
$var wire 1 3 CLK $end
$var wire 1 4 RESET $end
$scope module v $end
$var wire 1 , CLK $end
$var wire 1 3 CLK $end
$var wire 1 # RESET $end
$scope module glbl $end
$var wire 1 + GSR $end
$var wire 1 2 GSR $end
$upscope $end
$scope module little $end
$var wire 1 3 clk $end
$var wire 128 . i128 [63:190] $end
$var wire 49 , i48 [1:49] $end
$var wire 8 + i8 [0:7] $end
$upscope $end
$scope module neg $end
$var wire 1 , clk $end
$var wire 1 3 clk $end
$var wire 128 ' i128 [63:-64] $end
$var wire 48 % i48 [-1:-48] $end
$var wire 8 $ i8 [0:-7] $end
@ -28,44 +34,56 @@ $enddefinitions $end
b00000000 $
b000000000000000000000000000000000000000000000000 %
b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 '
1+
1-
0,
b00000000 +
b0000000000000000000000000000000000000000000000000 ,
b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 .
12
14
03
#1
#2
#3
b11111111 $
b111111111111111111111111111111111111111111111111 %
b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 '
1,
b11111111 +
b1111111111111111111111111111111111111111111111111 ,
b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 .
13
#4
#5
#6
0,
03
#7
0+
02
#8
#9
0#
b00000000 $
b000000000000000000000000000000000000000000000000 %
b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 '
0-
1,
b00000000 +
b0000000000000000000000000000000000000000000000000 ,
b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 .
04
13
#10
#11
#12
0,
03
#13
#14
#15
b11111111 $
b111111111111111111111111111111111111111111111111 %
b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 '
1,
b11111111 +
b1111111111111111111111111111111111111111111111111 ,
b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 .
13
#16
#17
#18
0,
03
#19
#20

View File

@ -1,19 +1,25 @@
$version Generated by SpTraceVcd $end
$date Thu Sep 18 07:55:56 2008
$date Sat Sep 26 15:05:35 2009
$end
$timescale 1ns $end
$scope module TOP $end
$var wire 1 + CLK $end
$var wire 1 , RESET $end
$var wire 1 2 CLK $end
$var wire 1 3 RESET $end
$scope module v $end
$var wire 1 + CLK $end
$var wire 1 2 CLK $end
$var wire 1 # RESET $end
$scope module glbl $end
$var wire 1 - GSR $end
$var wire 1 4 GSR $end
$upscope $end
$scope module little $end
$var wire 1 2 clk $end
$var wire 128 . i128 [63:190] $end
$var wire 49 , i48 [1:49] $end
$var wire 8 + i8 [0:7] $end
$upscope $end
$scope module neg $end
$var wire 1 + clk $end
$var wire 1 2 clk $end
$var wire 128 ' i128 [63:-64] $end
$var wire 48 % i48 [-1:-48] $end
$var wire 8 $ i8 [0:-7] $end
@ -28,44 +34,56 @@ $enddefinitions $end
b00000000 $
b000000000000000000000000000000000000000000000000 %
b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 '
1,
0+
1-
b00000000 +
b0000000000000000000000000000000000000000000000000 ,
b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 .
13
02
14
#1
#2
#3
b11111111 $
b111111111111111111111111111111111111111111111111 %
b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 '
1+
b11111111 +
b1111111111111111111111111111111111111111111111111 ,
b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 .
12
#4
#5
#6
0+
02
#7
0-
04
#8
#9
0#
b00000000 $
b000000000000000000000000000000000000000000000000 %
b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 '
0,
1+
b00000000 +
b0000000000000000000000000000000000000000000000000 ,
b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 .
03
12
#10
#11
#12
0+
02
#13
#14
#15
b11111111 $
b111111111111111111111111111111111111111111111111 %
b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 '
1+
b11111111 +
b1111111111111111111111111111111111111111111111111 ,
b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 .
12
#16
#17
#18
0+
02
#19
#20