diff --git a/Changes b/Changes index 206d397d3..8f39ef14f 100644 --- a/Changes +++ b/Changes @@ -5,8 +5,14 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.814**** +*** Support 'const' variables in limited cases; similar to enums. [Alex Solomatnikov] + *** Support disable for loop escapes. +*** Support $fopen and I/O with integer instead of `verilator_file_descriptor. + +**** Use 'vluint64_t' for SystemC instead of (same sized) 'uint64' for MSVC++. + * Verilator 3.813 2011/06/28 *** Support bit vectors > 64 bits wide in DPI import and exports. diff --git a/bin/verilator b/bin/verilator index 8f9598f7c..27c0df5de 100755 --- a/bin/verilator +++ b/bin/verilator @@ -268,7 +268,7 @@ descriptions in the next sections for more information. --MP Create phony dependency targets --Mdir Name of output object directory --mod-prefix Name to prepend to lower classes - --no-pins64 Don't use uint64_t's for 33-64 bit sigs + --no-pins64 Don't use vluint64_t's for 33-64 bit sigs --no-skip-identical Disable skipping identical output +notimingchecks Ignored -O0 Disable optimizations @@ -713,7 +713,7 @@ Backward compatible alias for "--pins-bv 65". Note that's a 65, not a 64. =item --pins-bv I Specifies SystemC inputs/outputs of greater than or equal to I bits -wide should use sc_bv's instead of uint32/uint64_t's. The default is +wide should use sc_bv's instead of uint32/vluint64_t's. The default is "--pins-bv 65". Versions before Verilator 3.671 defaulted to "--pins-bv 33". The more sc_bv is used, the worse for performance. @@ -1352,7 +1352,7 @@ will plug directly into a SystemC netlist. The SC_MODULE gets the same pinout as the Verilog module, with the following type conversions: Pins of a single bit become bool. Pins 2-32 bits wide become uint32_t's. Pins 33-64 bits wide become sc_bv's or -uint64_t's depending on the --no-pins64 switch. Wider pins become sc_bv's. +vluint64_t's depending on the --no-pins64 switch. Wider pins become sc_bv's. (Uints simulate the fastest so are used where possible.) Lower modules are not pure SystemC code. This is a feature, as using the @@ -1661,7 +1661,7 @@ please file a bug if a feature you need is missing. Verilator supports ==? and !=? operators, ++ and -- in some contexts, $bits, $countones, $error, $fatal, $info, $isunknown, $onehot, $onehot0, $unit, $warning, always_comb, always_ff, always_latch, bit, byte, chandle, -do-while, enum, export, final, import, int, logic, longint, package, +const, do-while, enum, export, final, import, int, logic, longint, package, program, shortint, time, typedef, var, void, priority case/if, and unique case/if. @@ -1711,8 +1711,8 @@ assertion clocks. =head2 Synthesis Directive Assertion Support With the --assert switch, Verilator reads any "//synopsys full_case" or "// -synopsys parallel_case" directives. The same applies to any "//cadence" or -"// ambit synthesis" directives of the same form. +synopsys parallel_case" directives. The same applies to any "// ambit +synthesis", "//cadence" or "//pragma" directives of the same form. When these synthesis directives are discovered, Verilator will either formally prove the directive to be true, or failing that, will insert the @@ -2261,7 +2261,7 @@ specified as illegal on chandles. =item disable Disable statements may be used only if the block being disabled is a block -the disable statement itself is inside. This is commonly used to provide +the disable statement itself is inside. This was commonly used to provide loop break and continue functionality before SystemVerilog added the break and continue keywords. @@ -2313,11 +2313,6 @@ The rarely used optional parameter to $finish and $stop is ignored. File descriptors passed to the file PLI calls must be file descriptors, not MCDs, which includes the mode parameter to $fopen being mandatory. -Verilator will convert the integer used to hold the file descriptor into a -internal FILE*. To prevent core dumps due to mis-use, and because integers -are 32 bits while FILE*s may be 64 bits, the descriptor must be stored in a -reg [63:0] rather than an integer. The define `verilator_file_descriptor in -verilated.v can be used to hide this difference. =item $fscanf, $sscanf @@ -3182,7 +3177,7 @@ as you would any other member variable. Signals are the smallest of 8 bit chars, 16 bit shorts, 32 bit longs, or 64 bit long longs that fits the width of the signal. Generally, you can use -just uint32_t's for 1 to 32 bits, or uint64_t for 1 to 64 bits, and the +just uint32_t's for 1 to 32 bits, or vluint64_t for 1 to 64 bits, and the compiler will properly up-convert smaller entities. Signals wider than 64 bits are stored as an array of 32-bit uint32_t's. diff --git a/include/verilated.cpp b/include/verilated.cpp index 75948558b..d85b5598e 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -26,7 +26,6 @@ #define _VERILATED_CPP_ #include "verilated_imp.h" #include -#include #define VL_VALUE_STRING_MAX_WIDTH 1024 ///< Max static char array for VL_VALUE_STRING @@ -628,6 +627,10 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf //=========================================================================== // File I/O +FILE* VL_CVT_I_FP(IData lhs) { + return VerilatedImp::fdToFp(lhs); +} + void _VL_VINT_TO_STRING(int obits, char* destoutp, WDataInP sourcep) { // See also VL_DATA_TO_STRING_NW int lsb=obits-1; @@ -655,8 +658,8 @@ void _VL_STRING_TO_VINT(int obits, void* destp, int srclen, const char* srcp) { for (; i #include +#include #include class VerilatedScope; @@ -62,9 +63,18 @@ class VerilatedImp { ExportNameMap m_exportMap; ///< Map of int m_exportNext; ///< Next export funcnum + // File I/O + vector m_fdps; ///< File descriptors + deque m_fdFree; ///< List of free descriptors (SLOW - FOPEN/CLOSE only) + public: // But only for verilated*.cpp // CONSTRUCTORS - VerilatedImp() : m_argVecLoaded(false), m_exportNext(0) {} + VerilatedImp() : m_argVecLoaded(false), m_exportNext(0) { + m_fdps.resize(3); + m_fdps[0] = stdin; + m_fdps[1] = stdout; + m_fdps[2] = stderr; + } ~VerilatedImp() {} // METHODS - arguments @@ -184,6 +194,34 @@ public: // But only for verilated*.cpp } // We don't free up m_exportMap until the end, because we can't be sure // what other models are using the assigned funcnum's. + +public: // But only for verilated*.cpp + // METHODS - file IO + static IData fdNew(FILE* fp) { + if (VL_UNLIKELY(!fp)) return 0; + // Bit 31 indicates it's a descriptor not a MCD + if (s_s.m_fdFree.empty()) { + // Need to create more space in m_fdps and m_fdFree + size_t start = s_s.m_fdps.size(); + s_s.m_fdps.resize(start*2); + for (size_t i=start; i= s_s.m_fdps.size())) return; + if (VL_UNLIKELY(!s_s.m_fdps[idx])) return; // Already free + s_s.m_fdps[idx] = NULL; + s_s.m_fdFree.push_back(idx); + } + static inline FILE* fdToFp(IData fdi) { + IData idx = VL_MASK_I(31) & fdi; + if (VL_UNLIKELY(!(fdi & (1ULL<<31)) || idx >= s_s.m_fdps.size())) return NULL; + return s_s.m_fdps[idx]; + } }; #endif // Guard diff --git a/nodist/vtree_importer b/nodist/vtree_importer index 0ecf7a447..4905ef646 100755 --- a/nodist/vtree_importer +++ b/nodist/vtree_importer @@ -256,7 +256,7 @@ sub gentree { 'TRACE' => sub { p "TRACE what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; }, 'UCFUNC' => sub { p '$c(';p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p ")"; }, 'UCSTMT' => sub { p '$c(';p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p ");";nl; }, - 'UNARYMIN' => sub { p " -";p1; }, + 'NEGATE' => sub { p " -";p1; }, 'VAR' => sub { p_var(); }, 'VARPIN' => sub { p "VARPIN what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; }, 'VARREF' => sub { a1; }, diff --git a/src/V3Ast.h b/src/V3Ast.h index 7a10bc232..9dd499dfd 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -209,7 +209,7 @@ class AstBasicDTypeKwd { public: enum en { BIT, BYTE, CHANDLE, INT, INTEGER, LOGIC, LONGINT, - REAL, REALTIME, SHORTINT, SHORTREAL, TIME, + REAL, SHORTINT, SHORTREAL, TIME, // Closer to a class type, but limited usage STRING, // Internal types for mid-steps @@ -221,7 +221,7 @@ public: const char* ascii() const { static const char* names[] = { "bit", "byte", "chandle", "int", "integer", "logic", "longint", - "real", "realtime", "shortint", "shortreal", "time", + "real", "shortint", "shortreal", "time", "string", "VerilatedScope*", "char*", "LOGIC_IMPLICIT" @@ -231,7 +231,7 @@ public: const char* dpiType() const { static const char* names[] = { "unsigned char", "char", "void*", "int", "int", "svLogic", "long long", - "double", "double", "short int", "float", "long long", + "double", "short int", "float", "long long", "const char*", "dpiScope", "const char*", "" @@ -251,6 +251,8 @@ public: case INTEGER: return 32; case LOGIC: return 1; case LONGINT: return 64; + case REAL: return 64; + case SHORTREAL: return 32; case SHORTINT: return 16; case TIME: return 64; case STRING: return 64; // Just the pointer, for today @@ -265,7 +267,7 @@ public: } bool isZeroInit() const { // Otherwise initializes to X return (m_e==BIT || m_e==BYTE || m_e==CHANDLE || m_e==INT || m_e==LONGINT || m_e==SHORTINT - || m_e==STRING); + || m_e==STRING || m_e==REAL || m_e==SHORTREAL); } bool isSloppy() const { // Don't be as anal about width warnings return !(m_e==LOGIC || m_e==BIT); @@ -274,10 +276,13 @@ public: return (m_e==LOGIC || m_e==BIT); } bool isDpiUnsupported() const { - return (m_e==LOGIC || m_e==TIME || m_e==REALTIME); + return (m_e==LOGIC || m_e==TIME); } bool isOpaque() const { // IE not a simple number we can bit optimize - return (m_e==STRING || m_e==SCOPEPTR || m_e==CHARPTR); + return (m_e==STRING || m_e==SCOPEPTR || m_e==CHARPTR || m_e==REAL || m_e==SHORTREAL); + } + bool isReal() const { + return (m_e==REAL); } }; inline bool operator== (AstBasicDTypeKwd lhs, AstBasicDTypeKwd rhs) { return (lhs.m_e == rhs.m_e); } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 9c6146aa1..3a133e3d6 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -113,6 +113,10 @@ string AstVar::vlArgType(bool named, bool forReturn) const { arg += "const char*"; } else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::SCOPEPTR) { arg += "const VerilatedScope*"; + } else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::REAL) { + arg += "double"; + } else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::SHORTREAL) { + arg += "float"; } else if (strtype) { if (isInOnly()) arg += "const "; arg += "string"; @@ -185,7 +189,7 @@ string AstVar::cPubArgType(bool named, bool forReturn) const { } else if (isWide()) { arg += "uint32_t"; // []'s added later } else { - arg += "uint64_t"; + arg += "vluint64_t"; } if (isWide()) { if (forReturn) v3error("Unsupported: Public functions with >64 bit outputs; make an output of a public task instead"); @@ -237,7 +241,7 @@ string AstVar::scType() const { return "uint32_t"; } } else { - return "uint64_t"; + return "vluint64_t"; } } @@ -608,7 +612,8 @@ void AstVar::dump(ostream& str) { else if (isInput()) str<<" [I]"; else if (isOutput()) str<<" [O]"; } - if (isUsedClock()) str<<" [C]"; + if (isConst()) str<<" [CONST]"; + if (isUsedClock()) str<<" [CLK]"; if (isSigPublic()) str<<" [P]"; if (isUsedLoopIdx()) str<<" [LOOP]"; if (attrClockEn()) str<<" [aCLKEN]"; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 479dce113..8b384e0c0 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -300,6 +300,25 @@ public: void implicit(bool flag) { m_implicit = flag; } }; +struct AstConstDType : public AstNodeDType { + // const data type, ie "const some_dtype var_name [2:0]" + // ConstDType are removed in V3LinkLValue and become AstVar::isConst. + // When more generic types are supported AstConstDType will be propagated further. + AstConstDType(FileLine* fl, AstNodeDType* dtypep) + : AstNodeDType(fl) { + setOp1p(dtypep); + widthSignedFrom(dtypep); + } + ASTNODE_NODE_FUNCS(ConstDType, CONSTDTYPE) + AstNodeDType* dtypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable + void dtypep(AstNodeDType* nodep) { setOp1p(nodep); } + // METHODS + virtual AstBasicDType* basicp() const { return dtypep()->basicp(); } // (Slow) recurse down to find basic data type + virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } + virtual int widthAlignBytes() const { return dtypep()->widthAlignBytes(); } + virtual int widthTotalBytes() const { return dtypep()->widthTotalBytes(); } +}; + struct AstRefDType : public AstNodeDType { private: AstTypedef* m_defp; @@ -677,7 +696,7 @@ public: bool isToggleCoverable() const { return ((isIO() || isSignal()) && (isIO() || isBitLogic()) // Wrapper would otherwise duplicate wrapped module's coverage - && !isSc() && !isPrimaryIO()); } + && !isSc() && !isPrimaryIO() && !isConst()); } bool isStatementTemp() const { return (varType()==AstVarType::STMTTEMP); } bool isMovableToBlock() const { return (varType()==AstVarType::BLOCKTEMP || isFuncLocal()); } bool isPure() const { return (varType()==AstVarType::XTEMP); } @@ -2453,13 +2472,13 @@ struct AstUCFunc : public AstNodeMath { //====================================================================== // Unary ops -struct AstUnaryMin : public AstNodeUniop { - AstUnaryMin(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { +struct AstNegate : public AstNodeUniop { + AstNegate(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { if (lhsp) widthSignedFrom(lhsp); } - ASTNODE_NODE_FUNCS(UnaryMin, UNARYMIN) - virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opUnaryMin(lhs); } + ASTNODE_NODE_FUNCS(Negate, NEGATE) + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opNegate(lhs); } virtual string emitVerilog() { return "%f(- %l)"; } - virtual string emitC() { return "VL_UNARYMIN_%lq(%lW, %P, %li)"; } + virtual string emitC() { return "VL_NEGATE_%lq(%lW, %P, %li)"; } virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} virtual bool sizeMattersLhs() {return true;} }; @@ -2697,7 +2716,7 @@ struct AstFEof : public AstNodeUniop { ASTNODE_NODE_FUNCS(FEof, FEOF) virtual void numberOperate(V3Number& out, const V3Number& lhs) { V3ERROR_NA; } virtual string emitVerilog() { return "%f$feof(%l)"; } - virtual string emitC() { return "(%li ? feof(VL_CVT_Q_FP(%li)) : true)"; } + virtual string emitC() { return "(%li ? feof(VL_CVT_I_FP(%li)) : true)"; } virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} virtual bool sizeMattersLhs() {return false;} virtual int instrCount() const { return widthInstrs()*16; } @@ -2710,7 +2729,7 @@ struct AstFGetC : public AstNodeUniop { virtual void numberOperate(V3Number& out, const V3Number& lhs) { V3ERROR_NA; } virtual string emitVerilog() { return "%f$fgetc(%l)"; } // Non-existent filehandle returns EOF - virtual string emitC() { return "(%li ? fgetc(VL_CVT_Q_FP(%li)) : -1)"; } + virtual string emitC() { return "(%li ? fgetc(VL_CVT_I_FP(%li)) : -1)"; } virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return true;} virtual bool sizeMattersLhs() {return false;} virtual int instrCount() const { return widthInstrs()*64; } diff --git a/src/V3Cast.cpp b/src/V3Cast.cpp index 40e1b032a..4be7091d4 100644 --- a/src/V3Cast.cpp +++ b/src/V3Cast.cpp @@ -135,7 +135,7 @@ private: insureLower32Cast(nodep); nodep->user1(1); } - virtual void visit(AstUnaryMin* nodep, AstNUser*) { + virtual void visit(AstNegate* nodep, AstNUser*) { nodep->iterateChildren(*this); nodep->user1(nodep->lhsp()->user1()); if (nodep->lhsp()->widthMin()==1) { diff --git a/src/V3Const.cpp b/src/V3Const.cpp index d160d3857..b43b8bc99 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -1519,7 +1519,7 @@ private: if (!inPct && ch=='%') { inPct = true; fmt = ch; - } else if (inPct && isdigit(ch)) { + } else if (inPct && (isdigit(ch) || ch=='.')) { fmt += ch; } else if (inPct) { inPct = false; @@ -1664,7 +1664,7 @@ private: TREEOP ("AstShiftRS{$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); TREEOP ("AstXor {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); TREEOP ("AstXnor {$lhsp.isZero, $rhsp}", "AstNot{$rhsp}"); - TREEOP ("AstSub {$lhsp.isZero, $rhsp}", "AstUnaryMin{$rhsp}"); + TREEOP ("AstSub {$lhsp.isZero, $rhsp}", "AstNegate{$rhsp}"); TREEOP ("AstAdd {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); TREEOP ("AstAnd {$lhsp, $rhsp.isZero}", "replaceZero(nodep)"); TREEOP ("AstLogAnd{$lhsp, $rhsp.isZero}", "replaceZero(nodep)"); diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 96761b907..5d164fecf 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -344,13 +344,11 @@ public: puts(");\n"); } virtual void visit(AstFClose* nodep, AstNUser*) { - puts("if ("); + puts("VL_FCLOSE_I("); nodep->filep()->iterateAndNext(*this); - puts(") { fclose (VL_CVT_Q_FP("); - nodep->filep()->iterateAndNext(*this); - puts(")); "); + puts("); "); nodep->filep()->iterateAndNext(*this); // For saftey, so user doesn't later WRITE with it. - puts("=0; }\n"); + puts("=0;\n"); } virtual void visit(AstFFlush* nodep, AstNUser*) { if (!nodep->filep()) { @@ -358,7 +356,7 @@ public: } else { puts("if ("); nodep->filep()->iterateAndNext(*this); - puts(") { fflush (VL_CVT_Q_FP("); + puts(") { fflush (VL_CVT_I_FP("); nodep->filep()->iterateAndNext(*this); puts(")); }\n"); } diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index e17b563a5..2b1be6293 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -483,6 +483,10 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { putfs(nodep,nodep->prettyName()); if (nodep->rangep()) { puts(" "); nodep->rangep()->iterateAndNext(*this); puts(" "); } } + virtual void visit(AstConstDType* nodep, AstNUser*) { + putfs(nodep,"const "); + nodep->dtypep()->iterateAndNext(*this); + } virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { if (nodep->dotted()!="") { putfs(nodep,nodep->dotted()); puts("."); puts(nodep->prettyName()); } else { putfs(nodep,nodep->prettyName()); } diff --git a/src/V3Expand.cpp b/src/V3Expand.cpp index 75110d5c0..b27152ca2 100644 --- a/src/V3Expand.cpp +++ b/src/V3Expand.cpp @@ -696,8 +696,7 @@ private: int lhswidth = lhsp->widthMin(); if (lhswidth==1) { UINFO(8," REPLICATE(w1) "<fileline(), - lhsp); + newp = new AstNegate (nodep->fileline(), lhsp); } else { UINFO(8," REPLICATE "<rhsp()->castConst(); @@ -731,7 +730,7 @@ private: for (int w=0; wwidthWords(); w++) { AstNode* newp; if (lhswidth==1) { - newp = new AstUnaryMin (nodep->fileline(), lhsp->cloneTree(true)); + newp = new AstNegate (nodep->fileline(), lhsp->cloneTree(true)); newp->width(VL_WORDSIZE,VL_WORDSIZE); } else { newp = newAstWordSelClone (lhsp, w); diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index 7bdce5e5d..747050d47 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -45,6 +45,7 @@ private: // STATE bool m_setRefLvalue; // Set VarRefs to lvalues for pin assignments + AstInitial* m_initialp; // In an initial block AstNodeFTask* m_ftaskp; // Function or task we're inside // METHODS @@ -67,6 +68,10 @@ private: nodep->v3error("Assigning to input variable: "<prettyName()); } } + if (nodep->lvalue() && nodep->varp()->isConst() + && !m_initialp) { // Too loose, but need to allow our generated first assignment + nodep->v3error("Assigning to const variable: "<prettyName()); + } } nodep->iterateChildren(*this); } @@ -222,6 +227,11 @@ private: } m_setRefLvalue = last_setRefLvalue; } + virtual void visit(AstInitial* nodep, AstNUser*) { + m_initialp = nodep; + nodep->iterateChildren(*this); + m_initialp = NULL; + } virtual void visit(AstNodeFTask* nodep, AstNUser*) { m_ftaskp = nodep; nodep->iterateChildren(*this); @@ -259,6 +269,7 @@ public: LinkLValueVisitor(AstNode* nodep, bool start) { m_setRefLvalue = start; m_ftaskp = NULL; + m_initialp = NULL; nodep->accept(*this); } virtual ~LinkLValueVisitor() {} diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index e4d2d6bb6..608b56875 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -95,6 +95,9 @@ private: nodep->dtypeSkipRefp()->castArrayDType())) { nodep->v3error("Unsupported: Inputs and outputs must be simple data types"); } + if (nodep->dtypeSkipRefp()->castConstDType()) { + nodep->isConst(true); + } if (m_ftaskp) nodep->funcLocal(true); if (nodep->isSigModPublic()) { nodep->sigModPublic(false); // We're done with this attribute diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 83f5de8e4..3ae923851 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -1079,12 +1079,12 @@ V3Number& V3Number::opAbsS (const V3Number& lhs) { // op i, L(lhs) bit return if (lhs.isFourState()) return setAllBitsX(); if (lhs.isNegative()) { - return opUnaryMin(lhs); + return opNegate(lhs); } else { return opAssign(lhs); } } -V3Number& V3Number::opUnaryMin (const V3Number& lhs) { +V3Number& V3Number::opNegate (const V3Number& lhs) { // op i, L(lhs) bit return if (lhs.isFourState()) return setAllBitsX(); V3Number notlhs (lhs.m_fileline, width()); @@ -1112,7 +1112,7 @@ V3Number& V3Number::opSub (const V3Number& lhs, const V3Number& rhs) { // i op j, max(L(lhs),L(rhs)) bit return, if any 4-state, 4-state return if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX(); V3Number negrhs (rhs.m_fileline, rhs.width()); - negrhs.opUnaryMin(rhs); + negrhs.opNegate(rhs); return opAdd(lhs, negrhs); } V3Number& V3Number::opMul (const V3Number& lhs, const V3Number& rhs) { @@ -1140,12 +1140,12 @@ V3Number& V3Number::opMul (const V3Number& lhs, const V3Number& rhs) { V3Number& V3Number::opMulS (const V3Number& lhs, const V3Number& rhs) { // Signed multiply if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX(); - V3Number lhsNoSign = lhs; if (lhs.isNegative()) lhsNoSign.opUnaryMin(lhs); - V3Number rhsNoSign = rhs; if (rhs.isNegative()) rhsNoSign.opUnaryMin(rhs); + V3Number lhsNoSign = lhs; if (lhs.isNegative()) lhsNoSign.opNegate(lhs); + V3Number rhsNoSign = rhs; if (rhs.isNegative()) rhsNoSign.opNegate(rhs); V3Number qNoSign = opMul(lhsNoSign,rhsNoSign); if ((lhs.isNegative() && !rhs.isNegative()) || (!lhs.isNegative() && rhs.isNegative())) { - opUnaryMin(qNoSign); + opNegate(qNoSign); } else { opAssign(qNoSign); } @@ -1169,13 +1169,13 @@ V3Number& V3Number::opDivS (const V3Number& lhs, const V3Number& rhs) { //UINFO(9, ">>divs-start "<divs-mid "<fileline(), - new AstShiftR(nodep->fileline(), - nodep->lhsp()->cloneTree(false), - new AstConst(nodep->fileline(), - nodep->widthMin()-1), - nodep->width())); + constzerop = new AstNegate (nodep->fileline(), + new AstShiftR(nodep->fileline(), + nodep->lhsp()->cloneTree(false), + new AstConst(nodep->fileline(), + nodep->widthMin()-1), + nodep->width())); } else { V3Number zeronum (nodep->fileline(), nodep->width(), 0); constzerop = new AstConst(nodep->fileline(), zeronum); diff --git a/src/V3Signed.cpp b/src/V3Signed.cpp index 51469ae40..c56d03525 100644 --- a/src/V3Signed.cpp +++ b/src/V3Signed.cpp @@ -48,7 +48,6 @@ class SignedVisitor : public AstNVisitor { private: // NODE STATE/TYPES // STATE - int m_taskDepth; // Recursion check bool m_paramsOnly; // Computing parameter value; limit operation // METHODS - special type detection @@ -62,8 +61,8 @@ private: // VISITORS //======== // Signed: Output explicit by user, Lhs either - virtual void visit(AstSigned* nodep, AstNUser*) { signed_Os_Ix(nodep); } virtual void visit(AstUnsigned* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstSigned* nodep, AstNUser*) { signed_Os_Ix(nodep); } //======== // Signed: Output unsigned, Operands either @@ -121,7 +120,7 @@ private: //======= // Signed: Output signed iff LHS signed; unary operator virtual void visit(AstNot* nodep, AstNUser*) { signed_Olhs(nodep); } - virtual void visit(AstUnaryMin* nodep, AstNUser*) { signed_Olhs(nodep); } + virtual void visit(AstNegate* nodep, AstNUser*) { signed_Olhs(nodep); } virtual void visit(AstShiftL* nodep, AstNUser*) { signed_Olhs(nodep); } virtual void visit(AstShiftR* nodep, AstNUser*) { signed_Olhs(nodep); } @@ -291,6 +290,18 @@ private: // ShiftRS converts to ShiftR, but not vice-versa virtual void visit(AstShiftRS* nodep, AstNUser*) { checkReplace_Olhs(nodep); } + // VISITORS - defaults + virtual void visit(AstNodeMath* nodep, AstNUser*) { + nodep->v3fatalSrc("Visit function missing? Signedness unknown for this node: "<iterateChildren(*this); + } + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + + //======= + // Lower level functions + void checkReplace_Ou_FlavLhsAndRhs(AstNodeBiop* nodep) { // For compares, the output of the comparison is unsigned. // However, we need the appropriate type of compare selected by RHS & LHS @@ -336,15 +347,6 @@ private: } } - // VISITORS - defaults - virtual void visit(AstNodeMath* nodep, AstNUser*) { - nodep->v3fatalSrc("Visit function missing? Signedness unknown for this node: "<iterateChildren(*this); - } - virtual void visit(AstNode* nodep, AstNUser*) { - nodep->iterateChildren(*this); - } - // COMMON SCHEMES // Signed: Output signed, Lhs/Rhs/etc either void signed_Os_Ix(AstNode* nodep) { @@ -392,7 +394,6 @@ public: // CONSTRUCTORS SignedVisitor(bool paramsOnly) { m_paramsOnly = paramsOnly; - m_taskDepth = 0; } virtual ~SignedVisitor() {} AstNode* mainAcceptEdit(AstNode* nodep) { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index b0ab5a2b9..864e94787 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -57,6 +57,10 @@ #include "V3Const.h" #include "V3Task.h" +// More code; this file was getting too large; see actions there +#define _V3WIDTH_CPP_ +#include "V3WidthCommit.h" + //###################################################################### // Width state, as a visitor of each AstNode @@ -99,19 +103,16 @@ private: // VISITORS // Naming: width_{output size rule}_{lhs rule}_{rhs rule} // Widths: 1 bit out, lhs 1 bit - void width_O1_L1(AstNode* nodep, AstNUser* vup); virtual void visit(AstLogNot* nodep, AstNUser* vup) { width_O1_L1(nodep,vup); } virtual void visit(AstPslBool* nodep, AstNUser* vup) { width_O1_L1(nodep,vup); } // Widths: 1 bit out, lhs 1 bit, rhs 1 bit - void width_O1_L1_R1(AstNode* nodep, AstNUser* vup); virtual void visit(AstLogAnd* nodep, AstNUser* vup) { width_O1_L1_R1(nodep,vup); } virtual void visit(AstLogOr* nodep, AstNUser* vup) { width_O1_L1_R1(nodep,vup); } virtual void visit(AstLogIf* nodep, AstNUser* vup) { width_O1_L1_R1(nodep,vup); } virtual void visit(AstLogIff* nodep, AstNUser* vup) { width_O1_L1_R1(nodep,vup); } // Widths: 1 bit out, Any width lhs - void width_O1_L(AstNode* nodep, AstNUser* vup); virtual void visit(AstRedAnd* nodep, AstNUser* vup) { width_O1_L(nodep,vup); } virtual void visit(AstRedOr* nodep, AstNUser* vup) { width_O1_L(nodep,vup); } virtual void visit(AstRedXnor* nodep, AstNUser* vup){ width_O1_L(nodep,vup); } @@ -121,7 +122,6 @@ private: virtual void visit(AstOneHot0* nodep,AstNUser* vup) { width_O1_L(nodep,vup); } // Widths: 1 bit out, lhs width == rhs width - void width_O1_L_Rlhs(AstNode* nodep, AstNUser* vup); virtual void visit(AstEq* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); } virtual void visit(AstEqCase* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); } virtual void visit(AstEqWild* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); } @@ -138,7 +138,6 @@ private: virtual void visit(AstNeqWild* nodep, AstNUser* vup){ width_O1_L_Rlhs(nodep,vup); } // Widths: out width = lhs width = rhs width - void width_Omax_L_Rlhs(AstNode* nodep, AstNUser* vup); virtual void visit(AstAnd* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } virtual void visit(AstOr* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } virtual void visit(AstXnor* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } @@ -156,23 +155,25 @@ private: virtual void visit(AstMulS* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } // Widths: out width = lhs width, but upper matters - void width_Olhs_L(AstNodeUniop* nodep, AstNUser* vup); virtual void visit(AstNot* nodep, AstNUser* vup) { width_Olhs_L(nodep,vup); } - virtual void visit(AstUnaryMin* nodep, AstNUser* vup) { width_Olhs_L(nodep,vup); } + virtual void visit(AstNegate* nodep, AstNUser* vup) { width_Olhs_L(nodep,vup); } // Widths: out width = lhs width, upper doesn't matter - void width_Olhs_Lforce(AstNodeUniop* nodep, AstNUser* vup); virtual void visit(AstSigned* nodep, AstNUser* vup) { width_Olhs_Lforce(nodep,vup); } virtual void visit(AstUnsigned* nodep, AstNUser* vup) { width_Olhs_Lforce(nodep,vup); } // Widths: Output width from lhs, rhs<33 bits - void width_Olhs_L_R32(AstNode* nodep, AstNUser* vup); virtual void visit(AstPow* nodep, AstNUser* vup) { width_Olhs_L_R32(nodep,vup); } virtual void visit(AstPowS* nodep, AstNUser* vup) { width_Olhs_L_R32(nodep,vup); } virtual void visit(AstShiftL* nodep, AstNUser* vup) { width_Olhs_L_R32(nodep,vup); } virtual void visit(AstShiftR* nodep, AstNUser* vup) { width_Olhs_L_R32(nodep,vup); } virtual void visit(AstShiftRS* nodep, AstNUser* vup) { width_Olhs_L_R32(nodep,vup); } + // Widths: Constant, terminal + virtual void visit(AstTime* nodep, AstNUser*) { nodep->width(64,64); } + virtual void visit(AstTestPlusArgs* nodep, AstNUser*) { nodep->width(32,32); } + virtual void visit(AstScopeName* nodep, AstNUser* vup) { nodep->width(64,1); } // A pointer, but not that it matters + // Special cases. So many.... virtual void visit(AstNodeCond* nodep, AstNUser* vup) { // op=cond?expr1:expr2 is a Good large example of the propagation mess @@ -431,9 +432,6 @@ private: nodep->width(32,32); // Says the spec } } - virtual void visit(AstTime* nodep, AstNUser*) { - nodep->width(64,64); - } virtual void visit(AstUCFunc* nodep, AstNUser* vup) { // Give it the size the user wants. if (vup && vup->c()->prelim()) { @@ -459,7 +457,7 @@ private: int selwidth = V3Number::log2b(nodep->lhsp()->width())+1; nodep->width(selwidth,selwidth); } - } + } virtual void visit(AstCvtPackString* nodep, AstNUser* vup) { nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); } @@ -470,9 +468,6 @@ private: virtual void visit(AstText* nodep, AstNUser* vup) { // Only used in CStmts which don't care.... } - virtual void visit(AstScopeName* nodep, AstNUser* vup) { - nodep->width(64,1); // A pointer, but not that it matters - } virtual void visit(AstArrayDType* nodep, AstNUser* vup) { // Lower datatype determines the width nodep->dtypep()->iterateAndNext(*this,vup); @@ -488,6 +483,10 @@ private: // else width in node is correct; it was set based on keyword().width() // at construction time } + virtual void visit(AstConstDType* nodep, AstNUser* vup) { + nodep->iterateChildren(*this, vup); + nodep->widthFrom(nodep->dtypep()); + } virtual void visit(AstRefDType* nodep, AstNUser* vup) { nodep->iterateChildren(*this, vup); if (nodep->defp()) nodep->defp()->iterate(*this,vup); @@ -759,55 +758,55 @@ private: } virtual void visit(AstDisplay* nodep, AstNUser*) { if (nodep->filep()) { - nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); - widthCheck(nodep,"file_descriptor (see docs)",nodep->filep(),64,64); + nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p()); + widthCheck(nodep,"file_descriptor",nodep->filep(),32,32); } // Just let all arguments seek their natural sizes nodep->iterateChildren(*this,WidthVP(ANYSIZE,0,BOTH).p()); } virtual void visit(AstFOpen* nodep, AstNUser*) { - nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); + nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p()); nodep->filenamep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); nodep->modep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); - widthCheck(nodep,"file_descriptor (see docs)",nodep->filep(),64,64); + widthCheck(nodep,"file_descriptor",nodep->filep(),32,32); } virtual void visit(AstFClose* nodep, AstNUser*) { - nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); - widthCheck(nodep,"file_descriptor (see docs)",nodep->filep(),64,64); + nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p()); + widthCheck(nodep,"file_descriptor",nodep->filep(),32,32); } virtual void visit(AstFEof* nodep, AstNUser*) { - nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); + nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p()); nodep->width(1,1); - widthCheck(nodep,"file_descriptor (see docs)",nodep->filep(),64,64); + widthCheck(nodep,"file_descriptor",nodep->filep(),32,32); } virtual void visit(AstFFlush* nodep, AstNUser*) { if (nodep->filep()) { - nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); - widthCheck(nodep,"file_descriptor (see docs)",nodep->filep(),64,64); + nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p()); + widthCheck(nodep,"file_descriptor",nodep->filep(),32,32); } } virtual void visit(AstFGetC* nodep, AstNUser* vup) { - nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); + nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p()); if (vup->c()->prelim()) { nodep->width(32,8); } - widthCheck(nodep,"file_descriptor (see docs)",nodep->filep(),64,64); + widthCheck(nodep,"file_descriptor",nodep->filep(),32,32); } virtual void visit(AstFGetS* nodep, AstNUser* vup) { - nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); + nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p()); nodep->strgp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); if (vup->c()->prelim()) { nodep->width(32,32); } - widthCheck(nodep,"file_descriptor (see docs)",nodep->filep(),64,64); + widthCheck(nodep,"file_descriptor",nodep->filep(),32,32); } virtual void visit(AstFScanF* nodep, AstNUser* vup) { - nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); + nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p()); nodep->exprsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); if (vup->c()->prelim()) { nodep->width(32,32); } - widthCheck(nodep,"file_descriptor (see docs)",nodep->filep(),64,64); + widthCheck(nodep,"file_descriptor",nodep->filep(),32,32); } virtual void visit(AstSScanF* nodep, AstNUser* vup) { nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); @@ -825,9 +824,6 @@ private: nodep->lsbp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); nodep->msbp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); } - virtual void visit(AstTestPlusArgs* nodep, AstNUser* vup) { - nodep->width(32,32); - } virtual void visit(AstValuePlusArgs* nodep, AstNUser* vup) { nodep->exprsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); nodep->width(32,32); @@ -939,7 +935,7 @@ private: m_funcp = NULL; } virtual void visit(AstReturn* nodep, AstNUser* vup) { - if (!m_funcp) { + if (!m_funcp) { if (nodep->lhsp()) { // Return w/o value ok other places nodep->v3error("Return with return value isn't underneath a function"); } @@ -1044,18 +1040,328 @@ private: nodep->iterateChildren(*this); } - // METHODS - bool widthBad (AstNode* nodep, int expWidth, int expWidthMin); + //---------------------------------------------------------------------- + // METHODs + + bool widthBad (AstNode* nodep, int expWidth, int expWidthMin) { + if (nodep->width()==0) nodep->v3fatalSrc("Under node "<prettyTypeName()<<" has no expected width?? Missing Visitor func?"); + if (expWidth==0) nodep->v3fatalSrc("Node "<prettyTypeName()<<" has no expected width?? Missing Visitor func?"); + if (expWidthMin==0) expWidthMin = expWidth; + if (nodep->widthSized() && nodep->width() != expWidthMin) return true; + if (!nodep->widthSized() && nodep->widthMin() > expWidthMin) return true; + return false; + } + + void fixWidthExtend (AstNode* nodep, int expWidth) { + // Fix the width mismatch by extending or truncating bits + // Truncation is rarer, but can occur: parameter [3:0] FOO = 64'h12312; + // A(CONSTwide)+B becomes A(CONSTwidened)+B + // A(somewide)+B becomes A(TRUNC(somewide,width))+B + // or A(EXTRACT(somewide,width,0))+B + UINFO(4," widthExtend_old: "<castConst(); + if (constp && !nodep->isSigned()) { + // Save later constant propagation work, just right-size it. + V3Number num (nodep->fileline(), expWidth); + num.opAssign(constp->num()); + num.isSigned(nodep->isSigned()); + AstNode* newp = new AstConst(nodep->fileline(), num); + constp->replaceWith(newp); + pushDeletep(constp); constp=NULL; + nodep=newp; + } else if (expWidthwidth()) { + // Trunc - Extract + AstNRelinker linker; + nodep->unlinkFrBack(&linker); + AstNode* newp = new AstSel(nodep->fileline(), nodep, 0, expWidth); + linker.relink(newp); + nodep=newp; + } else { + // Extend + AstNRelinker linker; + nodep->unlinkFrBack(&linker); + AstNode* newp = (nodep->isSigned() + ? (new AstExtendS(nodep->fileline(), nodep))->castNode() + : (new AstExtend (nodep->fileline(), nodep))->castNode()); + linker.relink(newp); + nodep=newp; + } + nodep->width(expWidth,expWidth); + UINFO(4," _new: "<castConst(); + if (constp) { + V3Number num (nodep->fileline(), expWidth); + num.opRedOr(constp->num()); + num.isSigned(constp->isSigned()); + AstNode* newp = new AstConst(nodep->fileline(), num); + constp->replaceWith(newp); + nodep=newp; + } else { + AstNRelinker linker; + nodep->unlinkFrBack(&linker); + AstNode* newp = new AstRedOr(nodep->fileline(), nodep); + linker.relink(newp); + nodep=newp; + } + nodep->width(expWidth,expWidth); + UINFO(4," _new: "<castConst()) { + if (constp->num().autoExtend() && !constp->num().sized() && constp->width()==1) { + // Make it the proper size. Careful of proper extension of 0's/1's + V3Number num (constp->fileline(), expWidth); + num.opRepl(constp->num(), expWidth); // {width{'1}} + AstNode* newp = new AstConst(constp->fileline(), num); + // Spec says always unsigned with proper width + if (debug()>4) constp->dumpTree(cout," fixAutoExtend_old: "); + if (debug()>4) newp->dumpTree(cout," _new: "); + constp->replaceWith(newp); + constp->deleteTree(); constp=NULL; + // Tell caller the new constp, and that we changed it. + nodepr = newp; + return true; + } + } + return false; // No change + } + void widthCheck (AstNode* nodep, const char* side, AstNode* underp, int expWidth, int expWidthMin, - bool ignoreWarn=false); + bool ignoreWarn=false) { + //UINFO(9,"wchk "<castConst() && underp->castConst()->num().isFromString() + && expWidth > underp->width() + && (((expWidth - underp->width()) % 8) == 0)) { // At least it's character sized + // reg [31:0] == "foo" we'll consider probably fine. + // Maybe this should be a special warning? Not for now. + ignoreWarn = true; + } + if ((nodep->castAdd() && underp->width()==1 && underp->isOne()) + || (nodep->castSub() && underp->width()==1 && underp->isOne() && 0==strcmp(side,"RHS"))) { + // "foo + 1'b1", or "foo - 1'b1" are very common, people assume they extend correctly + ignoreWarn = true; + } + + if (bad && !ignoreWarn) { + if (debug()>4) nodep->backp()->dumpTree(cout," back: "); + nodep->v3warn(WIDTH,"Operator "<prettyTypeName() + <<" expects "<prettyTypeName()<<" generates "<width() + <<(underp->width()!=underp->widthMin() + ?" or "+cvtToStr(underp->widthMin()):"") + <<" bits."); + } + if (bad || underp->width()!=expWidth) { + fixWidthExtend(underp, expWidth); underp=NULL;//Changed + } + } + void widthCheckReduce (AstNode* nodep, const char* side, AstNode* underp, int expWidth, int expWidthMin, - bool ignoreWarn=false); - void widthCheckPin (AstNode* nodep, AstNode* underp, int expWidth, bool inputPin); - bool fixAutoExtend (AstNode*& nodepr, int expWidth); - void fixWidthExtend (AstNode* nodep, int expWidth); - void fixWidthReduce (AstNode* nodep, int expWidth); + bool ignoreWarn=false) { + // Before calling this, iterate into underp with FINAL state, so numbers get resized appropriately + if (expWidthMin==0) expWidthMin = expWidth; + if (expWidth!=1) nodep->v3fatalSrc("Only for binary functions"); + bool bad = widthBad(underp,expWidth,expWidthMin); + if (bad) { + if (!ignoreWarn) { + if (debug()>4) nodep->backp()->dumpTree(cout," back: "); + nodep->v3warn(WIDTH,"Logical Operator "<prettyTypeName() + <<" expects 1 bit on the "<prettyTypeName()<<" generates "<width() + <<(underp->width()!=underp->widthMin() + ?" or "+cvtToStr(underp->widthMin()):"") + <<" bits."); + } + fixWidthReduce(underp, expWidth); underp=NULL;//Changed + } + } + + void widthCheckPin (AstNode* nodep, AstNode* underp, int expWidth, bool inputPin) { + // Before calling this, iterate into underp with FINAL state, so numbers get resized appropriately + bool bad = widthBad(underp,expWidth,expWidth); + if (bad && fixAutoExtend(underp/*ref*/,expWidth)) bad=false; // Changes underp + if (bad) { + nodep->v3warn(WIDTH,(inputPin?"Input":"Output") + <<" port connection "<prettyName() + <<" expects "<prettyTypeName()<<" generates "<width() + <<(underp->width()!=underp->widthMin() + ?" or "+cvtToStr(underp->widthMin()):"") + <<" bits."); + } + // We only fix input mismatches + if (bad && inputPin) { + fixWidthExtend(underp, expWidth); underp=NULL;//Changed + } + } + + void width_O1_L1(AstNode* nodep, AstNUser* vup) { + // Widths: 1 bit out, lhs 1 bit + // We calculate the width of the UNDER expression. + // We then check its width to see if it's legal, and edit if not + // We finally set the width of our output + if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); + if (vup->c()->prelim()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(1,0,BOTH).p()); + } + nodep->width(1,1); + if (vup->c()->final()) { + widthCheckReduce(nodep,"LHS",nodep->op1p(),1,1); + } + } + + void width_O1_L1_R1(AstNode* nodep, AstNUser* vup) { + // Widths: 1 bit out, lhs 1 bit, rhs 1 bit + if (!nodep->op2p()) nodep->v3fatalSrc("For binary ops only!"); + if (vup->c()->prelim()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(1,0,BOTH).p()); + nodep->op2p()->iterateAndNext(*this,WidthVP(1,0,BOTH).p()); + } + nodep->width(1,1); + if (vup->c()->final()) { + widthCheckReduce(nodep,"LHS",nodep->op1p(),1,1); + widthCheckReduce(nodep,"RHS",nodep->op2p(),1,1); + } + } + + void width_O1_L(AstNode* nodep, AstNUser* vup) { + // Widths: 1 bit out, Any width lhs + if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); + if (vup->c()->prelim()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + } + nodep->width(1,1); + } + + void width_O1_L_Rlhs(AstNode* nodep, AstNUser* vup) { + // Widths: 1 bit out, lhs width == rhs width + if (!nodep->op2p()) nodep->v3fatalSrc("For binary ops only!"); + if (vup->c()->prelim()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + nodep->op2p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + } + int width = max(nodep->op1p()->width(), nodep->op2p()->width()); + int ewidth = max(nodep->op1p()->widthMin(), nodep->op2p()->widthMin()); + nodep->width(1,1); + if (vup->c()->final()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); + nodep->op2p()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); + widthCheck(nodep,"LHS",nodep->op1p(),width,ewidth); + widthCheck(nodep,"RHS",nodep->op2p(),width,ewidth); + } + } + + void width_Ofixed_L(AstNodeUniop* nodep, AstNUser* vup, int width) { + // Widths: out width = specified width + if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); + if (vup->c()->prelim()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + } + nodep->width(width,width); + } + + void width_Olhs_L(AstNodeUniop* nodep, AstNUser* vup) { + // Widths: out width = lhs width + // "Interim results shall take the max of operands, including LHS of assignments" + if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); + if (vup->c()->prelim()) { + nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + } + int width = max(vup->c()->width(), nodep->lhsp()->width()); + int ewidth = max(vup->c()->widthMin(), nodep->lhsp()->widthMin()); + nodep->width(width,ewidth); + if (vup->c()->final()) { + nodep->lhsp()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); + widthCheck(nodep,"LHS",nodep->lhsp(),width,ewidth); + } + } + + void width_Olhs_Lforce(AstNodeUniop* nodep, AstNUser* vup) { + // Widths: out width = lhs width + // It always comes exactly from LHS; ignores any upper operand + if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); + if (vup->c()->prelim()) { + nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + } + int width = nodep->lhsp()->width(); + int ewidth = nodep->lhsp()->width(); // Not minWidth; force it. + nodep->width(width,ewidth); + if (vup->c()->final()) { + // Final call, so make sure children check their sizes + nodep->lhsp()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); + widthCheck(nodep,"LHS",nodep->lhsp(),width,ewidth); + } + } + + void width_Olhs_L_R32(AstNode* nodep, AstNUser* vup) { + // Widths: Output width from lhs, rhs<33 bits + if (!nodep->op2p()) nodep->v3fatalSrc("For binary ops only!"); + if (vup->c()->prelim()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + nodep->op2p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + } + int width = max(vup->c()->width(), nodep->op1p()->width()); + int ewidth = max(vup->c()->widthMin(), nodep->op1p()->widthMin()); + nodep->width(width,ewidth); + if (vup->c()->final()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); + widthCheck(nodep,"LHS",nodep->op1p(),width,ewidth); + if (nodep->op2p()->width()>32) + nodep->op2p()->v3error("Unsupported: Shifting of by a over 32 bit number isn't supported." + <<" (This isn't a shift of 32 bits, but a shift of 2^32, or 4 billion!)\n"); + } + } + + void width_Omax_L_Rlhs(AstNode* nodep, AstNUser* vup) { + // Widths: out width = lhs width = rhs width + if (!nodep->op2p()) nodep->v3fatalSrc("For binary ops only!"); + // If errors are off, we need to follow the spec; thus we really need to do the max() + // because the rhs could be larger, and we need to have proper editing to get the widths + // to be the same for our operations. + if (vup->c()->prelim()) { // First stage evaluation + // Determine expression widths only relying on what's in the subops + nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + nodep->op2p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + } + int width = max(vup->c()->width(), max(nodep->op1p()->width(), nodep->op2p()->width())); + int mwidth = max(vup->c()->widthMin(), max(nodep->op1p()->widthMin(), nodep->op2p()->widthMin())); + nodep->width(width,mwidth); + if (vup->c()->final()) { + // Final call, so make sure children check their sizes + nodep->op1p()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); + nodep->op2p()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); + // Some warning suppressions + bool lhsOk=false; bool rhsOk = false; + if (nodep->castAdd() || nodep->castSub()) { + lhsOk = (mwidth == (nodep->op1p()->widthMin()+1)); // Ok if user wants extra bit from carry + rhsOk = (mwidth == (nodep->op2p()->widthMin()+1)); // Ok if user wants extra bit from carry + } else if (nodep->castMul() || nodep->castMulS()) { + lhsOk = (mwidth >= (nodep->op1p()->widthMin())); + rhsOk = (mwidth >= (nodep->op2p()->widthMin())); + } + // Error report and change sizes for suboperands of this node. + widthCheck(nodep,"LHS",nodep->op1p(),width,mwidth,lhsOk); + widthCheck(nodep,"RHS",nodep->op2p(),width,mwidth,rhsOk); + } + } public: // CONSTUCTORS @@ -1072,356 +1378,6 @@ public: virtual ~WidthVisitor() {} }; -//---------------------------------------------------------------------- -// METHODs - -bool WidthVisitor::widthBad (AstNode* nodep, int expWidth, int expWidthMin) { - if (nodep->width()==0) nodep->v3fatalSrc("Under node "<prettyTypeName()<<" has no expected width?? Missing Visitor func?"); - if (expWidth==0) nodep->v3fatalSrc("Node "<prettyTypeName()<<" has no expected width?? Missing Visitor func?"); - if (expWidthMin==0) expWidthMin = expWidth; - if (nodep->widthSized() && nodep->width() != expWidthMin) return true; - if (!nodep->widthSized() && nodep->widthMin() > expWidthMin) return true; - return false; -} - -void WidthVisitor::fixWidthExtend (AstNode* nodep, int expWidth) { - // Fix the width mismatch by extending or truncating bits - // Truncation is rarer, but can occur: parameter [3:0] FOO = 64'h12312; - // A(CONSTwide)+B becomes A(CONSTwidened)+B - // A(somewide)+B becomes A(TRUNC(somewide,width))+B - // or A(EXTRACT(somewide,width,0))+B - UINFO(4," widthExtend_old: "<castConst(); - if (constp && !nodep->isSigned()) { - // Save later constant propagation work, just right-size it. - V3Number num (nodep->fileline(), expWidth); - num.opAssign(constp->num()); - num.isSigned(nodep->isSigned()); - AstNode* newp = new AstConst(nodep->fileline(), num); - constp->replaceWith(newp); - pushDeletep(constp); constp=NULL; - nodep=newp; - } else if (expWidthwidth()) { - // Trunc - Extract - AstNRelinker linker; - nodep->unlinkFrBack(&linker); - AstNode* newp = new AstSel(nodep->fileline(), nodep, 0, expWidth); - linker.relink(newp); - nodep=newp; - } else { - // Extend - AstNRelinker linker; - nodep->unlinkFrBack(&linker); - AstNode* newp = (nodep->isSigned() - ? (new AstExtendS(nodep->fileline(), nodep))->castNode() - : (new AstExtend (nodep->fileline(), nodep))->castNode()); - linker.relink(newp); - nodep=newp; - } - nodep->width(expWidth,expWidth); - UINFO(4," _new: "<castConst(); - if (constp) { - V3Number num (nodep->fileline(), expWidth); - num.opRedOr(constp->num()); - num.isSigned(constp->isSigned()); - AstNode* newp = new AstConst(nodep->fileline(), num); - constp->replaceWith(newp); - nodep=newp; - } else { - AstNRelinker linker; - nodep->unlinkFrBack(&linker); - AstNode* newp = new AstRedOr(nodep->fileline(), nodep); - linker.relink(newp); - nodep=newp; - } - nodep->width(expWidth,expWidth); - UINFO(4," _new: "<castConst()) { - if (constp->num().autoExtend() && !constp->num().sized() && constp->width()==1) { - // Make it the proper size. Careful of proper extension of 0's/1's - V3Number num (constp->fileline(), expWidth); - num.opRepl(constp->num(), expWidth); // {width{'1}} - AstNode* newp = new AstConst(constp->fileline(), num); - // Spec says always unsigned with proper width - if (debug()>4) constp->dumpTree(cout," fixAutoExtend_old: "); - if (debug()>4) newp->dumpTree(cout," _new: "); - constp->replaceWith(newp); - constp->deleteTree(); constp=NULL; - // Tell caller the new constp, and that we changed it. - nodepr = newp; - return true; - } - } - return false; // No change -} - -void WidthVisitor::widthCheck (AstNode* nodep, const char* side, - AstNode* underp, int expWidth, int expWidthMin, - bool ignoreWarn) { - //UINFO(9,"wchk "<castConst() && underp->castConst()->num().isFromString() - && expWidth > underp->width() - && (((expWidth - underp->width()) % 8) == 0)) { // At least it's character sized - // reg [31:0] == "foo" we'll consider probably fine. - // Maybe this should be a special warning? Not for now. - ignoreWarn = true; - } - if ((nodep->castAdd() && underp->width()==1 && underp->isOne()) - || (nodep->castSub() && underp->width()==1 && underp->isOne() && 0==strcmp(side,"RHS"))) { - // "foo + 1'b1", or "foo - 1'b1" are very common, people assume they extend correctly - ignoreWarn = true; - } - - if (bad && !ignoreWarn) { - if (debug()>4) nodep->backp()->dumpTree(cout," back: "); - nodep->v3warn(WIDTH,"Operator "<prettyTypeName() - <<" expects "<prettyTypeName()<<" generates "<width() - <<(underp->width()!=underp->widthMin() - ?" or "+cvtToStr(underp->widthMin()):"") - <<" bits."); - } - if (bad || underp->width()!=expWidth) { - fixWidthExtend(underp, expWidth); underp=NULL;//Changed - } -} - -void WidthVisitor::widthCheckReduce (AstNode* nodep, const char* side, - AstNode* underp, int expWidth, int expWidthMin, - bool ignoreWarn) { - // Before calling this, iterate into underp with FINAL state, so numbers get resized appropriately - if (expWidthMin==0) expWidthMin = expWidth; - if (expWidth!=1) nodep->v3fatalSrc("Only for binary functions"); - bool bad = widthBad(underp,expWidth,expWidthMin); - if (bad) { - if (!ignoreWarn) { - if (debug()>4) nodep->backp()->dumpTree(cout," back: "); - nodep->v3warn(WIDTH,"Logical Operator "<prettyTypeName() - <<" expects 1 bit on the "<prettyTypeName()<<" generates "<width() - <<(underp->width()!=underp->widthMin() - ?" or "+cvtToStr(underp->widthMin()):"") - <<" bits."); - } - fixWidthReduce(underp, expWidth); underp=NULL;//Changed - } -} - -void WidthVisitor::widthCheckPin (AstNode* nodep, AstNode* underp, int expWidth, bool inputPin) { - // Before calling this, iterate into underp with FINAL state, so numbers get resized appropriately - bool bad = widthBad(underp,expWidth,expWidth); - if (bad && fixAutoExtend(underp/*ref*/,expWidth)) bad=false; // Changes underp - if (bad) { - nodep->v3warn(WIDTH,(inputPin?"Input":"Output") - <<" port connection "<prettyName() - <<" expects "<prettyTypeName()<<" generates "<width() - <<(underp->width()!=underp->widthMin() - ?" or "+cvtToStr(underp->widthMin()):"") - <<" bits."); - } - // We only fix input mismatches - if (bad && inputPin) { - fixWidthExtend(underp, expWidth); underp=NULL;//Changed - } -} - -void WidthVisitor::width_O1_L1(AstNode* nodep, AstNUser* vup) { - // Widths: 1 bit out, lhs 1 bit - // We calculate the width of the UNDER expression. - // We then check its width to see if it's legal, and edit if not - // We finally set the width of our output - if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); - if (vup->c()->prelim()) { - nodep->op1p()->iterateAndNext(*this,WidthVP(1,0,BOTH).p()); - } - nodep->width(1,1); - if (vup->c()->final()) { - widthCheckReduce(nodep,"LHS",nodep->op1p(),1,1); - } -} - -void WidthVisitor::width_O1_L1_R1(AstNode* nodep, AstNUser* vup) { - // Widths: 1 bit out, lhs 1 bit, rhs 1 bit - if (!nodep->op2p()) nodep->v3fatalSrc("For binary ops only!"); - if (vup->c()->prelim()) { - nodep->op1p()->iterateAndNext(*this,WidthVP(1,0,BOTH).p()); - nodep->op2p()->iterateAndNext(*this,WidthVP(1,0,BOTH).p()); - } - nodep->width(1,1); - if (vup->c()->final()) { - widthCheckReduce(nodep,"LHS",nodep->op1p(),1,1); - widthCheckReduce(nodep,"RHS",nodep->op2p(),1,1); - } -} - -void WidthVisitor::width_O1_L(AstNode* nodep, AstNUser* vup) { - // Widths: 1 bit out, Any width lhs - if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); - if (vup->c()->prelim()) { - nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); - } - nodep->width(1,1); -} - -void WidthVisitor::width_O1_L_Rlhs(AstNode* nodep, AstNUser* vup) { - // Widths: 1 bit out, lhs width == rhs width - if (!nodep->op2p()) nodep->v3fatalSrc("For binary ops only!"); - if (vup->c()->prelim()) { - nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); - nodep->op2p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); - } - int width = max(nodep->op1p()->width(), nodep->op2p()->width()); - int ewidth = max(nodep->op1p()->widthMin(), nodep->op2p()->widthMin()); - nodep->width(1,1); - if (vup->c()->final()) { - nodep->op1p()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); - nodep->op2p()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); - widthCheck(nodep,"LHS",nodep->op1p(),width,ewidth); - widthCheck(nodep,"RHS",nodep->op2p(),width,ewidth); - } -} - -void WidthVisitor::width_Olhs_L(AstNodeUniop* nodep, AstNUser* vup) { - // Widths: out width = lhs width - // "Interim results shall take the max of operands, including LHS of assignments" - if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); - if (vup->c()->prelim()) { - nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); - } - int width = max(vup->c()->width(), nodep->lhsp()->width()); - int ewidth = max(vup->c()->widthMin(), nodep->lhsp()->widthMin()); - nodep->width(width,ewidth); - if (vup->c()->final()) { - nodep->lhsp()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); - widthCheck(nodep,"LHS",nodep->lhsp(),width,ewidth); - } -} - -void WidthVisitor::width_Olhs_Lforce(AstNodeUniop* nodep, AstNUser* vup) { - // Widths: out width = lhs width - // It always comes exactly from LHS; ignores any upper operand - if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); - if (vup->c()->prelim()) { - nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); - } - int width = nodep->lhsp()->width(); - int ewidth = nodep->lhsp()->width(); // Not minWidth; force it. - nodep->width(width,ewidth); - if (vup->c()->final()) { - // Final call, so make sure children check their sizes - nodep->lhsp()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); - widthCheck(nodep,"LHS",nodep->lhsp(),width,ewidth); - } -} - -void WidthVisitor::width_Olhs_L_R32(AstNode* nodep, AstNUser* vup) { - // Widths: Output width from lhs, rhs<33 bits - if (!nodep->op2p()) nodep->v3fatalSrc("For binary ops only!"); - if (vup->c()->prelim()) { - nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); - nodep->op2p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); - } - int width = max(vup->c()->width(), nodep->op1p()->width()); - int ewidth = max(vup->c()->widthMin(), nodep->op1p()->widthMin()); - nodep->width(width,ewidth); - if (vup->c()->final()) { - nodep->op1p()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); - widthCheck(nodep,"LHS",nodep->op1p(),width,ewidth); - if (nodep->op2p()->width()>32) - nodep->op2p()->v3error("Unsupported: Shifting of by a over 32 bit number isn't supported." - <<" (This isn't a shift of 32 bits, but a shift of 2^32, or 4 billion!)\n"); - } -} - -void WidthVisitor::width_Omax_L_Rlhs(AstNode* nodep, AstNUser* vup) { - // Widths: out width = lhs width = rhs width - if (!nodep->op2p()) nodep->v3fatalSrc("For binary ops only!"); - // If errors are off, we need to follow the spec; thus we really need to do the max() - // because the rhs could be larger, and we need to have proper editing to get the widths - // to be the same for our operations. - if (vup->c()->prelim()) { // First stage evaluation - // Determine expression widths only relying on what's in the subops - nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); - nodep->op2p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); - } - int width = max(vup->c()->width(), max(nodep->op1p()->width(), nodep->op2p()->width())); - int mwidth = max(vup->c()->widthMin(), max(nodep->op1p()->widthMin(), nodep->op2p()->widthMin())); - nodep->width(width,mwidth); - if (vup->c()->final()) { - // Final call, so make sure children check their sizes - nodep->op1p()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); - nodep->op2p()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); - // Some warning suppressions - bool lhsOk=false; bool rhsOk = false; - if (nodep->castAdd() || nodep->castSub()) { - lhsOk = (mwidth == (nodep->op1p()->widthMin()+1)); // Ok if user wants extra bit from carry - rhsOk = (mwidth == (nodep->op2p()->widthMin()+1)); // Ok if user wants extra bit from carry - } else if (nodep->castMul() || nodep->castMulS()) { - lhsOk = (mwidth >= (nodep->op1p()->widthMin())); - rhsOk = (mwidth >= (nodep->op2p()->widthMin())); - } - // Error report and change sizes for suboperands of this node. - widthCheck(nodep,"LHS",nodep->op1p(),width,mwidth,lhsOk); - widthCheck(nodep,"RHS",nodep->op2p(),width,mwidth,rhsOk); - } -} - -//###################################################################### - -class WidthCommitVisitor : public AstNVisitor { - // Now that all widthing is complete, - // Copy all width() to widthMin(). V3Const expects this -private: - // VISITORS - virtual void visit(AstConst* nodep, AstNUser*) { - nodep->width(nodep->width(),nodep->width()); - if ((nodep->width() != nodep->num().width()) || !nodep->num().sized()) { - V3Number num (nodep->fileline(), nodep->width()); - num.opAssign(nodep->num()); - num.isSigned(nodep->isSigned()); - AstNode* newp = new AstConst(nodep->fileline(), num); - nodep->replaceWith(newp); - //if (debug()>4) nodep->dumpTree(cout," fixConstSize_old: "); - //if (debug()>4) newp->dumpTree(cout," _new: "); - pushDeletep(nodep); nodep=NULL; - } - } - virtual void visit(AstNode* nodep, AstNUser*) { - 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) { - nodep->accept(*this); - } - virtual ~WidthCommitVisitor() {} -}; - //###################################################################### // Width class functions @@ -1441,13 +1397,6 @@ AstNode* V3Width::widthParamsEdit(AstNode* nodep) { return nodep; } -AstNode* V3Width::widthParamsEditIfNeed(AstNode* nodep) { - if (!nodep->width()) { - nodep = V3Width::widthParamsEdit(nodep); - } - return nodep; -} - void V3Width::widthCommit(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<width(nodep->width(),nodep->width()); + if ((nodep->width() != nodep->num().width()) || !nodep->num().sized()) { + V3Number num (nodep->fileline(), nodep->width()); + num.opAssign(nodep->num()); + num.isSigned(nodep->isSigned()); + AstNode* newp = new AstConst(nodep->fileline(), num); + nodep->replaceWith(newp); + //if (debug()>4) nodep->dumpTree(cout," fixConstSize_old: "); + //if (debug()>4) newp->dumpTree(cout," _new: "); + pushDeletep(nodep); nodep=NULL; + } + } + virtual void visit(AstNode* nodep, AstNUser*) { + 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) { + nodep->accept(*this); + } + virtual ~WidthCommitVisitor() {} +}; + +//###################################################################### + +#endif // Guard diff --git a/src/verilog.l b/src/verilog.l index 6bcb92e59..c07083e40 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -478,12 +478,12 @@ word [a-zA-Z0-9_]+ { /* Keywords */ "assert" { FL; return yASSERT; } + "const" { FL; return yCONST__LEX; } "cover" { FL; return yCOVER; } "property" { FL; return yPROPERTY; } /* Generic unsupported warnings */ "assume" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented in non-PSL context: %s",yytext); } "before" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented in non-PSL context: %s",yytext); } - "const" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented in non-PSL context: %s",yytext); } "sequence" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented in non-PSL context: %s",yytext); } "union" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented in non-PSL context: %s",yytext); } "within" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented in non-PSL context: %s",yytext); } @@ -964,7 +964,8 @@ int V3ParseImp::lexToken() { //yylval // Set by yylexThis() } // If a paren, read another - if (token == yGLOBAL__LEX + if (token == yCONST__LEX + || token == yGLOBAL__LEX // Never put yID_* here; below symbol table resolution would break ) { if (debugFlex()) { cout<<" lexToken: reading ahead to find possible strength"<newString("global"); } // Avoid 2009 "global" conflicting with old code when we can } diff --git a/src/verilog.y b/src/verilog.y index e284ea022..0a0d0b300 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -274,6 +274,8 @@ class AstSenTree; %token yCASEZ "casez" %token yCHANDLE "chandle" %token yCLOCKING "clocking" +%token yCONST__ETC "const" +%token yCONST__LEX "const-in-lex" %token yCMOS "cmos" %token yCONTEXT "context" %token yCONTINUE "continue" @@ -1269,23 +1271,24 @@ data_declarationVar: // IEEE: part of data_declaration ; data_declarationVarFront: // IEEE: part of data_declaration + // // Expanded: "constE yVAR lifetimeE data_type" // // implicit_type expanded into /*empty*/ or "signingE rangeList" - constE yVAR lifetimeE data_type { /*VARRESET-in-ddVar*/ VARDTYPE($4); } - | constE yVAR lifetimeE { /*VARRESET-in-ddVar*/ VARDTYPE(new AstBasicDType($2, LOGIC_IMPLICIT)); } - | constE yVAR lifetimeE signingE rangeList { /*VARRESET-in-ddVar*/ VARDTYPE(GRAMMARP->addRange(new AstBasicDType($2, LOGIC_IMPLICIT, $4),$5,false)); } + /**/ yVAR lifetimeE data_type { /*VARRESET-in-ddVar*/ VARDTYPE($3); } + | /**/ yVAR lifetimeE { /*VARRESET-in-ddVar*/ VARDTYPE(new AstBasicDType($1, LOGIC_IMPLICIT)); } + | /**/ yVAR lifetimeE signingE rangeList { /*VARRESET-in-ddVar*/ VARDTYPE(GRAMMARP->addRange(new AstBasicDType($1, LOGIC_IMPLICIT, $3), $4,false)); } + // + // // implicit_type expanded into /*empty*/ or "signingE rangeList" + | yCONST__ETC yVAR lifetimeE data_type { /*VARRESET-in-ddVar*/ VARDTYPE(new AstConstDType($1, $4)); } + | yCONST__ETC yVAR lifetimeE { /*VARRESET-in-ddVar*/ VARDTYPE(new AstConstDType($1, new AstBasicDType($2, LOGIC_IMPLICIT))); } + | yCONST__ETC yVAR lifetimeE signingE rangeList { /*VARRESET-in-ddVar*/ VARDTYPE(new AstConstDType($1, GRAMMARP->addRange(new AstBasicDType($2, LOGIC_IMPLICIT, $4), $5,false))); } // // // Expanded: "constE lifetimeE data_type" | /**/ data_type { /*VARRESET-in-ddVar*/ VARDTYPE($1); } | /**/ lifetime data_type { /*VARRESET-in-ddVar*/ VARDTYPE($2); } - //UNSUP yCONST__ETC lifetimeE data_type { /*VARRESET-in-ddVar*/ VARDTYPE($3); } + | yCONST__ETC lifetimeE data_type { /*VARRESET-in-ddVar*/ VARDTYPE(new AstConstDType($1, $3)); } // // = class_new is in variable_decl_assignment ; -constE: // IEEE: part of data_declaration - /* empty */ { } - //UNSUP yCONST__ETC { UNSUP } - ; - implicit_typeE: // IEEE: part of *data_type_or_implicit // // Also expanded in data_declaration /* empty */ { $$ = NULL; } @@ -2374,7 +2377,7 @@ expr: // IEEE: part of expression/constant_expression/primary // // // IEEE: unary_operator primary '+' ~r~expr %prec prUNARYARITH { $$ = $2; } - | '-' ~r~expr %prec prUNARYARITH { $$ = new AstUnaryMin ($1,$2); } + | '-' ~r~expr %prec prUNARYARITH { $$ = new AstNegate ($1,$2); } | '!' ~r~expr %prec prNEGATION { $$ = new AstLogNot ($1,$2); } | '&' ~r~expr %prec prREDUCTION { $$ = new AstRedAnd ($1,$2); } | '~' ~r~expr %prec prNEGATION { $$ = new AstNot ($1,$2); } diff --git a/test_regress/driver.pl b/test_regress/driver.pl index b85a346ca..eff31cfbb 100755 --- a/test_regress/driver.pl +++ b/test_regress/driver.pl @@ -336,6 +336,7 @@ sub new { $self->{run_log_filename} ||= "$self->{obj_dir}/vlt_sim.log"; $self->{coverage_filename} ||= "$self->{obj_dir}/vlt_coverage.pl"; $self->{vcd_filename} ||= "$self->{obj_dir}/sim.vcd"; + $self->{main_filename} ||= "$self->{obj_dir}/$self->{VM_PREFIX}__main.cpp"; ($self->{top_filename} = $self->{pl_filename}) =~ s/\.pl$/\.v/; if (!$self->{make_top_shell}) { $self->{top_shell_filename} = $self->{top_filename}; @@ -536,6 +537,11 @@ sub compile { return 1; } + if (!$param{fails} && $param{verilator_make_gcc} + && $param{make_main}) { + $self->_make_main(); + } + $self->_run(logfile=>"$self->{obj_dir}/vlt_compile.log", fails=>$param{fails}, expect=>$param{expect}, @@ -543,9 +549,6 @@ sub compile { return 1 if $self->errors || $self->skips; if (!$param{fails} && $param{verilator_make_gcc}) { - if ($param{make_main}) { - $self->_make_main(); - } if ($self->sp) { $self->_sp_preproc(%param); } @@ -806,7 +809,7 @@ sub _make_main { $self->_read_inputs(); - my $filename = "$self->{obj_dir}/$self->{VM_PREFIX}__main.cpp"; + my $filename = $self->{main_filename}; my $fh = IO::File->new(">$filename") or die "%Error: $! $filename,"; print $fh "// Test defines\n"; diff --git a/test_regress/t/t_assert_synth.v b/test_regress/t/t_assert_synth.v index a65acf774..8506ca13a 100644 --- a/test_regress/t/t_assert_synth.v +++ b/test_regress/t/t_assert_synth.v @@ -47,7 +47,7 @@ module t (/*AUTOARG*/ endcase end -`ifdef NOT_YET_verilator +`ifdef NOT_YET_VERILATOR // Unsupported // ambit synthesis one_hot "a, b_oh" // cadence one_cold "a_l, b_oc_l" `endif diff --git a/test_regress/t/t_case_huge_sub2.v b/test_regress/t/t_case_huge_sub2.v index 2dec19bbb..53e2ab113 100644 --- a/test_regress/t/t_case_huge_sub2.v +++ b/test_regress/t/t_case_huge_sub2.v @@ -25,7 +25,7 @@ module t_case_huge_sub2 (/*AUTOARG*/ always @(/*AS*/index) begin case (index[7:0]) -`ifdef verilator +`ifdef VERILATOR // Harder test 8'h00: begin outa = $c("0"); end // Makes whole table non-optimizable `else 8'h00: begin outa = 10'h0; end diff --git a/test_regress/t/t_case_write1_tasks.v b/test_regress/t/t_case_write1_tasks.v index 19df87e11..0c4844254 100644 --- a/test_regress/t/t_case_write1_tasks.v +++ b/test_regress/t/t_case_write1_tasks.v @@ -2497,11 +2497,7 @@ module t_case_write1_tasks (); endtask task big_case; -`ifdef verilator - input [ 63:0] fd; -`else input [ 31:0] fd; -`endif input [ 31:0] foo; reg [STRLEN*8: 1] foobar; // verilator no_inline_task diff --git a/test_regress/t/t_case_write2_tasks.v b/test_regress/t/t_case_write2_tasks.v index e0e2bf537..9790eaad3 100644 --- a/test_regress/t/t_case_write2_tasks.v +++ b/test_regress/t/t_case_write2_tasks.v @@ -10,11 +10,7 @@ module t_case_write2_tasks (); // verilator lint_off WIDTH // verilator lint_off CASEINCOMPLETE -`ifdef verilator - `define FD_BITS 63:0 -`else `define FD_BITS 31:0 -`endif parameter STRLEN = 78; task ozonerab; diff --git a/test_regress/t/t_clk_gater.v b/test_regress/t/t_clk_gater.v index 0fe44d6fd..4a8bdfe88 100644 --- a/test_regress/t/t_clk_gater.v +++ b/test_regress/t/t_clk_gater.v @@ -86,7 +86,7 @@ module Test (/*AUTOARG*/ end reg displayit; -`ifdef verilator +`ifdef VERILATOR // Harder test initial displayit = $c1("0"); // Something that won't optimize away `else initial displayit = '0; diff --git a/test_regress/t/t_dpi_import.v b/test_regress/t/t_dpi_import.v index 6661a3eb3..4d73d6a04 100644 --- a/test_regress/t/t_dpi_import.v +++ b/test_regress/t/t_dpi_import.v @@ -11,7 +11,7 @@ `ifdef NC `define NO_SHORTREAL `endif -`ifdef VERILATOR +`ifdef VERILATOR // Unsupported `define NO_SHORTREAL `endif diff --git a/test_regress/t/t_extend.v b/test_regress/t/t_extend.v index 4babc6f48..31814f1df 100644 --- a/test_regress/t/t_extend.v +++ b/test_regress/t/t_extend.v @@ -55,7 +55,7 @@ module t (/*AUTOARG*/ #error "`systemc_header didn't work" #endif bool m_did_ctor; - uint32_t my_function() { + vluint32_t my_function() { if (!m_did_ctor) vl_fatal(__FILE__,__LINE__,__FILE__,"`systemc_ctor didn't work"); return 1; } diff --git a/test_regress/t/t_func_public.v b/test_regress/t/t_func_public.v index a187e9bc0..6b3da7b81 100644 --- a/test_regress/t/t_func_public.v +++ b/test_regress/t/t_func_public.v @@ -100,7 +100,7 @@ module tpub ( if (1'b1 != got_bool) $stop; $c("publicGetLong(got_long);"); if (24'h11bca != got_long) $stop; - $c("{ uint64_t qq; publicGetQuad(qq); got_quad=qq; }"); + $c("{ vluint64_t qq; publicGetQuad(qq); got_quad=qq; }"); if (60'haaaa_bbbb_cccc != got_quad) $stop; $c("{ WData gw[3]; publicGetWide(gw); VL_ASSIGN_W(72,got_wide,gw); }"); if (72'hac_abca_aaaa_bbbb_1234 != got_wide) $stop; diff --git a/test_regress/t/t_sys_file_basic.pl b/test_regress/t/t_sys_file_basic.pl index d319381ef..e5184e2c6 100755 --- a/test_regress/t/t_sys_file_basic.pl +++ b/test_regress/t/t_sys_file_basic.pl @@ -10,12 +10,15 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di unlink("$Self->{obj_dir}/t_sys_file_basic_test.log"); compile ( - v_flags2 => ['+incdir+../include'], - ); + v_flags2 => ['+incdir+../include', + # Build without cached objects, see bug363 + "--exe ../$Self->{main_filename}"], + make_flags=>'MAKE_MAIN=0 VM_PARALLEL_BUILDS=0', + ); execute ( - check_finished=>1, - ); + check_finished=>1, + ); file_grep ("$Self->{obj_dir}/t_sys_file_basic_test.log", qr/\[0\] hello v=12345667 diff --git a/test_regress/t/t_var_const.pl b/test_regress/t/t_var_const.pl new file mode 100755 index 000000000..7058e622f --- /dev/null +++ b/test_regress/t/t_var_const.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_var_const.v b/test_regress/t/t_var_const.v new file mode 100644 index 000000000..6a3808445 --- /dev/null +++ b/test_regress/t/t_var_const.v @@ -0,0 +1,21 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2011 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + const logic [2:0] five = 3'd5; + + always @ (posedge clk) begin + if (five !== 3'd5) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_var_const_bad.pl b/test_regress/t/t_var_const_bad.pl new file mode 100755 index 000000000..9ceccf307 --- /dev/null +++ b/test_regress/t/t_var_const_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2005 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 ( + v_flags2 => ["--lint-only"], + fails=>1, + expect=> +'%Error: t/t_var_const_bad.v:\d+: Assigning to const variable: five +%Error: Exiting due to.*', + ) if $Self->{v3}; + +ok(1); +1; diff --git a/test_regress/t/t_var_const_bad.v b/test_regress/t/t_var_const_bad.v new file mode 100644 index 000000000..f37a58213 --- /dev/null +++ b/test_regress/t/t_var_const_bad.v @@ -0,0 +1,22 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2011 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + const logic [2:0] five = 3'd5; + + always @ (posedge clk) begin + five = 3'd4; + if (five !== 3'd5) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_var_pins_sc64.pl b/test_regress/t/t_var_pins_sc64.pl index 20a8cb72e..d0ecf9d1d 100755 --- a/test_regress/t/t_var_pins_sc64.pl +++ b/test_regress/t/t_var_pins_sc64.pl @@ -21,14 +21,14 @@ if ($Self->{v3}) { file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_in \s+ i8;/x); file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_in \s+ i16;/x); file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_in \s+ i32;/x); - file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_in \s+ i64;/x); + file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_in \s+ i64;/x); file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_in\s> \s+ i65;/x); file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_out \s+ o1;/x); file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_out \s+ o8;/x); file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_out \s+ o16;/x); file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_out \s+ o32;/x); - file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_out \s+ o64;/x); + file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_out \s+ o64;/x); file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_out\s> \s+ o65;/x); } diff --git a/test_regress/t/t_var_pins_scui.pl b/test_regress/t/t_var_pins_scui.pl index 26b69c522..97bdafb71 100755 --- a/test_regress/t/t_var_pins_scui.pl +++ b/test_regress/t/t_var_pins_scui.pl @@ -21,14 +21,14 @@ if ($Self->{v3}) { file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_in \s+ i8;/x); file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_in \s+ i16;/x); file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_in \s+ i32;/x); - file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_in \s+ i64;/x); + file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_in \s+ i64;/x); file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_in\s> \s+ i65;/x); file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_out \s+ o1;/x); file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_out \s+ o8;/x); file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_out \s+ o16;/x); file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_out \s+ o32;/x); - file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_out \s+ o64;/x); + file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_out \s+ o64;/x); file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.sp", qr/sc_out\s> \s+ o65;/x); } diff --git a/test_sc/sc_main.cpp b/test_sc/sc_main.cpp index 43629e1e8..f509fdc84 100644 --- a/test_sc/sc_main.cpp +++ b/test_sc/sc_main.cpp @@ -60,11 +60,11 @@ int sc_main(int argc, char* argv[]) { cout << "Defining Interconnect\n"; sc_signal reset_l; sc_signal passed; - sc_signal in_small; - sc_signal in_quad; + sc_signal in_small; + sc_signal in_quad; sc_signal > in_wide; - sc_signal out_small; - sc_signal out_quad; + sc_signal out_small; + sc_signal out_quad; sc_signal > out_wide; //========== diff --git a/test_verilated/vgen.pl b/test_verilated/vgen.pl index 4c5f9051c..0bd9026d7 100755 --- a/test_verilated/vgen.pl +++ b/test_verilated/vgen.pl @@ -58,7 +58,7 @@ our $Raise_Weight_Max = 50; 'VREDXNOR'=> {weight=>1&&1, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(^~ %1)', }, 'VREDXOR'=> {weight=>1&&1, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(^ %1)', }, 'VNOT'=> {weight=>1&&3, width=>0, sc=>1, terminal=>0, v=>'(~ %1)', }, - 'VUNARYMIN'=> {weight=>1&&2, width=>0, sc=>1, terminal=>0, v=>'(- %1)', }, + 'VNEGATE'=> {weight=>1&&2, width=>0, sc=>1, terminal=>0, v=>'(- %1)', }, 'VCOUNTONES'=> {weight=>0&&2, width=>32, signed=>0, sc=>0, terminal=>0, v=>'\$countones(%1)', }, # No ncv support 'VONEHOT'=> {weight=>0&&2, width=>1, signed=>0, sc=>0, terminal=>0, v=>'\$onehot(%1)', }, # No ncv support 'VONEHOT0'=> {weight=>0&&2, width=>1, signed=>0, sc=>0, terminal=>0, v=>'\$onehot0(%1)', }, # No ncv support @@ -143,7 +143,7 @@ my %ops2 = 'VREDXOR'=> {pl=>'VREDXOR (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>0);'}, 'VREDXNOR'=> {pl=>'VREDXNOR (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>0);'}, 'VNOT'=> {pl=>'VNOT (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg);'}, - 'VUNARYMIN'=> {pl=>'VUNARYMIN(%tr,%1v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg);'}, + 'VNEGATE'=> {pl=>'VNEGATE (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg);'}, 'VCOUNTONES'=> {pl=>'VCOUNTONES(%tr,%1v);', rnd=>'%1r=gen_leaf(width=>0);'}, 'VONEHOT'=> {pl=>'VONEHOT (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>0);'}, 'VONEHOT0'=> {pl=>'VONEHOT0 (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>0);'}, @@ -832,8 +832,8 @@ sub decl_text { my $varref = $Vars{$var}; if ($Opt_Sc) { (!$varref->{signed}) or die "%Error: No signed SystemC yet\n"; - my $type = (( ($varref->{val}->Size == 32) && "uint32_t") - || (($varref->{val}->Size == 64) && "uint64_t")); + my $type = (( ($varref->{val}->Size == 32) && "sc_dt::uint32") + || (($varref->{val}->Size == 64) && "sc_dt::uint64")); $type or die "%Error: Unknown Size ".$varref->{val}->Size,","; return sprintf " %s<%s> %s; //=%s" , $decl_with, $type, $var, $varref->{val}->to_Hex; @@ -893,8 +893,8 @@ sub countones { } -sub VLOGNOT { $_[0]{val} = makebool(($_[1]->is_empty)?1:0); } -sub VUNARYMIN { $_[0]{val} = my $o = newsized($_[1]); $o->Negate($_[1]); } +sub VLOGNOT { $_[0]{val} = makebool(($_[1]->is_empty)?1:0); } +sub VNEGATE { $_[0]{val} = my $o = newsized($_[1]); $o->Negate($_[1]); } sub VCOUNTONES { $_[0]{val} = Bit::Vector->new_Dec(32,countones($_[1])); } sub VONEHOT { $_[0]{val} = makebool((countones($_[1])==1)?1:0); } sub VONEHOT0 { $_[0]{val} = makebool((countones($_[1])<=1)?1:0); }