diff --git a/Changes b/Changes index 29dada666..6a1e3cb3a 100644 --- a/Changes +++ b/Changes @@ -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. diff --git a/bin/verilator b/bin/verilator index bbcbfa2ec..2fcf56127 100755 --- a/bin/verilator +++ b/bin/verilator @@ -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 diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index ed778ee64..716232a2c 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -204,6 +204,7 @@ RAW_OBJS = \ V3Unknown.o \ V3Unroll.o \ V3Width.o \ + V3WidthSel.o \ # Non-concatable OBJS += \ diff --git a/src/V3Ast.h b/src/V3Ast.h index 9aeb17341..c4ab329ce 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -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; } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 1fcf735b1..55506b642 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -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] => "; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 38c60dbc7..6c8e62622 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -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); }; diff --git a/src/V3Const.cpp b/src/V3Const.cpp index d4b015148..1aafa9253 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -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 diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 381ba0647..0ae657717 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -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"); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 046e120e0..7671d0152 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -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("["); diff --git a/src/V3Error.h b/src/V3Error.h index cbac1dbb5..0b3e7a596 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -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); } diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 86547a7a3..e8ef4778c 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -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*) { diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 8ff43951e..1b5d83ca0 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -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 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: "<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! diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index f84c0c389..67b4a90e6 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -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); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 9ad20c63d..492964b7b 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -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"<backp()->castRange()) huntbackp=huntbackp->backp(); - if (huntbackp->backp()->castVar() - && huntbackp->backp()->castVar()->arraysp()==huntbackp) { - } else { - nodep->v3error("Unsupported: MSB < LSB of bit range: "<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"<width(width,width); + if (nodep->littleEndian()) { + nodep->v3warn(LITENDIAN,"Little bit endian vector: MSB < LSB of bit range: "<lsbConst()<<":"<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) { diff --git a/src/V3Width.h b/src/V3Width.h index 94cdee9ce..837eb4e2e 100644 --- a/src/V3Width.h +++ b/src/V3Width.h @@ -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 diff --git a/src/V3WidthSel.cpp b/src/V3WidthSel.cpp new file mode 100644 index 000000000..a1be62bce --- /dev/null +++ b/src/V3WidthSel.cpp @@ -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 +#include +#include + +#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: "<prettyName()); + } + } else if (dimension > dimensions) { // Too many indexes provided + nodep->v3error("Illegal bit or array select; variable already selected, or bad dimension: "<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: "<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 "<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 (msbbackp()->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 "<=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"<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 "<=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 "<=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("["<fileline(), AstConst::Unsized32(), // Unsized so width from user + msb +1-lsb); + AstSel* newp = new AstSel (nodep->fileline(), + fromp, + newSubLsbOf(lsbp, basefromp), + widthp); + UINFO(6," new "<=9) newp->dumpTree(cout,"--SLEXnew: "); + nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL; + pushDeletep(msbp); msbp=NULL; + } + + void replaceSelPlusMinus(AstNodePreSel* nodep) { + UINFO(6,"SELPLUS/MINUS "<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: "<prettyName()); + if (width<0) nodep->v3error("Width of :+ or :- is < 0: "<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 "<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 diff --git a/test_regress/t/t_mem_multi_ref_bad.pl b/test_regress/t/t_mem_multi_ref_bad.pl index 43cba0c58..b33f9d979 100755 --- a/test_regress/t/t_mem_multi_ref_bad.pl +++ b/test_regress/t/t_mem_multi_ref_bad.pl @@ -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); diff --git a/test_regress/t/t_mem_multi_ref_bad.v b/test_regress/t/t_mem_multi_ref_bad.v index 389aefe2f..9533f97c5 100644 --- a/test_regress/t/t_mem_multi_ref_bad.v +++ b/test_regress/t/t_mem_multi_ref_bad.v @@ -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 diff --git a/test_regress/t/t_select_bad_msb.pl b/test_regress/t/t_select_bad_msb.pl index 28e3e82ec..c4162871d 100755 --- a/test_regress/t/t_select_bad_msb.pl +++ b/test_regress/t/t_select_bad_msb.pl @@ -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}; diff --git a/test_regress/t/t_select_little.pl b/test_regress/t/t_select_little.pl new file mode 100755 index 000000000..7058e622f --- /dev/null +++ b/test_regress/t/t_select_little.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_select_little.v b/test_regress/t/t_select_little.v new file mode 100644 index 000000000..8840fcbe1 --- /dev/null +++ b/test_regress/t/t_select_little.v @@ -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 diff --git a/test_regress/t/t_trace_public.v b/test_regress/t/t_trace_public.v index d802ad91f..07c5f324f 100644 --- a/test_regress/t/t_trace_public.v +++ b/test_regress/t/t_trace_public.v @@ -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 diff --git a/test_regress/t/t_trace_public_func.out b/test_regress/t/t_trace_public_func.out index 39f95e8db..d79b1693a 100644 --- a/test_regress/t/t_trace_public_func.out +++ b/test_regress/t/t_trace_public_func.out @@ -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 diff --git a/test_regress/t/t_trace_public_sig.out b/test_regress/t/t_trace_public_sig.out index 3b29842cb..f86635f1c 100644 --- a/test_regress/t/t_trace_public_sig.out +++ b/test_regress/t/t_trace_public_sig.out @@ -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