From 55906486d868afab6d4824edca98144ec47dd87b Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 24 Jul 2011 15:01:51 -0400 Subject: [PATCH] Support 'real' numbers and related functions. --- Changes | 2 + bin/verilator | 15 +- src/Makefile_obj.in | 1 - src/V3Ast.h | 7 +- src/V3AstNodes.cpp | 1 + src/V3AstNodes.h | 283 ++++++- src/V3Case.cpp | 8 +- src/V3Const.cpp | 7 +- src/V3EmitC.cpp | 10 +- src/V3Error.h | 4 +- src/V3Link.cpp | 2 +- src/V3LinkJump.cpp | 6 +- src/V3Signed.cpp | 451 ---------- src/V3Signed.h | 40 - src/V3Width.cpp | 1056 ++++++++++++++++++------ src/V3WidthCommit.h | 31 + src/Verilator.cpp | 4 - src/verilog.l | 13 +- src/verilog.y | 29 +- test_regress/t/t_display_real.pl | 39 + test_regress/t/t_display_real.v | 37 + test_regress/t/t_display_real_noopt.pl | 42 + test_regress/t/t_dpi_import.v | 14 +- test_regress/t/t_func_bad.pl | 2 - test_regress/t/t_lint_realcvt_bad.pl | 23 + test_regress/t/t_lint_realcvt_bad.v | 11 + test_regress/t/t_math_real.pl | 18 + test_regress/t/t_math_real.v | 136 +++ test_regress/t/t_sys_sformat.v | 11 + test_regress/t/t_trace_ena.v | 2 + test_regress/t/t_var_types.v | 4 +- 31 files changed, 1502 insertions(+), 807 deletions(-) delete mode 100644 src/V3Signed.cpp delete mode 100644 src/V3Signed.h create mode 100755 test_regress/t/t_display_real.pl create mode 100644 test_regress/t/t_display_real.v create mode 100755 test_regress/t/t_display_real_noopt.pl create mode 100755 test_regress/t/t_lint_realcvt_bad.pl create mode 100644 test_regress/t/t_lint_realcvt_bad.v create mode 100755 test_regress/t/t_math_real.pl create mode 100644 test_regress/t/t_math_real.v diff --git a/Changes b/Changes index 5a73aa281..d75f574f7 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,8 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.814**** +** Support 'real' numbers and related functions. + *** Support 'const' variables in limited cases; similar to enums. [Alex Solomatnikov] *** Support disable for loop escapes. diff --git a/bin/verilator b/bin/verilator index 27c0df5de..bcfbfb2a0 100755 --- a/bin/verilator +++ b/bin/verilator @@ -930,8 +930,8 @@ Enables the specified warning message. Enable all lint related warning messages (note by default they are already enabled), but do not affect style messages. This is equivalent to "-Wwarn-CASEINCOMPLETE -Wwarn-CASEOVERLAP -Wwarn-CASEX -Wwarn-CASEWITHX --Wwarn-CMPCONST -Wwarn-IMPLICIT -Wwarn-LITENDIAN -Wwarn-UNSIGNED --Wwarn-WIDTH". +-Wwarn-CMPCONST -Wwarn-IMPLICIT -Wwarn-LITENDIAN -Wwarn-REALCVT +-Wwarn-UNSIGNED -Wwarn-WIDTH". =item -Wwarn-style @@ -2096,7 +2096,7 @@ that Verilator will print a list of known scopes to help your debugging. =head2 Floating Point -Floating Point numbers are not synthesizable, and so not supported. +Floating Point (real) numbers are supported. =head2 Latches @@ -2336,10 +2336,6 @@ Read memory commands should work properly. Note Verilator and the Verilog specification does not include support for readmem to multi-dimensional arrays. -=item $realtime - -Treated as $time. - =item $test$plusargs, $value$plusargs Supported, but the instantiating C++/SystemC testbench must call @@ -2619,6 +2615,11 @@ not really needed. The best solution is to insure that each module is in a unique file by the same name. Otherwise, make sure all library files are read in as libraries with -v, instead of automatically with -y. +=item REALCVT + +Warns that a real number is being implicitly rounded to an integer, with +possible loss of precision. + =item REDEFMACRO Warns that you have redefined the same macro with a different value, for diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 79331ea1f..869630e97 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -209,7 +209,6 @@ RAW_OBJS = \ V3PreShell.o \ V3Premit.o \ V3Scope.o \ - V3Signed.o \ V3Slice.o \ V3Split.o \ V3SplitAs.o \ diff --git a/src/V3Ast.h b/src/V3Ast.h index 0d0e94ef2..cc7b6292c 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -817,6 +817,8 @@ public: static int instrCountLd() { return 2; } ///< Instruction cycles to load memory static int instrCountMul() { return 3; } ///< Instruction cycles to multiply integers static int instrCountPli() { return 20; } ///< Instruction cycles to call pli routines + static int instrCountDouble() { return 8; } ///< Instruction cycles to convert or do floats + static int instrCountDoubleDiv() { return 40; } ///< Instruction cycles to divide floats static int instrCountCall() { return instrCountBranch()+10; } ///< Instruction cycles to call subroutine static int instrCountTime() { return instrCountCall()+5; } ///< Instruction cycles to determine simulation time @@ -1323,7 +1325,6 @@ private: string m_name; // Name of task string m_cname; // Name of task if DPI import bool m_taskPublic:1; // Public task - bool m_didSigning:1; // V3Signed completed; can skip iteration bool m_attrIsolateAssign:1;// User isolate_assignments attribute bool m_prototype:1; // Just a prototype bool m_dpiExport:1; // DPI exported @@ -1334,7 +1335,7 @@ private: public: AstNodeFTask(FileLine* fileline, const string& name, AstNode* stmtsp) : AstNode(fileline) - , m_name(name), m_taskPublic(false), m_didSigning(false) + , m_name(name), m_taskPublic(false) , m_attrIsolateAssign(false), m_prototype(false) , m_dpiExport(false), m_dpiImport(false), m_dpiContext(false) , m_dpiTask(false), m_pure(false) { @@ -1361,8 +1362,6 @@ public: void scopeNamep(AstNode* nodep) { setNOp4p(nodep); } void taskPublic(bool flag) { m_taskPublic=flag; } bool taskPublic() const { return m_taskPublic; } - void didSigning(bool flag) { m_didSigning=flag; } - bool didSigning() const { return m_didSigning; } void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; } bool attrIsolateAssign() const { return m_attrIsolateAssign; } void prototype(bool flag) { m_prototype = flag; } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 8deec5589..43566e311 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -503,6 +503,7 @@ void AstNode::dump(ostream& os) { <<((editCount()>=editCountLast())?"#>":">") <<" {"<lineno()<<"}" <<" "<<(isSigned()?"s":"") + <<(isDouble()?"d":"") <<"w"<<(widthSized()?"":"u")< 1 && !isOpaque()) rangep = new AstRange(fileline(), keyword().width()-1, 0); @@ -278,7 +285,8 @@ public: AstRange* rangep() const { return op1p()->castRange(); } // op1 = Range of variable void rangep(AstRange* nodep) { setNOp1p(nodep); } void setSignedState(AstSignedState signst) { - if (signst!=signedst_NOP) isSigned(signst==signedst_SIGNED); + if (signst==signedst_UNSIGNED) numeric(AstNumeric::UNSIGNED); + else if (signst==signedst_SIGNED) numeric(AstNumeric::SIGNED); } // METHODS virtual AstBasicDType* basicp() const { return (AstBasicDType*)this; } // (Slow) recurse down to find basic data type @@ -581,8 +589,6 @@ private: bool m_attrClockEn:1;// User clock enable attribute bool m_attrIsolateAssign:1;// User isolate_assignments attribute bool m_attrSFormat:1;// User sformat attribute - bool m_didSigning:1; // V3Signed completed; can skip iteration - bool m_didWidth:1; // V3Width completed; can skip iteration bool m_fileDescr:1; // File descriptor bool m_isConst:1; // Table contains constant data bool m_isStatic:1; // Static variable @@ -596,7 +602,6 @@ private: m_sigPublic=false; m_sigModPublic=false; m_sigUserRdPublic=false; m_sigUserRWPublic=false; m_funcLocal=false; m_funcReturn=false; m_attrClockEn=false; m_attrIsolateAssign=false; m_attrSFormat=false; - m_didSigning=false; m_didWidth=false; m_fileDescr=false; m_isConst=false; m_isStatic=false; m_trace=false; } @@ -607,6 +612,7 @@ public: init(); combineType(type); setOp1p(dtypep); if (dtypep && dtypep->basicp()) { + numericFrom(dtypep); width(dtypep->basicp()->width(), 0); } else width(1, 0); } @@ -626,6 +632,7 @@ public: if (examplep->dtypep()) { setOp1p(examplep->dtypep()->cloneTree(true)); } + numericFrom(examplep); width(examplep->width(), examplep->widthMin()); } ASTNODE_NODE_FUNCS(Var, VAR) @@ -659,10 +666,6 @@ public: void attrScClocked(bool flag) { m_scClocked = flag; } void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; } void attrSFormat(bool flag) { m_attrSFormat = flag; } - void didSigning(bool flag) { m_didSigning=flag; } - bool didSigning() const { return m_didSigning; } - void didWidth(bool flag) { m_didWidth=flag; } - bool didWidth() const { return m_didWidth; } void usedClock(bool flag) { m_usedClock = flag; } void usedParam(bool flag) { m_usedParam = flag; } void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; } @@ -1454,6 +1457,15 @@ struct AstCondBound : public AstNodeCond { ASTNODE_NODE_FUNCS(CondBound, CONDBOUND) }; +struct AstCondD : public AstNodeCond { + // Conditional ?: statement, double rhs/lhs/out + // Parents: MATH + // Children: MATH + AstCondD(FileLine* fl, AstNode* condp, AstNode* expr1p, AstNode* expr2p) + : AstNodeCond(fl, condp, expr1p, expr2p) { numeric(AstNumeric::DOUBLE); } + ASTNODE_NODE_FUNCS(CondD, CONDD) +}; + struct AstCoverDecl : public AstNodeStmt { // Coverage analysis point declaration // Parents: {statement list} @@ -2447,6 +2459,20 @@ struct AstTime : public AstNodeTermop { virtual bool same(AstNode* samep) const { return true; } }; +struct AstTimeD : public AstNodeTermop { + AstTimeD(FileLine* fl) : AstNodeTermop(fl) { + numeric(AstNumeric::DOUBLE); } + ASTNODE_NODE_FUNCS(TimeD, TIMED) + virtual string emitVerilog() { return "%f$realtime"; } + virtual string emitC() { return "VL_TIME_D()"; } + virtual bool cleanOut() { return true; } + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual int instrCount() const { return instrCountTime(); } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } +}; + struct AstUCFunc : public AstNodeMath { // User's $c function // Perhaps this should be a AstNodeListop; but there's only one list math right now @@ -2482,9 +2508,22 @@ struct AstNegate : public AstNodeUniop { virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} virtual bool sizeMattersLhs() {return true;} }; +struct AstNegateD : public AstNodeUniop { + AstNegateD(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + numeric(AstNumeric::DOUBLE); } + ASTNODE_NODE_FUNCS(NegateD, NEGATED) + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opNegateD(lhs); } + virtual string emitVerilog() { return "%f(- %l)"; } + virtual string emitC() { V3ERROR_NA; return ""; } + virtual string emitSimpleOperator() { return "-"; } + virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return false;} + virtual bool sizeMattersLhs() {return false;} + virtual int instrCount() const { return instrCountDouble(); } + virtual bool doubleFlavor() const { return true; } +}; struct AstRedAnd : public AstNodeUniop { AstRedAnd(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - width(1,1); } + width(1,1); numeric(AstNumeric::UNSIGNED); } ASTNODE_NODE_FUNCS(RedAnd, REDAND) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRedAnd(lhs); } virtual string emitVerilog() { return "%f(& %l)"; } @@ -2494,7 +2533,7 @@ struct AstRedAnd : public AstNodeUniop { }; struct AstRedOr : public AstNodeUniop { AstRedOr(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - width(1,1); } + width(1,1); numeric(AstNumeric::UNSIGNED); } ASTNODE_NODE_FUNCS(RedOr, REDOR) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRedOr(lhs); } virtual string emitVerilog() { return "%f(| %l)"; } @@ -2504,7 +2543,7 @@ struct AstRedOr : public AstNodeUniop { }; struct AstRedXor : public AstNodeUniop { AstRedXor(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - width(1,1); } + width(1,1); numeric(AstNumeric::UNSIGNED); } ASTNODE_NODE_FUNCS(RedXor, REDXOR) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRedXor(lhs); } virtual string emitVerilog() { return "%f(^ %l)"; } @@ -2518,7 +2557,7 @@ struct AstRedXor : public AstNodeUniop { struct AstRedXnor : public AstNodeUniop { // AstRedXnors are replaced with AstRedXors in V3Const. AstRedXnor(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - width(1,1); } + width(1,1); numeric(AstNumeric::UNSIGNED); } ASTNODE_NODE_FUNCS(RedXnor, REDXNOR) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRedXnor(lhs); } virtual string emitVerilog() { return "%f(~^ %l)"; } @@ -2576,7 +2615,7 @@ struct AstExtendS : public AstNodeUniop { struct AstSigned : public AstNodeUniop { // $signed(lhs) AstSigned(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - isSigned(true); + numeric(AstNumeric::SIGNED); } ASTNODE_NODE_FUNCS(Signed, SIGNED) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opAssign(lhs); out.isSigned(false); } @@ -2589,7 +2628,7 @@ struct AstSigned : public AstNodeUniop { struct AstUnsigned : public AstNodeUniop { // $unsigned(lhs) AstUnsigned(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - isSigned(false); + numeric(AstNumeric::UNSIGNED); } ASTNODE_NODE_FUNCS(Unsigned, UNSIGNED) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opAssign(lhs); out.isSigned(false); } @@ -2599,6 +2638,63 @@ struct AstUnsigned : public AstNodeUniop { virtual bool sizeMattersLhs() {return true;} // Eliminated before matters virtual int instrCount() const { return 0; } }; +struct AstRToIS : public AstNodeUniop { + // $rtoi(lhs) + AstRToIS(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + width(32,32); } + ASTNODE_NODE_FUNCS(RToIS, RTOIS) + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRToIS(lhs); } + virtual string emitVerilog() { return "%f$rtoi(%l)"; } + virtual string emitC() { return "VL_RTOI_I_D(%li)"; } + virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} // Eliminated before matters + virtual bool sizeMattersLhs() {return false;} // Eliminated before matters + virtual int instrCount() const { return instrCountDouble(); } +}; +struct AstRToIRoundS : public AstNodeUniop { + AstRToIRoundS(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + width(32,32); } + ASTNODE_NODE_FUNCS(RToIRoundS, RTOIROUNDS) + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRToIRoundS(lhs); } + virtual string emitVerilog() { return "%f$rtoi_rounded(%l)"; } + virtual string emitC() { return "VL_RTOIROUND_I_D(%li)"; } + virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} // Eliminated before matters + virtual bool sizeMattersLhs() {return false;} // Eliminated before matters + virtual int instrCount() const { return instrCountDouble(); } +}; +struct AstIToRD : public AstNodeUniop { + AstIToRD(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + numeric(AstNumeric::DOUBLE); } + ASTNODE_NODE_FUNCS(IToRD, ITORD) + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opIToRD(lhs); } + virtual string emitVerilog() { return "%f$itor(%l)"; } + virtual string emitC() { return "VL_ITOR_D_I(%li)"; } + virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} // Eliminated before matters + virtual bool sizeMattersLhs() {return false;} // Eliminated before matters + virtual int instrCount() const { return instrCountDouble(); } +}; +struct AstRealToBits : public AstNodeUniop { + AstRealToBits(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + width(64,64); } + ASTNODE_NODE_FUNCS(RealToBits, REALTOBITS) + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRealToBits(lhs); } + virtual string emitVerilog() { return "%f$realtobits(%l)"; } + virtual string emitC() { return "VL_CVT_Q_D(%li)"; } + virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} // Eliminated before matters + virtual bool sizeMattersLhs() {return false;} // Eliminated before matters + virtual int instrCount() const { return instrCountDouble(); } +}; +struct AstBitsToRealD : public AstNodeUniop { + AstBitsToRealD(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + numeric(AstNumeric::DOUBLE); } + ASTNODE_NODE_FUNCS(BitsToRealD, BITSTOREALD) + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opBitsToRealD(lhs); } + virtual string emitVerilog() { return "%f$bitstoreal(%l)"; } + virtual string emitC() { return "VL_CVT_D_Q(%li)"; } + virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} // Eliminated before matters + virtual bool sizeMattersLhs() {return false;} // Eliminated before matters + virtual int instrCount() const { return instrCountDouble(); } +}; + struct AstCLog2 : public AstNodeUniop { AstCLog2(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) {} ASTNODE_NODE_FUNCS(CLog2, CLOG2) @@ -2851,6 +2947,20 @@ struct AstEq : public AstNodeBiCom { virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} }; +struct AstEqD : public AstNodeBiCom { + AstEqD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { + width(1,1); } + ASTNODE_NODE_FUNCS(EqD, EQD) + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opEqD(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %f== %r)"; } + virtual string emitC() { V3ERROR_NA; return ""; } + virtual string emitSimpleOperator() { return "=="; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return instrCountDouble(); } + virtual bool doubleFlavor() const { return true; } +}; struct AstNeq : public AstNodeBiCom { AstNeq(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { width(1,1); } @@ -2863,6 +2973,20 @@ struct AstNeq : public AstNodeBiCom { virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} }; +struct AstNeqD : public AstNodeBiCom { + AstNeqD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { + width(1,1); } + ASTNODE_NODE_FUNCS(NeqD, NEQD) + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opNeqD(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %f!= %r)"; } + virtual string emitC() { V3ERROR_NA; return ""; } + virtual string emitSimpleOperator() { return "!="; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return instrCountDouble(); } + virtual bool doubleFlavor() const { return true; } +}; struct AstLt : public AstNodeBiop { AstLt(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { width(1,1); } @@ -2875,6 +2999,20 @@ struct AstLt : public AstNodeBiop { virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} }; +struct AstLtD : public AstNodeBiop { + AstLtD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + width(1,1); } + ASTNODE_NODE_FUNCS(LtD, LTD) + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLtD(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %f< %r)"; } + virtual string emitC() { V3ERROR_NA; return ""; } + virtual string emitSimpleOperator() { return "<"; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return instrCountDouble(); } + virtual bool doubleFlavor() const { return true; } +}; struct AstLtS : public AstNodeBiop { AstLtS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { width(1,1); } @@ -2900,6 +3038,20 @@ struct AstGt : public AstNodeBiop { virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} }; +struct AstGtD : public AstNodeBiop { + AstGtD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + width(1,1); } + ASTNODE_NODE_FUNCS(GtD, GTD) + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGtD(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %f> %r)"; } + virtual string emitC() { V3ERROR_NA; return ""; } + virtual string emitSimpleOperator() { return ">"; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return instrCountDouble(); } + virtual bool doubleFlavor() const { return true; } +}; struct AstGtS : public AstNodeBiop { AstGtS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { width(1,1); } @@ -2925,6 +3077,20 @@ struct AstGte : public AstNodeBiop { virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} }; +struct AstGteD : public AstNodeBiop { + AstGteD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + width(1,1); } + ASTNODE_NODE_FUNCS(GteD, GTED) + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGteD(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %f>= %r)"; } + virtual string emitC() { V3ERROR_NA; return ""; } + virtual string emitSimpleOperator() { return ">="; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return instrCountDouble(); } + virtual bool doubleFlavor() const { return true; } +}; struct AstGteS : public AstNodeBiop { AstGteS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { width(1,1); } @@ -2950,6 +3116,20 @@ struct AstLte : public AstNodeBiop { virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} }; +struct AstLteD : public AstNodeBiop { + AstLteD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + width(1,1); } + ASTNODE_NODE_FUNCS(LteD, LTED) + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLteD(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %f<= %r)"; } + virtual string emitC() { V3ERROR_NA; return ""; } + virtual string emitSimpleOperator() { return "<="; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return instrCountDouble(); } + virtual bool doubleFlavor() const { return true; } +}; struct AstLteS : public AstNodeBiop { AstLteS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { width(1,1); } @@ -3018,6 +3198,20 @@ struct AstAdd : public AstNodeBiComAsv { virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} }; +struct AstAddD : public AstNodeBiComAsv { + AstAddD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { + numeric(AstNumeric::DOUBLE); } + ASTNODE_NODE_FUNCS(AddD, ADDD) + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opAddD(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %f+ %r)"; } + virtual string emitC() { V3ERROR_NA; return ""; } + virtual string emitSimpleOperator() { return "+"; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return instrCountDouble(); } + virtual bool doubleFlavor() const { return true; } +}; struct AstSub : public AstNodeBiop { AstSub(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { if (lhsp) widthSignedFrom(lhsp); } @@ -3030,6 +3224,20 @@ struct AstSub : public AstNodeBiop { virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} }; +struct AstSubD : public AstNodeBiop { + AstSubD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + numeric(AstNumeric::DOUBLE); } + ASTNODE_NODE_FUNCS(SubD, SUBD) + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opSubD(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %f- %r)"; } + virtual string emitC() { V3ERROR_NA; return ""; } + virtual string emitSimpleOperator() { return "-"; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return instrCountDouble(); } + virtual bool doubleFlavor() const { return true; } +}; struct AstMul : public AstNodeBiComAsv { AstMul(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { if (lhsp) widthSignedFrom(lhsp); } @@ -3043,6 +3251,20 @@ struct AstMul : public AstNodeBiComAsv { virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} virtual int instrCount() const { return widthInstrs()*instrCountMul(); } }; +struct AstMulD : public AstNodeBiComAsv { + AstMulD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { + numeric(AstNumeric::DOUBLE); } + ASTNODE_NODE_FUNCS(MulD, MULD) + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opMulD(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %f* %r)"; } + virtual string emitC() { V3ERROR_NA; return ""; } + virtual string emitSimpleOperator() { return "*"; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} + virtual int instrCount() const { return instrCountDouble(); } + virtual bool doubleFlavor() const { return true; } +}; struct AstMulS : public AstNodeBiComAsv { AstMulS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { if (lhsp) widthSignedFrom(lhsp); } @@ -3069,6 +3291,20 @@ struct AstDiv : public AstNodeBiop { virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} virtual int instrCount() const { return widthInstrs()*instrCountDiv(); } }; +struct AstDivD : public AstNodeBiop { + AstDivD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + numeric(AstNumeric::DOUBLE); } + ASTNODE_NODE_FUNCS(DivD, DIVD) + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opDivD(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %f/ %r)"; } + virtual string emitC() { V3ERROR_NA; return ""; } + virtual string emitSimpleOperator() { return "/"; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return instrCountDoubleDiv(); } + virtual bool doubleFlavor() const { return true; } +}; struct AstDivS : public AstNodeBiop { AstDivS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { if (lhsp) widthSignedFrom(lhsp); } @@ -3119,6 +3355,19 @@ struct AstPow : public AstNodeBiop { virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return false;} virtual int instrCount() const { return widthInstrs()*instrCountMul(); } }; +struct AstPowD : public AstNodeBiop { + AstPowD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + numeric(AstNumeric::DOUBLE); } + ASTNODE_NODE_FUNCS(PowD, POWD) + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opPowD(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %f** %r)"; } + virtual string emitC() { return "pow(%li,%ri)"; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return instrCountDoubleDiv(); } + virtual bool doubleFlavor() const { return true; } +}; struct AstPowS : public AstNodeBiop { AstPowS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { if (lhsp) widthSignedFrom(lhsp); } diff --git a/src/V3Case.cpp b/src/V3Case.cpp index a20020c31..40aebfb6a 100644 --- a/src/V3Case.cpp +++ b/src/V3Case.cpp @@ -148,17 +148,19 @@ private: bool isCaseTreeFast(AstCase* nodep) { int width = 0; + bool opaque = false; m_caseItems = 0; m_caseNoOverlapsAllCovered = true; for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) { for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) { if (icondp->width() > width) width = icondp->width(); + if (icondp->isDouble()) opaque = true; if (!icondp->castConst()) width = CASE_BARF; // Can't parse; not a constant m_caseItems++; } } m_caseWidth = width; - if (width==0 || width > CASE_OVERLAP_WIDTH) { + if (width==0 || width > CASE_OVERLAP_WIDTH || opaque) { m_caseNoOverlapsAllCovered = false; return false; // Too wide for analysis } @@ -348,7 +350,9 @@ private: and1p = cexprp->cloneTree(false); and2p = icondp; } - AstEq* condp = new AstEq(itemp->fileline(), and1p, and2p); + AstNodeBiop* condp = (and1p->isDouble() + ? (new AstEqD(itemp->fileline(), and1p, and2p))->castNodeBiop() + : (new AstEq(itemp->fileline(), and1p, and2p))->castNodeBiop()); if (!ifexprp) { ifexprp = condp; } else { diff --git a/src/V3Const.cpp b/src/V3Const.cpp index b43b8bc99..f5e7cd08a 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -37,7 +37,6 @@ #include "V3Const.h" #include "V3Ast.h" #include "V3Width.h" -#include "V3Signed.h" #include "V3Simulate.h" //###################################################################### @@ -1783,17 +1782,23 @@ private: TREEOP ("AstXnor {operandsSame($lhsp,,$rhsp)}", "replaceAllOnes(nodep)"); TREEOP ("AstXor {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); TREEOP ("AstEq {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); // We let X==X -> 1, although in a true 4-state sim it's X. + TREEOP ("AstEqD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); // We let X==X -> 1, although in a true 4-state sim it's X. TREEOP ("AstEqCase {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); TREEOP ("AstEqWild {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); TREEOP ("AstGt {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstGtD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); TREEOP ("AstGtS {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); TREEOP ("AstGte {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); + TREEOP ("AstGteD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); TREEOP ("AstGteS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); TREEOP ("AstLt {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstLtD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); TREEOP ("AstLtS {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); TREEOP ("AstLte {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); + TREEOP ("AstLteD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); TREEOP ("AstLteS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); TREEOP ("AstNeq {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstNeqD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); TREEOP ("AstNeqCase{operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); TREEOP ("AstNeqWild{operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); TREEOP ("AstLogAnd {operandsSame($lhsp,,$rhsp), $lhsp.width1}", "replaceWLhs(nodep)"); diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 0cf580ce5..202d115fc 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -545,6 +545,8 @@ public: ofp()->printf(",0x%08" VL_PRI64 "x", (vluint64_t)(nodep->num().dataWord(word))); } ofp()->printf(",0x%08" VL_PRI64 "x)", (vluint64_t)(nodep->num().dataWord(0))); + } else if (nodep->isDouble()) { + ofp()->printf("%g", nodep->num().toDouble()); } else if (nodep->isQuad()) { vluint64_t num = nodep->toUQuad(); if (num<10) ofp()->printf("VL_ULL(%" VL_PRI64 "d)", num); @@ -2013,7 +2015,9 @@ class EmitCTrace : EmitCStmts { return varp->isSc() && varp->isScBv(); } void emitTraceInitOne(AstTraceDecl* nodep) { - if (nodep->isWide()) { + if (nodep->isDouble()) { + puts("vcdp->declDouble"); + } else if (nodep->isWide()) { puts("vcdp->declArray"); } else if (nodep->isQuad()) { puts("vcdp->declQuad "); @@ -2042,7 +2046,9 @@ class EmitCTrace : EmitCStmts { string full = ((m_funcp->funcType() == AstCFuncType::TRACE_FULL || m_funcp->funcType() == AstCFuncType::TRACE_FULL_SUB) ? "full":"chg"); - if (nodep->isWide() || emitTraceIsScBv(nodep)) { + if (nodep->isDouble()) { + puts("vcdp->"+full+"Double"); + } else if (nodep->isWide() || emitTraceIsScBv(nodep)) { puts("vcdp->"+full+"Array"); } else if (nodep->isQuad()) { puts("vcdp->"+full+"Quad "); diff --git a/src/V3Error.h b/src/V3Error.h index a58d9470e..848d5a2aa 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -73,6 +73,7 @@ public: LITENDIAN, // Little bit endian vector MODDUP, // Duplicate module MULTIDRIVEN, // Driven from multiple blocks + REALCVT, // Real conversion REDEFMACRO, // Redefining existing define macro STMTDLY, // Delayed statement SYMRSVDWORD, // Symbol is Reserved Word @@ -111,7 +112,7 @@ public: "IFDEPTH", "IMPERFECTSCH", "IMPLICIT", "IMPURE", "INCABSPATH", "LITENDIAN", "MODDUP", "MULTIDRIVEN", - "REDEFMACRO", + "REALCVT", "REDEFMACRO", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNSIGNED", "UNUSED", "VARHIDDEN", "WIDTH", "WIDTHCONCAT", @@ -135,6 +136,7 @@ public: || m_e==CMPCONST || m_e==IMPLICIT || m_e==LITENDIAN + || m_e==REALCVT || m_e==UNSIGNED || m_e==WIDTH); } // Warnings that are style only diff --git a/src/V3Link.cpp b/src/V3Link.cpp index 6b20c5288..1aad1e5ef 100644 --- a/src/V3Link.cpp +++ b/src/V3Link.cpp @@ -428,7 +428,7 @@ private: if (dtypep) dtypep->unlinkFrBack(); else dtypep = new AstBasicDType(nodep->fileline(), AstBasicDTypeKwd::LOGIC); AstVar* newvarp = new AstVar(nodep->fileline(), AstVarType::OUTPUT, nodep->name(), dtypep); - newvarp->isSigned(nodep->isSigned()); + if (nodep->isSigned()) newvarp->numeric(AstNumeric::SIGNED); newvarp->funcReturn(true); newvarp->trace(false); // Not user visible newvarp->attrIsolateAssign(nodep->attrIsolateAssign()); diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index 4f73b4321..b337c46b9 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -135,8 +135,8 @@ private: // Spec says value is integral, if negative is ignored AstVar* varp = new AstVar(nodep->fileline(), AstVarType::BLOCKTEMP, name, AstLogicPacked(), 32); - varp->isSigned(true); - varp->dtypep()->isSigned(true); + varp->numeric(AstNumeric::SIGNED); + varp->dtypep()->numeric(AstNumeric::SIGNED); varp->usedLoopIdx(true); m_modp->addStmtp(varp); AstNode* initsp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, true), @@ -144,7 +144,7 @@ private: AstNode* decp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, true), new AstSub(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, false), new AstConst(nodep->fileline(), 1))); - AstNode* zerosp = new AstConst(nodep->fileline(), 0); zerosp->isSigned(true); + AstNode* zerosp = new AstConst(nodep->fileline(), 0); zerosp->numeric(AstNumeric::SIGNED); AstNode* condp = new AstGtS(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, false), zerosp); AstNode* bodysp = nodep->bodysp(); if (bodysp) bodysp->unlinkFrBackWithNext(); diff --git a/src/V3Signed.cpp b/src/V3Signed.cpp deleted file mode 100644 index 8cf56b093..000000000 --- a/src/V3Signed.cpp +++ /dev/null @@ -1,451 +0,0 @@ -//************************************************************************* -// DESCRIPTION: Verilator: Signed/unsigned resolution -// -// Code available from: http://www.veripool.org/verilator -// -// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli -// -//************************************************************************* -// -// Copyright 2005-2011 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. -// -//************************************************************************* -// Signedness depends on: -// Decimal numbers are signed -// Based numbers are unsigned unless 's' prefix -// Comparison results are unsigned -// Bit&Part selects are unsigned, even if whole -// Concatenates are unsigned -// Ignore signedness of self-determined: -// shift rhs, ** rhs, x?: lhs, concat and replicate members -// Else, if any operand unsigned, output unsigned -//************************************************************************* - -#include "config_build.h" -#include "verilatedos.h" -#include -#include -#include -#include -#include - -#include "V3Global.h" -#include "V3Signed.h" -#include "V3Ast.h" - -//###################################################################### -// Signed class functions - -class SignedVisitor : public AstNVisitor { -private: - // NODE STATE/TYPES - // STATE - bool m_paramsOnly; // Computing parameter value; limit operation - - // METHODS - special type detection - bool backRequiresUnsigned(AstNode* nodep) { - // The spec doesn't state this, but if you have an array select where the selection - // index is NOT wide enough, you do not sign extend, but always zero extend. - return (nodep->castArraySel() - || nodep->castSel()); - } - - // VISITORS - //======== - // Signed: Output explicit by user, Lhs either - 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 - virtual void visit(AstSel* nodep, AstNUser*) { signed_Ou_Ix(nodep); } //See backRequiresUnsigned - virtual void visit(AstAttrOf* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstCountOnes* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstCLog2* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstPslBool* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstTime* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - // - virtual void visit(AstRedAnd* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstRedOr* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstRedXnor* nodep, AstNUser*){ signed_Ou_Ix(nodep); } - virtual void visit(AstRedXor* nodep,AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstIsUnknown* nodep,AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstOneHot* nodep,AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstOneHot0* nodep,AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstFEof* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstFGetC* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstFGetS* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstFScanF* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstSScanF* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstTestPlusArgs* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstValuePlusArgs* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - // - virtual void visit(AstConcat* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstReplicate* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstRange* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - // ... One presumes these are unsigned out, though the spec doesn't say - virtual void visit(AstLogNot* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstLogAnd* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstLogOr* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstLogIf* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstLogIff* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstBufIf1* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - // ... These shouldn't matter, just make unsigned - virtual void visit(AstScopeName* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstText* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstUCFunc* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - // ... These comparisons don't care about inbound types - // ... (Though they should match. We don't check.) - virtual void visit(AstEq* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstEqCase* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstEqWild* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstNeq* nodep, AstNUser*) { signed_Ou_Ix(nodep); } - virtual void visit(AstNeqCase* nodep, AstNUser*){ signed_Ou_Ix(nodep); } - virtual void visit(AstNeqWild* nodep, AstNUser*){ signed_Ou_Ix(nodep); } - // ... Opaque returns, so arbitrary - virtual void visit(AstCvtPackString* nodep, AstNUser*){ signed_Ou_Ix(nodep); } - - //======== - // Signed: Output signed - virtual void visit(AstRand* nodep, AstNUser*) { signed_Os_Ix(nodep); } - - //======= - // Signed: Output signed iff LHS signed; unary operator - virtual void visit(AstNot* 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); } - - // Signed: Output signed iff LHS signed; binary operator - // Note by contrast, bit extract selects are unsigned - virtual void visit(AstArraySel* nodep, AstNUser*) { signed_Olhs(nodep); } //See backRequiresUnsigned - - //======= - // Signed: Output signed iff LHS & RHS signed; binary operator - virtual void visit(AstAnd* nodep, AstNUser*) { signed_OlhsAndRhs(nodep); } - virtual void visit(AstOr* nodep, AstNUser*) { signed_OlhsAndRhs(nodep); } - virtual void visit(AstXnor* nodep, AstNUser*) { signed_OlhsAndRhs(nodep); } - virtual void visit(AstXor* nodep, AstNUser*) { signed_OlhsAndRhs(nodep); } - virtual void visit(AstSub* nodep, AstNUser*) { signed_OlhsAndRhs(nodep); } - virtual void visit(AstAdd* nodep, AstNUser*) { signed_OlhsAndRhs(nodep); } - - //======= - // Signed: Output signed iff RHS & THS signed - virtual void visit(AstNodeCond* nodep, AstNUser*) { signed_OrhsAndThs(nodep); } - - //======= - // These have proper signedness set when they were created. - virtual void visit(AstReturn* nodep, AstNUser*) { nodep->iterateChildren(*this); } - virtual void visit(AstNodeDType* nodep, AstNUser*) { nodep->iterateChildren(*this); } - - // Inherit from others - virtual void visit(AstVar* nodep, AstNUser*) { - // Avoid recursion; can't use user() as they're all full, and anyhow this is often called - if (nodep->didSigning()) return; - nodep->didSigning(true); - nodep->iterateChildren(*this); - nodep->numericFrom(nodep->dtypep()); - } - virtual void visit(AstNodeVarRef* nodep, AstNUser*) { - nodep->varp()->iterate(*this); - nodep->numericFrom(nodep->varp()); - } - virtual void visit(AstEnumItemRef* nodep, AstNUser*) { - nodep->itemp()->iterate(*this); - nodep->numericFrom(nodep->itemp()); - } - virtual void visit(AstCast* nodep, AstNUser*) { - nodep->lhsp()->iterate(*this); - nodep->dtypep()->iterate(*this); - nodep->numericFrom(nodep->dtypep()); - } - virtual void visit(AstConst* nodep, AstNUser*) { - // The node got setup with the signed state of the node. - // However a later operation may have changed the node->signed w/o changing - // the number's sign. So we don't: nodep->isSigned(nodep->num().isSigned()); - } - virtual void visit(AstNodeFTask* nodep, AstNUser*) { - // Avoid recursion; can't use user() as they're all full, and anyhow this is often called - if (nodep->didSigning()) return; - nodep->didSigning(true); - nodep->iterateChildren(*this); - if (nodep->fvarp()) { - nodep->numericFrom(nodep->fvarp()); // Which will get it from fvarp()->dtypep() - } - } - virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { - nodep->iterateChildren(*this); - if (nodep->taskp()) nodep->taskp()->iterate(*this); - nodep->numericFrom(nodep->taskp()); - } - virtual void visit(AstRefDType* nodep, AstNUser*) { - nodep->iterateChildren(*this); - if (nodep->defp()) nodep->defp()->iterate(*this); - nodep->numericFrom(nodep->skipRefp()); - } - virtual void visit(AstNodeIf* nodep, AstNUser*) { - if (!nodep->castGenIf()) { // for m_paramsOnly - nodep->ifsp()->iterateAndNext(*this); - nodep->elsesp()->iterateAndNext(*this); - } - nodep->condp()->iterateAndNext(*this); - } - virtual void visit(AstPin* nodep, AstNUser*) { - // Same as above taskref argument. - nodep->iterateChildren(*this); - } - - // VISITORS - Special - virtual void visit(AstSFormatF* nodep, AstNUser*) { - nodep->iterateChildren(*this); - // - UINFO(9," Display in "<text()<exprsp(); - for (const char* inp = nodep->text().c_str(); *inp; inp++) { - char ch = *inp; // Breaks with iterators... - if (!inPct && ch=='%') { - inPct = true; - } else if (inPct && isdigit(ch)) { - } else if (tolower(inPct)) { - inPct = false; - switch (tolower(ch)) { - case '%': break; // %% - just output a % - case 'm': break; // %m - auto insert "name" - case 'd': { // Convert decimal to either 'd' or 'u' - if (argp && argp->isSigned()) { // Convert it - ch = '~'; - } - if (argp) argp=argp->nextp(); - break; - } - default: // Most operators, just move to next argument - if (argp) argp=argp->nextp(); - break; - } // switch - } - dispout += ch; - } - nodep->text(dispout); - UINFO(9," Display out "<text()<isSigned() && !backRequiresUnsigned(nodep->backp())) { - replaceWithSignedVersion(nodep, new AstExtendS (nodep->fileline(), nodep->lhsp()->unlinkFrBack())); nodep=NULL; - } - } - virtual void visit(AstExtendS* nodep, AstNUser*) { - signed_Olhs(nodep); - if (!(nodep->isSigned() && !backRequiresUnsigned(nodep->backp()))) { - replaceWithSignedVersion(nodep, new AstExtend (nodep->fileline(), nodep->lhsp()->unlinkFrBack())); nodep=NULL; - } - } - - // Biop - virtual void visit(AstPow* nodep, AstNUser*) { - // Pow is special, output sign only depends on LHS sign - signed_Olhs(nodep); - if (nodep->isSigned()) { - replaceWithSignedVersion(nodep, new AstPowS (nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack())); nodep=NULL; - } - } - virtual void visit(AstPowS* nodep, AstNUser*) { - // Pow is special, output sign only depends on LHS sign - signed_Olhs(nodep); - if (!nodep->isSigned()) { - replaceWithSignedVersion(nodep, new AstPow (nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack())); nodep=NULL; - } - } - - // These have different node types, as they operate differently - // Must add to case statement below, - virtual void visit(AstGt* nodep, AstNUser*) { checkReplace_Ou_FlavLhsAndRhs(nodep); } - virtual void visit(AstGtS* nodep, AstNUser*) { checkReplace_Ou_FlavLhsAndRhs(nodep); } - virtual void visit(AstGte* nodep, AstNUser*) { checkReplace_Ou_FlavLhsAndRhs(nodep); } - virtual void visit(AstGteS* nodep, AstNUser*) { checkReplace_Ou_FlavLhsAndRhs(nodep); } - virtual void visit(AstLt* nodep, AstNUser*) { checkReplace_Ou_FlavLhsAndRhs(nodep); } - virtual void visit(AstLtS* nodep, AstNUser*) { checkReplace_Ou_FlavLhsAndRhs(nodep); } - virtual void visit(AstLte* nodep, AstNUser*) { checkReplace_Ou_FlavLhsAndRhs(nodep); } - virtual void visit(AstLteS* nodep, AstNUser*) { checkReplace_Ou_FlavLhsAndRhs(nodep); } - // Need replacements; output matches input sign - virtual void visit(AstDiv* nodep, AstNUser*) { checkReplace_OlhsAndRhs(nodep); } - virtual void visit(AstDivS* nodep, AstNUser*) { checkReplace_OlhsAndRhs(nodep); } - virtual void visit(AstModDiv* nodep, AstNUser*) { checkReplace_OlhsAndRhs(nodep); } - virtual void visit(AstModDivS* nodep, AstNUser*) { checkReplace_OlhsAndRhs(nodep); } - virtual void visit(AstMul* nodep, AstNUser*) { checkReplace_OlhsAndRhs(nodep); } - virtual void visit(AstMulS* nodep, AstNUser*) { checkReplace_OlhsAndRhs(nodep); } - // 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 - signed_Ou_Ix(nodep); - checkReplace(nodep, nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); - } - void checkReplace_Olhs(AstNodeBiop* nodep) { - signed_Olhs(nodep); - checkReplace(nodep, nodep->isSigned()); - } - void checkReplace_OlhsAndRhs(AstNodeBiop* nodep) { - signed_OlhsAndRhs(nodep); - checkReplace(nodep, nodep->isSigned()); - } - - void checkReplace(AstNodeBiop* nodep, bool signedFlavorNeeded) { - if (signedFlavorNeeded != nodep->signedFlavor()) { - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); - AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - AstNode* newp = NULL; - // Given a signed/unsigned node type, create the opposite type - switch (nodep->type()) { - case AstType::atGT: newp = new AstGtS (nodep->fileline(), lhsp, rhsp); break; - case AstType::atGTS: newp = new AstGt (nodep->fileline(), lhsp, rhsp); break; - case AstType::atGTE: newp = new AstGteS (nodep->fileline(), lhsp, rhsp); break; - case AstType::atGTES: newp = new AstGte (nodep->fileline(), lhsp, rhsp); break; - case AstType::atLT: newp = new AstLtS (nodep->fileline(), lhsp, rhsp); break; - case AstType::atLTS: newp = new AstLt (nodep->fileline(), lhsp, rhsp); break; - case AstType::atLTE: newp = new AstLteS (nodep->fileline(), lhsp, rhsp); break; - case AstType::atLTES: newp = new AstLte (nodep->fileline(), lhsp, rhsp); break; - case AstType::atDIV: newp = new AstDivS (nodep->fileline(), lhsp, rhsp); break; - case AstType::atDIVS: newp = new AstDiv (nodep->fileline(), lhsp, rhsp); break; - case AstType::atMODDIV: newp = new AstModDivS (nodep->fileline(), lhsp, rhsp); break; - case AstType::atMODDIVS: newp = new AstModDiv (nodep->fileline(), lhsp, rhsp); break; - case AstType::atMUL: newp = new AstMulS (nodep->fileline(), lhsp, rhsp); break; - case AstType::atMULS: newp = new AstMul (nodep->fileline(), lhsp, rhsp); break; - case AstType::atSHIFTRS: newp = new AstShiftR (nodep->fileline(), lhsp, rhsp); break; - default: - nodep->v3fatalSrc("Node needs sign change, but bad case: "<iterateChildren(*this); - nodep->isSigned(true); - } - // Signed: Output unsigned, Lhs/Rhs/etx either - void signed_Ou_Ix(AstNode* nodep) { - nodep->iterateChildren(*this); - nodep->isSigned(false); - } - // Signed: Output signed iff LHS signed; unary operator - void signed_Olhs(AstNodeUniop* nodep) { - nodep->iterateChildren(*this); - nodep->isSigned(nodep->lhsp()->isSigned()); - } - // Signed: Output signed iff LHS signed; binary operator - void signed_Olhs(AstNodeBiop* nodep) { - nodep->iterateChildren(*this); - nodep->isSigned(nodep->lhsp()->isSigned()); - } - // Signed: Output signed iff LHS signed; select operator - void signed_Olhs(AstSel* nodep) { - nodep->iterateChildren(*this); - nodep->isSigned(nodep->fromp()->isSigned()); - } - // Signed: Output signed iff LHS & RHS signed; binary operator - void signed_OlhsAndRhs(AstNodeBiop* nodep) { - nodep->iterateChildren(*this); - nodep->isSigned(nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); - } - // Signed: Output signed iff RHS & THS signed - void signed_OrhsAndThs(AstNodeTriop* nodep) { - nodep->iterateChildren(*this); - nodep->isSigned(nodep->rhsp()->isSigned() && nodep->thsp()->isSigned()); - } - void replaceWithSignedVersion(AstNode* nodep, AstNode* newp) { - UINFO(6," Replace "<replaceWith(newp); - newp->widthSignedFrom(nodep); - pushDeletep(nodep); nodep=NULL; - } - -public: - // CONSTRUCTORS - SignedVisitor(bool paramsOnly) { - m_paramsOnly = paramsOnly; - } - virtual ~SignedVisitor() {} - AstNode* mainAcceptEdit(AstNode* nodep) { - return nodep->acceptSubtreeReturnEdits(*this); - } -}; - -//###################################################################### -// Signed class functions -/// Remove all $signed, $unsigned, we're done with them. - -class SignedRemoveVisitor : public AstNVisitor { -private: - // VISITORS - virtual void visit(AstSigned* nodep, AstNUser*) { - replaceWithSignedVersion(nodep, nodep->lhsp()->unlinkFrBack()); nodep=NULL; - } - virtual void visit(AstUnsigned* nodep, AstNUser*) { - replaceWithSignedVersion(nodep, nodep->lhsp()->unlinkFrBack()); nodep=NULL; - } - virtual void visit(AstNode* nodep, AstNUser*) { - nodep->iterateChildren(*this); - } - void replaceWithSignedVersion(AstNode* nodep, AstNode* newp) { - UINFO(6," Replace "<replaceWith(newp); - newp->widthSignedFrom(nodep); - pushDeletep(nodep); nodep=NULL; - } -public: - // CONSTRUCTORS - SignedRemoveVisitor() {} - virtual ~SignedRemoveVisitor() {} - AstNode* mainAcceptEdit(AstNode* nodep) { - return nodep->acceptSubtreeReturnEdits(*this); - } -}; - -//###################################################################### -// Top Signed class - -void V3Signed::signedAll(AstNetlist* nodep) { - UINFO(2,__FUNCTION__<<": "<width(64,64); } - virtual void visit(AstTestPlusArgs* nodep, AstNUser*) { nodep->width(32,32); } + virtual void visit(AstTime* nodep, AstNUser*) { nodep->numeric(AstNumeric::UNSIGNED); nodep->width(64,64); } + virtual void visit(AstTimeD* nodep, AstNUser*) { nodep->numeric(AstNumeric::DOUBLE); } + virtual void visit(AstTestPlusArgs* nodep, AstNUser*) { nodep->numeric(AstNumeric::UNSIGNED); 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 + // Signed: Output signed iff RHS & THS signed + // Real: Output real if either expression is real, signed if both signed if (vup->c()->prelim()) { // First stage evaluation // Just once, do the conditional, expect one bit out. nodep->condp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p()); + spliceCvtCmpD0(nodep->condp()); // auto-compares with zero // Determine sub expression widths only relying on what's in the subops nodep->expr1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); nodep->expr2p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); @@ -200,11 +256,20 @@ private: // the size of this subexpression only. // Second call (final()) vup->c()->width() is probably the expression size, so // the expression includes the size of the output too. - int width = max(vup->c()->width(), max(nodep->expr1p()->width(), nodep->expr2p()->width())); - int mwidth = max(vup->c()->widthMin(), max(nodep->expr1p()->widthMin(), nodep->expr2p()->widthMin())); - nodep->width(width,mwidth); + if (nodep->expr1p()->isDouble() || nodep->expr2p()->isDouble()) { + spliceCvtD(nodep->expr1p()); + spliceCvtD(nodep->expr2p()); + nodep->numeric(AstNumeric::DOUBLE); + } else { + int width = max(vup->c()->width(), max(nodep->expr1p()->width(), nodep->expr2p()->width())); + int mwidth = max(vup->c()->widthMin(), max(nodep->expr1p()->widthMin(), nodep->expr2p()->widthMin())); + nodep->isSigned(nodep->expr1p()->isSigned() && nodep->expr2p()->isSigned()); + nodep->width(width,mwidth); + } if (vup->c()->final()) { // Final width known, so make sure children recompute & check their sizes + int width = nodep->width(); + int mwidth = nodep->widthMin(); nodep->expr1p()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); nodep->expr2p()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); // Error report and change sizes for suboperands of this node. @@ -214,11 +279,16 @@ private: } } virtual void visit(AstConcat* nodep, AstNUser* vup) { + // Real: Not allowed + // Signed: unsigned output, input either if (vup->c()->prelim()) { nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + checkCvtUS(nodep->lhsp()); + checkCvtUS(nodep->rhsp()); nodep->width(nodep->lhsp()->width() + nodep->rhsp()->width(), nodep->lhsp()->widthMin() + nodep->rhsp()->widthMin()); + nodep->numeric(AstNumeric::UNSIGNED); // Cleanup zero width Verilog2001 {x,{0{foo}}} now, // otherwise having width(0) will cause later assertions to fire if (AstReplicate* repp=nodep->lhsp()->castReplicate()) { @@ -247,6 +317,8 @@ private: if (vup->c()->prelim()) { nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + checkCvtUS(nodep->lhsp()); + checkCvtUS(nodep->rhsp()); V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change AstConst* constp = nodep->rhsp()->castConst(); if (!constp) { nodep->v3error("Replication value isn't a constant."); return; } @@ -254,6 +326,7 @@ private: if (times==0 && !nodep->backp()->castConcat()) { // Concat Visitor will clean it up. nodep->v3error("Replication value of 0 is only legal under a concatenation."); times=1; } + nodep->numeric(AstNumeric::UNSIGNED); nodep->width((nodep->lhsp()->width() * times), (nodep->lhsp()->widthMin() * times)); } @@ -266,12 +339,17 @@ 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 + // Real: Not allowed + // Signed: unsigned output, input either 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()); + checkCvtUS(nodep->msbp()); + checkCvtUS(nodep->lsbp()); int width = nodep->elementsConst(); if (width > (1<<28)) nodep->v3error("Width of bit range is huge; vector of over 1billion bits: 0x"<numeric(AstNumeric::UNSIGNED); nodep->width(width,width); if (nodep->littleEndian()) { nodep->v3warn(LITENDIAN,"Little bit endian vector: MSB < LSB of bit range: "<lsbConst()<<":"<msbConst()); @@ -280,11 +358,15 @@ private: } virtual void visit(AstSel* nodep, AstNUser* vup) { + // Signed: always unsigned; Real: Not allowed if (vup->c()->prelim()) { if (debug()>=9) nodep->dumpTree(cout,"-selWidth: "); nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); nodep->lsbp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); nodep->widthp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + checkCvtUS(nodep->fromp()); + checkCvtUS(nodep->lsbp()); + checkCvtUS(nodep->widthp()); V3Const::constifyParamsEdit(nodep->widthp()); // widthp may change AstConst* widthConstp = nodep->widthp()->castConst(); if (!widthConstp) { @@ -293,6 +375,7 @@ private: } int width = nodep->widthConst(); nodep->width(width,width); + nodep->numeric(AstNumeric::UNSIGNED); if (nodep->lsbp()->castConst() && nodep->msbConst() < nodep->lsbConst()) { nodep->v3error("Unsupported: MSB < LSB of bit extract: " @@ -354,8 +437,12 @@ private: } virtual void visit(AstArraySel* nodep, AstNUser* vup) { + // Signed/Real: Output signed iff LHS signed/real; binary operator + // Note by contrast, bit extract selects are unsigned if (vup->c()->prelim()) { nodep->bitp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + checkCvtUS(nodep->bitp()); + // nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); AstNode* basefromp = AstArraySel::baseFromp(nodep->fromp()); int dimension = AstArraySel::dimension(nodep->fromp()); @@ -372,6 +459,7 @@ private: if (fromlsb>frommsb) {int t=frommsb; frommsb=fromlsb; fromlsb=t; } // However, if the lsb<0 we may go negative, so need more bits! if (fromlsb < 0) frommsb += -fromlsb; + nodep->numericFrom(adtypep); nodep->width(outwidth,outwidth); // Width out = width of array } else { @@ -419,12 +507,15 @@ private: } virtual void visit(AstExtend* nodep, AstNUser* vup) { - // Only created by this process, so we know width from here down it is correct. + // Only created by this process, so we know width from here down is correct. } virtual void visit(AstExtendS* nodep, AstNUser* vup) { - // Only created by signing process, so we know width from here down it is correct. + // Only created by this process, so we know width from here down is correct. } virtual void visit(AstConst* nodep, AstNUser* vup) { + // The node got setup with the signed/real state of the node. + // However a later operation may have changed the node->signed w/o changing + // the number's sign. So we don't: nodep->isSigned(nodep->num().isSigned()); if (vup && vup->c()->prelim()) { if (nodep->num().sized()) { nodep->width(nodep->num().width(), nodep->num().width()); @@ -440,10 +531,12 @@ private: } virtual void visit(AstRand* nodep, AstNUser* vup) { if (vup->c()->prelim()) { + nodep->numeric(AstNumeric::SIGNED); // Says the spec nodep->width(32,32); // Says the spec } } virtual void visit(AstUCFunc* nodep, AstNUser* vup) { + nodep->numeric(AstNumeric::UNSIGNED); // If want otherwise use a dpi import // Give it the size the user wants. if (vup && vup->c()->prelim()) { nodep->width(32,1); // We don't care @@ -458,22 +551,53 @@ private: virtual void visit(AstCLog2* nodep, AstNUser* vup) { if (vup->c()->prelim()) { nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + checkCvtUS(nodep->lhsp()); + nodep->numeric(AstNumeric::UNSIGNED); // If want otherwise use a dpi import nodep->width(32,32); } } + virtual void visit(AstPow* nodep, AstNUser* vup) { + // Pow is special, output sign only depends on LHS sign + // Real if either side is real (as with AstAdd) + shift_prelim(nodep, vup); + if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) { + spliceCvtD(nodep->lhsp()); + spliceCvtD(nodep->rhsp()); + replaceWithDVersion(nodep); nodep=NULL; + } else { + AstNodeBiop* newp = shift_final(nodep, vup); nodep=NULL; + newp->isSigned(newp->lhsp()->isSigned()); + if (newp->isSigned()) { + replaceWithUOrSVersion(newp, false); newp=NULL; + } + } + } + virtual void visit(AstPowS* nodep, AstNUser* vup) { + // Pow is special, output sign only depends on LHS sign + shift_prelim(nodep, vup); + AstNodeBiop* newp = shift_final(nodep, vup); nodep=NULL; + newp->isSigned(newp->lhsp()->isSigned()); + if (!newp->isSigned()) { + replaceWithUOrSVersion(newp, true); newp=NULL; + } + } virtual void visit(AstCountOnes* nodep, AstNUser* vup) { if (vup->c()->prelim()) { nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + checkCvtUS(nodep->lhsp()); // If it's a 32 bit number, we need a 6 bit number as we need to return '32'. int selwidth = V3Number::log2b(nodep->lhsp()->width())+1; + nodep->numeric(AstNumeric::UNSIGNED); nodep->width(selwidth,selwidth); } } virtual void visit(AstCvtPackString* nodep, AstNUser* vup) { + // Opaque returns, so arbitrary nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); } virtual void visit(AstAttrOf* nodep, AstNUser*) { nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + nodep->numeric(AstNumeric::UNSIGNED); nodep->width(32,1); // Approximation, unsized 32 } virtual void visit(AstText* nodep, AstNUser* vup) { @@ -492,7 +616,7 @@ private: nodep->widthFrom(nodep->rangep()); } // else width in node is correct; it was set based on keyword().width() - // at construction time + // at construction time. Ditto signed, so "unsigned byte" etc works right. } virtual void visit(AstConstDType* nodep, AstNUser* vup) { nodep->iterateChildren(*this, vup); @@ -501,13 +625,14 @@ private: virtual void visit(AstRefDType* nodep, AstNUser* vup) { nodep->iterateChildren(*this, vup); if (nodep->defp()) nodep->defp()->iterate(*this,vup); - nodep->widthFrom(nodep->dtypeSkipRefp()); + nodep->widthSignedFrom(nodep->dtypeSkipRefp()); } virtual void visit(AstTypedef* nodep, AstNUser* vup) { nodep->iterateChildren(*this, vup); - nodep->widthFrom(nodep->dtypep()->skipRefp()); + nodep->widthSignedFrom(nodep->dtypep()->skipRefp()); } virtual void visit(AstCast* nodep, AstNUser* vup) { + //if (debug()) nodep->dumpTree(cout," CastPre: "); nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); nodep->dtypep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); // When more general casts are supported, the cast elimination will be done later. @@ -517,80 +642,118 @@ private: V3Width::widthParamsEdit(nodep->dtypep()); // MAY CHANGE dtypep() AstBasicDType* basicp = nodep->dtypep()->basicp(); if (!basicp) nodep->v3fatalSrc("Unimplemented: Casting non-simple data type"); nodep->widthSignedFrom(basicp); - widthCheck(nodep,"Cast",nodep->lhsp(),nodep->width(),nodep->width(),true); AstNode* newp = nodep->lhsp()->unlinkFrBack(); - if (basicp->isSigned()) { + if (!basicp->isDouble() && !newp->isDouble()) { + widthCheck(nodep,"Cast",newp,nodep->width(),nodep->width(),true); + } + if (basicp->numeric() == newp->numeric()) { + newp = newp; // Can just remove cast + } else if (basicp->isDouble() && !newp->isDouble()) { + newp = new AstIToRD(nodep->fileline(), newp); + } else if (!basicp->isDouble() && newp->isDouble()) { + if (basicp->isSigned()) { + newp = new AstRToIRoundS(nodep->fileline(), newp); + } else { + newp = new AstUnsigned(nodep->fileline(), + new AstRToIS(nodep->fileline(), newp)); + } + } else if (basicp->isSigned()) { newp = new AstSigned(nodep->fileline(), newp); } else { newp = new AstUnsigned(nodep->fileline(), newp); } nodep->replaceWith(newp); + pushDeletep(nodep); nodep=NULL; + //if (debug()) newp->dumpTree(cout," CastOut: "); } virtual void visit(AstVar* nodep, AstNUser* vup) { //if (debug()) nodep->dumpTree(cout," InitPre: "); // Must have deterministic constant width // We can't skip this step when width()!=0, as creating a AstVar // with non-constant range gets size 1, not size 0. So use didWidth(). - if (nodep->didWidth()) { // Early exit if have circular parameter definition - if (!nodep->width()) { - if (!nodep->valuep()) nodep->v3fatalSrc("circular, but without value"); - nodep->v3error("Variable's initial value is circular: "<prettyName()); - pushDeletep(nodep->valuep()->unlinkFrBack()); - nodep->valuep(new AstConst(nodep->fileline(), 1)); - } else { - return; - } + if (nodep->didWidth()) return; + if (nodep->doingWidth()) { // Early exit if have circular parameter definition + if (!nodep->valuep()) nodep->v3fatalSrc("circular, but without value"); + nodep->v3error("Variable's initial value is circular: "<prettyName()); + pushDeletep(nodep->valuep()->unlinkFrBack()); + nodep->valuep(new AstConst(nodep->fileline(), AstConst::LogicTrue())); + nodep->widthFrom(nodep->valuep()); + nodep->didWidth(true); + return; } - nodep->didWidth(true); - int width=1; int mwidth=1; + nodep->doingWidth(true); // Parameters if implicit untyped inherit from what they are assigned to AstBasicDType* bdtypep = nodep->dtypep()->castBasicDType(); - if (nodep->isParam() && bdtypep && bdtypep->implicit()) { - width = mwidth = 0; + bool implicitParam = nodep->isParam() && bdtypep && bdtypep->implicit(); + if (implicitParam) { + AstNumeric rs = AstNumeric::UNSIGNED; + int width=0; int mwidth=0; if (nodep->valuep()) { nodep->valuep()->iterateAndNext(*this,WidthVP(width,0,PRELIM).p()); // Although nodep will get a different width for parameters just below, // we want the init numbers to retain their width/minwidth until parameters are replaced. // This prevents width warnings at the location the parameter is substituted in nodep->valuep()->iterateAndNext(*this,WidthVP(width,0,FINAL).p()); - if (nodep->valuep()->widthSized()) { - width = mwidth = nodep->valuep()->width(); - } else { - if (nodep->valuep()->width()>32) nodep->valuep()->v3warn(WIDTH,"Assigning >32 bit to unranged parameter (defaults to 32 bits)"); - width = 32; - mwidth = nodep->valuep()->widthMin(); + rs = nodep->numeric(); + if (!rs.isDouble()) { + if (nodep->valuep()->widthSized()) { + width = mwidth = nodep->valuep()->width(); + } else { + if (nodep->valuep()->width()>32) nodep->valuep()->v3warn(WIDTH,"Assigning >32 bit to unranged parameter (defaults to 32 bits)"); + width = 32; + mwidth = nodep->valuep()->widthMin(); + } } } // Parameter sizes can come from the thing they get assigned from // They then "stick" to that width. if (!width) width=32; // Or, if nothing, they're 32 bits. - if (bdtypep->rangep()) bdtypep->rangep()->unlinkFrBackWithNext()->deleteTree(); - bdtypep->rangep(new AstRange(nodep->fileline(),width-1,0)); + // CLEANUP: If we have a TypeOf operator, or make dtype on every AstNode, + // this would be just a copy of the dtype into the var. + if (rs.isDouble()) { + AstBasicDType* newp = new AstBasicDType(nodep->fileline(), AstBasicDTypeKwd::DOUBLE); + bdtypep->replaceWith(newp); + bdtypep->deleteTree(); bdtypep=NULL; + } else { + AstBasicDType* newp = new AstBasicDType(nodep->fileline(), AstLogicPacked(), width); + newp->implicit(true); + newp->numeric(rs); // SIGNED or UNSIGNED + bdtypep->replaceWith(newp); + bdtypep->deleteTree(); bdtypep=NULL; + } nodep->dtypep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); } else { // non param or sized param nodep->dtypep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); - width = nodep->dtypep()->width(); mwidth = nodep->dtypep()->widthMin(); if (nodep->valuep()) { - nodep->valuep()->iterateAndNext(*this,WidthVP(width,0,BOTH).p()); + nodep->valuep()->iterateAndNext(*this,WidthVP(nodep->dtypep()->width(),0,BOTH).p()); //if (debug()) nodep->dumpTree(cout," final: "); } } - nodep->width(width,mwidth); // See above note about valuep()->...FINAL - if (nodep->valuep()) widthCheck(nodep,"Initial value",nodep->valuep(),width,mwidth); + nodep->widthSignedFrom(nodep->dtypep()); + if (nodep->valuep()) { + if (implicitParam) { + nodep->width(nodep->width(), nodep->valuep()->widthMin()); // Needed as mwidth might not equal width + } + widthCheck(nodep,"Initial value",nodep->valuep(), + nodep->width(),nodep->widthMin()); + } UINFO(4,"varWidthed "<dumpTree(cout," InitOut: "); + nodep->didWidth(true); + nodep->doingWidth(false); } virtual void visit(AstNodeVarRef* nodep, AstNUser* vup) { - if (nodep->varp()->width()==0) { + if (!nodep->varp()->didWidth()) { // Var hasn't been widthed, so make it so. nodep->varp()->iterate(*this); } //if (debug()>=9) { nodep->dumpTree(cout," VRin "); nodep->varp()->dumpTree(cout," forvar "); } // Note genvar's are also entered as integers + nodep->numericFrom(nodep->varp()); if (nodep->backp()->castNodeAssign() && nodep->lvalue()) { // On LHS - // Consider Integers on LHS to sized (else may be unsized.) + // Consider Integers on LHS to sized (else may inherit unsized.) nodep->width(nodep->varp()->width(), nodep->varp()->width()); } else { nodep->widthFrom(nodep->varp()); @@ -650,7 +813,7 @@ private: if (!enump) nodep->v3fatalSrc("EnumItemRef can't deref back to a Enum"); enump->iterate(*this,vup); } - nodep->widthFrom(nodep->itemp()->valuep()); + nodep->widthSignedFrom(nodep->itemp()); } virtual void visit(AstPslClocked* nodep, AstNUser*) { nodep->propp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p()); @@ -660,6 +823,7 @@ private: widthCheckReduce(nodep,"Disable",nodep->disablep(),1,1); // it's like an if() condition. } widthCheckReduce(nodep,"Property",nodep->propp(),1,1); // it's like an if() condition. + nodep->numeric(AstNumeric::UNSIGNED); nodep->width(1,1); } @@ -739,7 +903,9 @@ private: nodep->ifsp()->iterateAndNext(*this); nodep->elsesp()->iterateAndNext(*this); } - nodep->condp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p()); + nodep->condp()->iterateAndNext(*this,WidthVP(1,1,PRELIM).p()); + spliceCvtCmpD0(nodep->condp()); + nodep->condp()->iterateAndNext(*this,WidthVP(1,1,FINAL).p()); widthCheckReduce(nodep,"If",nodep->condp(),1,1); // it's like an if() condition. //if (debug()) nodep->dumpTree(cout," IfOut: "); } @@ -749,15 +915,21 @@ private: nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); if (!nodep->lhsp()->widthSized()) nodep->v3fatalSrc("How can LHS be unsized?"); + if (!nodep->lhsp()->isDouble() && nodep->rhsp()->isDouble()) { + spliceCvtS(nodep->rhsp(), false); // Round RHS + } else if (nodep->lhsp()->isDouble() && !nodep->rhsp()->isDouble()) { + spliceCvtD(nodep->rhsp()); + } int awidth = nodep->lhsp()->width(); if (awidth==0) { awidth = nodep->rhsp()->width(); // Parameters can propagate by unsized assignment } nodep->rhsp()->iterateAndNext(*this,WidthVP(awidth,awidth,FINAL).p()); + nodep->numericFrom(nodep->lhsp()); nodep->width(awidth,awidth); // We know the assign will truncate, so rather - //UINFO(0,"aw "<rhsp()->width()<<" m"<rhsp()->widthMin()<rhsp()->width()<<" m"<rhsp()->widthMin()<rhsp(),awidth,awidth); //if (debug()) nodep->dumpTree(cout," AssignOut: "); } @@ -766,6 +938,38 @@ private: // TOP LEVEL NODE // Just let all arguments seek their natural sizes nodep->iterateChildren(*this,WidthVP(ANYSIZE,0,BOTH).p()); + // + UINFO(9," Display in "<text()<exprsp(); + for (const char* inp = nodep->text().c_str(); *inp; inp++) { + char ch = *inp; // Breaks with iterators... + if (!inPct && ch=='%') { + inPct = true; + } else if (inPct && isdigit(ch)) { + } else if (tolower(inPct)) { + inPct = false; + switch (tolower(ch)) { + case '%': break; // %% - just output a % + case 'm': break; // %m - auto insert "name" + case 'd': { // Convert decimal to either 'd' or 'u' + if (argp && argp->isSigned()) { // Convert it + ch = '~'; + } + if (argp) argp=argp->nextp(); + break; + } + default: { // Most operators, just move to next argument + if (argp) argp=argp->nextp(); + break; + } + } // switch + } + dispout += ch; + } + nodep->text(dispout); + UINFO(9," Display out "<text()<filep()) { @@ -787,6 +991,7 @@ private: } virtual void visit(AstFEof* nodep, AstNUser*) { nodep->filep()->iterateAndNext(*this,WidthVP(32,32,BOTH).p()); + nodep->numeric(AstNumeric::UNSIGNED); nodep->width(1,1); widthCheck(nodep,"file_descriptor",nodep->filep(),32,32); } @@ -837,6 +1042,7 @@ private: } virtual void visit(AstValuePlusArgs* nodep, AstNUser* vup) { nodep->exprsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + nodep->numeric(AstNumeric::UNSIGNED); nodep->width(32,32); } virtual void visit(AstUCStmt* nodep, AstNUser*) { @@ -935,14 +1141,25 @@ private: } virtual void visit(AstNodeFTask* nodep, AstNUser* vup) { // Grab width from the output variable (if it's a function) + if (nodep->didWidth()) return; UINFO(5," FTASK "<width()==0) { - // Function hasn't been widthed, so make it so. - nodep->iterateChildren(*this); - if (nodep->fvarp()) { - nodep->width(nodep->fvarp()->width(), nodep->fvarp()->width()); - } + if (nodep->doingWidth()) { + nodep->v3error("Unsupported: Recursive function or task call"); + nodep->width(1,1); + nodep->didWidth(true); + return; } + // Function hasn't been widthed, so make it so. + nodep->doingWidth(true); // Would use user1 etc, but V3Width called from too many places to spend a user + nodep->iterateChildren(*this); + if (nodep->fvarp()) { + m_funcp = nodep->castFunc(); + if (!m_funcp) nodep->v3fatalSrc("FTask with function variable, but isn't a function"); + nodep->numericFrom(nodep->fvarp()); // Which will get it from fvarp()->dtypep() + nodep->width(nodep->fvarp()->width(), nodep->fvarp()->width()); + } + nodep->didWidth(true); + nodep->doingWidth(false); m_funcp = NULL; } virtual void visit(AstReturn* nodep, AstNUser* vup) { @@ -965,18 +1182,11 @@ private: } virtual void visit(AstNodeFTaskRef* nodep, AstNUser* vup) { // Function hasn't been widthed, so make it so. + UINFO(5, " FTASKREF "<taskp()) nodep->v3fatalSrc("Unlinked"); - if (nodep->taskp()->width()==0) { - if (m_taskDepth > 100) { - nodep->v3error("Unsupported: Recursive function or task call"); - nodep->width(1,1); - nodep->taskp()->width(1,1); - return; - } - m_taskDepth++; - nodep->taskp()->iterate(*this); - m_taskDepth--; - } + if (nodep->didWidth()) return; + nodep->taskp()->iterate(*this); + // // And do the arguments to the task/function too for (int accept_mode=1; accept_mode>=0; accept_mode--) { // Avoid duplicate code; just do inner stuff twice V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp()); @@ -1037,6 +1247,7 @@ private: } } } + nodep->didWidth(true); } virtual void visit(AstNetlist* nodep, AstNUser*) { // Iterate modules backwards, in bottom-up order. That's faster @@ -1045,6 +1256,10 @@ private: //-------------------- // Default + virtual void visit(AstNodeMath* nodep, AstNUser*) { + nodep->v3fatalSrc("Visit function missing? Widthed function missing for math node: "<iterateChildren(*this); + } virtual void visit(AstNode* nodep, AstNUser* vup) { // Default: Just iterate if (vup) nodep->v3fatalSrc("Visit function missing? Widthed expectation for this node: "<c()->prelim()) { // First stage evaluation + nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + checkCvtUS(nodep->lhsp()); + nodep->numeric(AstNumeric::DOUBLE); + widthCheck(nodep,"LHS",nodep->lhsp(),64,64); + } + } + void visit_Or_Ls32(AstNodeUniop* nodep, AstNUser* vup) { + // Real: Output real + if (vup->c()->prelim()) { // First stage evaluation + nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + checkCvtUS(nodep->lhsp()); + nodep->numeric(AstNumeric::DOUBLE); + widthCheck(nodep,"LHS",nodep->lhsp(),32,32); + } + } + void visit_Os32_Lr(AstNodeUniop* nodep, AstNUser* vup) { + // Real: LHS real + if (vup->c()->prelim()) { // First stage evaluation + nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + checkCvtD(nodep->lhsp()); + nodep->numeric(AstNumeric::SIGNED); + nodep->width(32,32); + } + } + void visit_Ou64_Lr(AstNodeUniop* nodep, AstNUser* vup) { + // Real: LHS real + if (vup->c()->prelim()) { // First stage evaluation + nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + checkCvtD(nodep->lhsp()); + nodep->numeric(AstNumeric::UNSIGNED); + nodep->width(64,64); + } + } + + void visit_log_O1_L1rus(AstNode* nodep, AstNUser* vup) { + // Note AstPslBool isn't a AstNodeUniop, or we'd only allow that here + // Widths: 1 bit out, lhs 1 bit + // Real: Allowed; implicitly compares with zero + // 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()); + spliceCvtCmpD0(nodep->op1p()); + } + nodep->width(1,1); + nodep->numeric(AstNumeric::UNSIGNED); + if (vup->c()->final()) { + widthCheckReduce(nodep,"LHS",nodep->op1p(),1,1); + } + } + void visit_log_O1_LR1rus(AstNodeBiop* nodep, AstNUser* vup) { + // Widths: 1 bit out, lhs 1 bit, rhs 1 bit + if (vup->c()->prelim()) { + nodep->lhsp()->iterateAndNext(*this,WidthVP(1,0,BOTH).p()); + nodep->rhsp()->iterateAndNext(*this,WidthVP(1,0,BOTH).p()); + spliceCvtCmpD0(nodep->lhsp()); + spliceCvtCmpD0(nodep->rhsp()); + } + nodep->width(1,1); + nodep->numeric(AstNumeric::UNSIGNED); + if (vup->c()->final()) { + widthCheckReduce(nodep,"LHS",nodep->lhsp(),1,1); + widthCheckReduce(nodep,"RHS",nodep->rhsp(),1,1); + } + } + + void visit_red_O1_Lrus(AstNodeUniop* nodep, AstNUser* vup, bool realok) { + // Widths: 1 bit out, Any width lhs + // Signed: Output unsigned, Lhs/Rhs/etc non-real + if (vup->c()->prelim()) { + nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + } + if (!realok) checkCvtUS(nodep->lhsp()); + nodep->numeric(AstNumeric::UNSIGNED); + nodep->width(1,1); + } + void visit_cmp_O1_DSreplace(AstNodeBiop* nodep, AstNUser* vup) { + // COMPARES + // Widths: 1 bit out, lhs width == rhs width + // Signed: if RHS&LHS signed, OPERATOR CHANGES to signed flavor + // Real: allowed on RHS, if RHS|LHS is real, both become real, and OPERATOR CHANGES + if (vup->c()->prelim()) { + nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + } + if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) { + spliceCvtD(nodep->lhsp()); + spliceCvtD(nodep->rhsp()); + if (AstNodeBiop* newp=replaceWithDVersion(nodep)) { nodep=NULL; + nodep = newp; // Process new node instead + } + } else { + bool signedFl = nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned(); + if (AstNodeBiop* newp=replaceWithUOrSVersion(nodep, signedFl)) { nodep=NULL; + nodep = newp; // Process new node instead + } + } + int width = max(nodep->lhsp()->width(), nodep->rhsp()->width()); + int ewidth = max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin()); + nodep->numeric(AstNumeric::UNSIGNED); + nodep->width(1,1); + if (vup->c()->final()) { + nodep->lhsp()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); + nodep->rhsp()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); + widthCheck(nodep,"LHS",nodep->lhsp(),width,ewidth); + widthCheck(nodep,"RHS",nodep->rhsp(),width,ewidth); + } + } + void visit_cmp_O1_LRrus(AstNodeBiop* nodep, AstNUser* vup, bool real_lhs) { + // Widths: 1 bit out, lhs width == rhs width + // Signed doesn't matter + // Real if and only if real_lhs set + if (!nodep->rhsp()) nodep->v3fatalSrc("For binary ops only!"); + if (vup->c()->prelim()) { + nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + } + if (real_lhs) { + checkCvtD(nodep->lhsp()); + checkCvtD(nodep->rhsp()); + } else { + checkCvtUS(nodep->lhsp()); + checkCvtUS(nodep->rhsp()); + } + int width = max(nodep->lhsp()->width(), nodep->rhsp()->width()); + int ewidth = max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin()); + nodep->numeric(AstNumeric::UNSIGNED); + nodep->width(1,1); + if (vup->c()->final()) { + nodep->lhsp()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); + nodep->rhsp()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); + widthCheck(nodep,"LHS",nodep->lhsp(),width,ewidth); + widthCheck(nodep,"RHS",nodep->rhsp(),width,ewidth); + } + } + + void visit_math_Orus_Dreplace(AstNodeUniop* nodep, AstNUser* vup, bool real_ok) { + // Widths: out width = lhs width + // Signed: From lhs + // "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()); + if (!real_ok) checkCvtUS(nodep->lhsp()); + } + if (real_ok && nodep->lhsp()->isDouble()) { + spliceCvtD(nodep->lhsp()); + if (AstNodeUniop* newp=replaceWithDVersion(nodep)) { nodep=NULL; + nodep = newp; // Process new node instead + } + } else { + nodep->isSigned(nodep->lhsp()->isSigned()); + // Note there aren't yet uniops that need version changes + // So no need to call replaceWithUOrSVersion(nodep, nodep->isSigned()) + } + int width = max(vup->c()->width(), nodep->lhsp()->width()); + int ewidth = max(vup->c()->widthMin(), nodep->lhsp()->widthMin()); + nodep->width(width,ewidth); + nodep->numericFrom(nodep->lhsp()); + if (vup->c()->final()) { + nodep->lhsp()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); + widthCheck(nodep,"LHS",nodep->lhsp(),width,ewidth); + } + } + + void visit_Ous_Lus_Wforce(AstNodeUniop* nodep, AstNUser* vup, AstNumeric rs_out) { + // 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()); + checkCvtUS(nodep->lhsp()); + } + int width = nodep->lhsp()->width(); + int ewidth = nodep->lhsp()->width(); // Not minWidth; force it. + nodep->width(width,ewidth); + nodep->numeric(rs_out); + 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 visit_shift_Ous_Lus_Rus32(AstNodeBiop* nodep, AstNUser* vup) { + // Widths: Output width from lhs, rhs<33 bits + // Signed: Output signed iff LHS signed; unary operator + shift_prelim(nodep,vup); + nodep->isSigned(nodep->lhsp()->isSigned()); + AstNodeBiop* newp = shift_final(nodep,vup); nodep=NULL; + if (newp) {} // Ununused + } + void shift_prelim(AstNodeBiop* nodep, AstNUser* vup) { + if (vup->c()->prelim()) { + nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + checkCvtUS(nodep->lhsp()); + checkCvtUS(nodep->rhsp()); + } + int width = max(vup->c()->width(), nodep->lhsp()->width()); + int ewidth = max(vup->c()->widthMin(), nodep->lhsp()->widthMin()); + nodep->width(width,ewidth); + } + AstNodeBiop* shift_final(AstNodeBiop* nodep, AstNUser* vup) { + // Nodep maybe edited + if (vup->c()->final()) { + // ShiftRS converts to ShiftR, but not vice-versa + if (nodep->castShiftRS()) { + if (AstNodeBiop* newp=replaceWithUOrSVersion(nodep, nodep->isSigned())) { nodep=NULL; + nodep = newp; // Process new node instead + } + } + int width=nodep->width(); int ewidth=nodep->widthMin(); + nodep->lhsp()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); + widthCheck(nodep,"LHS",nodep->lhsp(),width,ewidth); + if (nodep->rhsp()->width()>32) + nodep->rhsp()->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"); + } + return nodep; // May edit + } + + void visit_boolmath_Ous_LRus(AstNodeBiop* nodep, AstNUser* vup) { + // Widths: out width = lhs width = rhs width + // Signed: if lhs & rhs signed + if (!nodep->rhsp()) 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->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + checkCvtUS(nodep->lhsp()); + checkCvtUS(nodep->rhsp()); + } + int width = max(vup->c()->width(), max(nodep->lhsp()->width(), nodep->rhsp()->width())); + int mwidth = max(vup->c()->widthMin(), max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin())); + nodep->width(width,mwidth); + nodep->isSigned(nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); + if (vup->c()->final()) { + // Final call, so make sure children check their sizes + nodep->lhsp()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); + nodep->rhsp()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); + // Some warning suppressions + bool lhsOk=false; bool rhsOk = false; + if (nodep->castAdd() || nodep->castSub()) { + lhsOk = (mwidth == (nodep->lhsp()->widthMin()+1)); // Ok if user wants extra bit from carry + rhsOk = (mwidth == (nodep->rhsp()->widthMin()+1)); // Ok if user wants extra bit from carry + } else if (nodep->castMul() || nodep->castMulS()) { + lhsOk = (mwidth >= (nodep->lhsp()->widthMin())); + rhsOk = (mwidth >= (nodep->rhsp()->widthMin())); + } + // Error report and change sizes for suboperands of this node. + widthCheck(nodep,"LHS",nodep->lhsp(),width,mwidth,lhsOk); + widthCheck(nodep,"RHS",nodep->rhsp(),width,mwidth,rhsOk); + } + } + + void visit_math_Orus_DSreplace(AstNodeBiop* nodep, AstNUser* vup, bool real_ok) { + // Widths: out width = lhs width = rhs width + // Signed: Replace operator with signed operator, or signed to unsigned + // Real: Replace operator with real operator + // + // 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->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + } + if (!real_ok) { + checkCvtUS(nodep->lhsp()); + checkCvtUS(nodep->rhsp()); + } + if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) { + spliceCvtD(nodep->lhsp()); + spliceCvtD(nodep->rhsp()); + if (AstNodeBiop* newp=replaceWithDVersion(nodep)) { nodep=NULL; + nodep = newp; // Process new node instead + } + } else { + nodep->isSigned(nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); + if (AstNodeBiop* newp=replaceWithUOrSVersion(nodep, nodep->isSigned())) { nodep=NULL; + nodep = newp; // Process new node instead + } + } + int width = max(vup->c()->width(), max(nodep->lhsp()->width(), nodep->rhsp()->width())); + int mwidth = max(vup->c()->widthMin(), max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin())); + nodep->width(width,mwidth); + if (vup->c()->final()) { + // Final call, so make sure children check their sizes + nodep->lhsp()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); + nodep->rhsp()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); + // Some warning suppressions + bool lhsOk=false; bool rhsOk = false; + if (nodep->castAdd() || nodep->castSub()) { + lhsOk = (mwidth == (nodep->lhsp()->widthMin()+1)); // Ok if user wants extra bit from carry + rhsOk = (mwidth == (nodep->rhsp()->widthMin()+1)); // Ok if user wants extra bit from carry + } else if (nodep->castMul() || nodep->castMulS()) { + lhsOk = (mwidth >= (nodep->lhsp()->widthMin())); + rhsOk = (mwidth >= (nodep->rhsp()->widthMin())); + } + // Error report and change sizes for suboperands of this node. + widthCheck(nodep,"LHS",nodep->lhsp(),width,mwidth,lhsOk); + widthCheck(nodep,"RHS",nodep->rhsp(),width,mwidth,rhsOk); + } + } + void visit_math_Or_LRr(AstNodeBiop* nodep, AstNUser* vup) { + if (vup->c()->prelim()) { // First stage evaluation + checkCvtD(nodep->lhsp()); + checkCvtD(nodep->rhsp()); + nodep->numeric(AstNumeric::DOUBLE); + // Determine expression widths only relying on what's in the subops + nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + } + } + void visit_math_Or_Lr(AstNodeUniop* nodep, AstNUser* vup) { + if (vup->c()->prelim()) { // First stage evaluation + checkCvtD(nodep->lhsp()); + nodep->numeric(AstNumeric::DOUBLE); + // Determine expression widths only relying on what's in the subops + nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + } + } + + //---------------------------------------------------------------------- + // LOWER LEVEL WIDTH METHODS (none iterate) bool widthBad (AstNode* nodep, int expWidth, int expWidthMin) { if (nodep->width()==0) nodep->v3fatalSrc("Under node "<prettyTypeName()<<" has no expected width?? Missing Visitor func?"); @@ -1224,161 +1775,167 @@ private: } } - void visit_log_O1_L1rus(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); + //---------------------------------------------------------------------- + // SIGNED/DOUBLE METHODS + + void checkCvtUS(AstNode* nodep) { + if (nodep && nodep->isDouble()) { + nodep->v3error("Expected integral (non-real) input to "<backp()->prettyTypeName()); + spliceCvtS(nodep, false); nodep=NULL; } } - - void visit_log_O1_LR1rus(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 checkCvtD(AstNode* nodep) { + if (nodep && !nodep->isDouble()) { + nodep->v3error("Expected real input to "<backp()->prettyTypeName()); + spliceCvtD(nodep); nodep=NULL; } } - - void visit_red_O1_Lrus(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 visit_cmp_O1_LRrus(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 spliceCvtCmpD0(AstNode* nodep) { + // For DOUBLE under a logical op, add implied test against zero + // Never a warning + if (nodep && nodep->isDouble()) { + UINFO(6," spliceCvtCmpD0: "<unlinkFrBack(&linker); + AstNode* newp = new AstNeqD(nodep->fileline(), nodep, + new AstConst(nodep->fileline(), AstConst::RealDouble(), 0.0)); + linker.relink(newp); } } - - 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 visit_math_Orus_Dreplace(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 spliceCvtD(AstNode* nodep) { + // For integer used in REAL context, convert to real + // We don't warn here, "2.0 * 2" is common and reasonable + if (nodep && !nodep->isDouble()) { + UINFO(6," spliceCvtD: "<unlinkFrBack(&linker); + AstNode* newp = new AstIToRD(nodep->fileline(), nodep); + linker.relink(newp); } } - - void visit_Ous_Lus_Wforce(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 spliceCvtS(AstNode* nodep, bool ignoreWarn) { + if (nodep && nodep->isDouble()) { + UINFO(6," spliceCvtS: "<unlinkFrBack(&linker); + if (!ignoreWarn) nodep->v3warn(REALCVT,"Implicit conversion of real to integer"); + AstNode* newp = new AstRToIRoundS(nodep->fileline(), nodep); + linker.relink(newp); } } - - void visit_shift_Ous_Lus_Rus32(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()); + AstNodeBiop* replaceWithUOrSVersion(AstNodeBiop* nodep, bool signedFlavorNeeded) { + // Given a signed/unsigned node type, create the opposite type + // Return new node or NULL if nothing + if (signedFlavorNeeded == nodep->signedFlavor()) { + return NULL; } - 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"); + // To simplify callers, some node types don't need to change + switch (nodep->type()) { + case AstType::atEQ: nodep->isSigned(signedFlavorNeeded); return NULL; + case AstType::atNEQ: nodep->isSigned(signedFlavorNeeded); return NULL; + case AstType::atADD: nodep->isSigned(signedFlavorNeeded); return NULL; + case AstType::atSUB: nodep->isSigned(signedFlavorNeeded); return NULL; + case AstType::atSHIFTL: nodep->isSigned(signedFlavorNeeded); return NULL; + default: break; } + FileLine* fl = nodep->fileline(); + AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); + AstNodeBiop* newp = NULL; + switch (nodep->type()) { + case AstType::atGT: newp = new AstGtS (fl,lhsp,rhsp); break; + case AstType::atGTS: newp = new AstGt (fl,lhsp,rhsp); break; + case AstType::atGTE: newp = new AstGteS (fl,lhsp,rhsp); break; + case AstType::atGTES: newp = new AstGte (fl,lhsp,rhsp); break; + case AstType::atLT: newp = new AstLtS (fl,lhsp,rhsp); break; + case AstType::atLTS: newp = new AstLt (fl,lhsp,rhsp); break; + case AstType::atLTE: newp = new AstLteS (fl,lhsp,rhsp); break; + case AstType::atLTES: newp = new AstLte (fl,lhsp,rhsp); break; + case AstType::atDIV: newp = new AstDivS (fl,lhsp,rhsp); break; + case AstType::atDIVS: newp = new AstDiv (fl,lhsp,rhsp); break; + case AstType::atMODDIV: newp = new AstModDivS (fl,lhsp,rhsp); break; + case AstType::atMODDIVS: newp = new AstModDiv (fl,lhsp,rhsp); break; + case AstType::atMUL: newp = new AstMulS (fl,lhsp,rhsp); break; + case AstType::atMULS: newp = new AstMul (fl,lhsp,rhsp); break; + case AstType::atPOW: newp = new AstPowS (fl,lhsp,rhsp); break; + case AstType::atPOWS: newp = new AstPow (fl,lhsp,rhsp); break; + case AstType::atSHIFTR: newp = new AstShiftRS (fl,lhsp,rhsp); break; + case AstType::atSHIFTRS: newp = new AstShiftR (fl,lhsp,rhsp); break; + default: + nodep->v3fatalSrc("Node needs sign change, but bad case: "<replaceWith(newp); + newp->widthSignedFrom(nodep); + pushDeletep(nodep); nodep=NULL; + return newp; + } + AstNodeBiop* replaceWithDVersion(AstNodeBiop* nodep) { + // Given a signed/unsigned node type, create the opposite type + // Return new node or NULL if nothing + if (nodep->doubleFlavor()) { + return NULL; + } + FileLine* fl = nodep->fileline(); + AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); + AstNodeBiop* newp = NULL; + switch (nodep->type()) { + case AstType::atADD: newp = new AstAddD (fl,lhsp,rhsp); break; + case AstType::atSUB: newp = new AstSubD (fl,lhsp,rhsp); break; + case AstType::atEQ: newp = new AstEqD (fl,lhsp,rhsp); break; + case AstType::atNEQ: newp = new AstNeqD (fl,lhsp,rhsp); break; + case AstType::atGT: case AstType::atGTS: newp = new AstGtD (fl,lhsp,rhsp); break; + case AstType::atGTE: case AstType::atGTES: newp = new AstGteD (fl,lhsp,rhsp); break; + case AstType::atLT: case AstType::atLTS: newp = new AstLtD (fl,lhsp,rhsp); break; + case AstType::atLTE: case AstType::atLTES: newp = new AstLteD (fl,lhsp,rhsp); break; + case AstType::atDIV: case AstType::atDIVS: newp = new AstDivD (fl,lhsp,rhsp); break; + case AstType::atMUL: case AstType::atMULS: newp = new AstMulD (fl,lhsp,rhsp); break; + default: + nodep->v3fatalSrc("Node needs conversion to double, but bad case: "<replaceWith(newp); + newp->widthSignedFrom(nodep); + pushDeletep(nodep); nodep=NULL; + return newp; + } + AstNodeUniop* replaceWithDVersion(AstNodeUniop* nodep) { + // Given a signed/unsigned node type, create the opposite type + // Return new node or NULL if nothing + if (nodep->doubleFlavor()) { + return NULL; + } + FileLine* fl = nodep->fileline(); + AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNodeUniop* newp = NULL; + switch (nodep->type()) { + case AstType::atNEGATE: newp = new AstNegateD (fl,lhsp); break; + default: + nodep->v3fatalSrc("Node needs conversion to double, but bad case: "<replaceWith(newp); + newp->widthSignedFrom(nodep); + pushDeletep(nodep); nodep=NULL; + return newp; } - void visit_boolmath_Ous_LRus(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); - } + //---------------------------------------------------------------------- + // METHODS - special type detection + bool backRequiresUnsigned(AstNode* nodep) { + // The spec doesn't state this, but if you have an array select where the selection + // index is NOT wide enough, you do not sign extend, but always zero extend. + return (nodep->castArraySel() || nodep->castSel()); } public: // CONSTUCTORS WidthVisitor(bool paramsOnly) { m_paramsOnly = paramsOnly; - m_taskDepth = 0; m_cellRangep = NULL; m_casep = NULL; m_funcp = NULL; @@ -1397,6 +1954,8 @@ void V3Width::width(AstNetlist* nodep) { // We should do it in bottom-up module order, but it works in any order. WidthVisitor visitor (false); (void)visitor.mainAcceptEdit(nodep); + WidthRemoveVisitor rvisitor; + (void)rvisitor.mainAcceptEdit(nodep); } AstNode* V3Width::widthParamsEdit(AstNode* nodep) { @@ -1404,7 +1963,8 @@ AstNode* V3Width::widthParamsEdit(AstNode* nodep) { // We should do it in bottom-up module order, but it works in any order. WidthVisitor visitor (true); nodep = visitor.mainAcceptEdit(nodep); - nodep = V3Signed::signedParamsEdit(nodep); + WidthRemoveVisitor rvisitor; + nodep = rvisitor.mainAcceptEdit(nodep); return nodep; } diff --git a/src/V3WidthCommit.h b/src/V3WidthCommit.h index 1e77bb6a0..b21784cf4 100644 --- a/src/V3WidthCommit.h +++ b/src/V3WidthCommit.h @@ -33,6 +33,37 @@ //###################################################################### +/// Remove all $signed, $unsigned, we're done with them. + +class WidthRemoveVisitor : public AstNVisitor { +private: + // VISITORS + virtual void visit(AstSigned* nodep, AstNUser*) { + replaceWithSignedVersion(nodep, nodep->lhsp()->unlinkFrBack()); nodep=NULL; + } + virtual void visit(AstUnsigned* nodep, AstNUser*) { + replaceWithSignedVersion(nodep, nodep->lhsp()->unlinkFrBack()); nodep=NULL; + } + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + void replaceWithSignedVersion(AstNode* nodep, AstNode* newp) { + UINFO(6," Replace "<replaceWith(newp); + newp->widthSignedFrom(nodep); + pushDeletep(nodep); nodep=NULL; + } +public: + // CONSTRUCTORS + WidthRemoveVisitor() {} + virtual ~WidthRemoveVisitor() {} + AstNode* mainAcceptEdit(AstNode* nodep) { + return nodep->acceptSubtreeReturnEdits(*this); + } +}; + +//###################################################################### + class WidthCommitVisitor : public AstNVisitor { // Now that all widthing is complete, // Copy all width() to widthMin(). V3Const expects this diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 094c63d0e..ef45c4f93 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -74,7 +74,6 @@ #include "V3PreShell.h" #include "V3Premit.h" #include "V3Scope.h" -#include "V3Signed.h" #include "V3Slice.h" #include "V3Split.h" #include "V3SplitAs.h" @@ -168,9 +167,6 @@ void process () { V3Width::width(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("width.tree")); - // Compute signed/unsigned - V3Signed::signedAll(v3Global.rootp()); - v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("signed.tree")); V3Error::abortIfErrors(); // Commit to the widths we've chosen; Make widthMin==width diff --git a/src/verilog.l b/src/verilog.l index 232a75def..be76cebe4 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -174,6 +174,7 @@ word [a-zA-Z0-9_]+ /* Extensions to Verilog set, some specified by PSL */ "$c"[0-9]* { FL; return yD_C; } /*Verilator only*/ /* System Tasks */ + "$bitstoreal" { FL; return yD_BITSTOREAL; } "$display" { FL; return yD_DISPLAY; } "$fclose" { FL; return yD_FCLOSE; } "$fdisplay" { FL; return yD_FDISPLAY; } @@ -187,15 +188,18 @@ word [a-zA-Z0-9_]+ "$fullskew" { FL; return yaTIMINGSPEC; } "$fwrite" { FL; return yD_FWRITE; } "$hold" { FL; return yaTIMINGSPEC; } + "$itor" { FL; return yD_ITOR; } "$nochange" { FL; return yaTIMINGSPEC; } "$period" { FL; return yaTIMINGSPEC; } "$random" { FL; return yD_RANDOM; } "$readmemb" { FL; return yD_READMEMB; } "$readmemh" { FL; return yD_READMEMH; } - "$realtime" { FL; return yD_TIME; } + "$realtime" { FL; return yD_REALTIME; } + "$realtobits" { FL; return yD_REALTOBITS; } "$recovery" { FL; return yaTIMINGSPEC; } "$recrem" { FL; return yaTIMINGSPEC; } "$removal" { FL; return yaTIMINGSPEC; } + "$rtoi" { FL; return yD_RTOI; } "$setup" { FL; return yaTIMINGSPEC; } "$setuphold" { FL; return yaTIMINGSPEC; } "$sformat" { FL; return yD_SFORMAT; } @@ -261,6 +265,8 @@ word [a-zA-Z0-9_]+ "pulldown" { FL; return yPULLDOWN; } "pullup" { FL; return yPULLUP; } "rcmos" { FL; return yRCMOS; } + "real" { FL; return yREAL; } + "realtime" { FL; return yREALTIME; } "reg" { FL; return yREG; } "repeat" { FL; return yREPEAT; } "rnmos" { FL; return yRNMOS; } @@ -310,8 +316,6 @@ word [a-zA-Z0-9_]+ "medium" { yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext); } "pull0" { yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext); } "pull1" { yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext); } - "real" { yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext); } - "realtime" { yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext); } "release" { yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext); } "small" { yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext); } "strong0" { yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext); } @@ -950,12 +954,13 @@ word [a-zA-Z0-9_]+ int V3ParseImp::stateVerilogRecent() { return STATE_VERILOG_RECENT; } double V3ParseImp::parseDouble(const char* textp, size_t length) { - char* strgp = new char[strlen(textp)+1]; + char* strgp = new char[length+1]; char* dp=strgp; for (const char* sp=textp; sp<(textp+length);) { if (*sp != '_') *dp++ = *sp++; else sp++; } + *dp++ = '\0'; char* endp = strgp; double d = strtod(strgp, &endp); if ((endp-strgp) != length) { yyerrorf("Syntax error parsing real: %s",strgp); } diff --git a/src/verilog.y b/src/verilog.y index 0a0d0b300..d36515e0a 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -342,6 +342,8 @@ class AstSenTree; %token yPULLUP "pullup" %token yPURE "pure" %token yRCMOS "rcmos" +%token yREAL "real" +%token yREALTIME "realtime" %token yREG "reg" %token yREPEAT "repeat" %token yRETURN "return" @@ -382,6 +384,7 @@ class AstSenTree; %token yXOR "xor" %token yD_BITS "$bits" +%token yD_BITSTOREAL "$bitstoreal" %token yD_C "$c" %token yD_CLOG2 "$clog2" %token yD_COUNTONES "$countones" @@ -400,11 +403,15 @@ class AstSenTree; %token yD_FWRITE "$fwrite" %token yD_INFO "$info" %token yD_ISUNKNOWN "$isunknown" +%token yD_ITOR "$itor" %token yD_ONEHOT "$onehot" %token yD_ONEHOT0 "$onehot0" %token yD_RANDOM "$random" %token yD_READMEMB "$readmemb" %token yD_READMEMH "$readmemh" +%token yD_REALTIME "$realtime" +%token yD_REALTOBITS "$realtobits" +%token yD_RTOI "$rtoi" %token yD_SFORMAT "$sformat" %token yD_SIGNED "$signed" %token yD_SSCANF "$sscanf" @@ -1067,6 +1074,12 @@ integer_vector_type: // ==IEEE: integer_atom_type | yREG { $$ = new AstBasicDType($1,AstBasicDTypeKwd::LOGIC); } // logic==reg ; +non_integer_type: // ==IEEE: non_integer_type + yREAL { $$ = new AstBasicDType($1,AstBasicDTypeKwd::DOUBLE); } + | yREALTIME { $$ = new AstBasicDType($1,AstBasicDTypeKwd::DOUBLE); } + //UNSUP ySHORTREAL { $$ = new AstBasicDType($1,AstBasicDTypeKwd::FLOAT); } + ; + signingE: // IEEE: signing - plus empty /*empty*/ { $$ = signedst_NOP; } | signing { $$ = $1; } @@ -1097,7 +1110,7 @@ simple_type: // ==IEEE: simple_type // // IEEE: integer_type integer_atom_type { $$ = $1; } | integer_vector_type { $$ = $1; } - //UNSUP non_integer_type { $$ = $1; } + | non_integer_type { $$ = $1; } // // IEEE: ps_type_identifier // // IEEE: ps_parameter_identifier (presumably a PARAMETER TYPE) | ps_type { $$ = $1; } @@ -1120,7 +1133,7 @@ data_type: // ==IEEE: data_type data_typeBasic: // IEEE: part of data_type integer_vector_type signingE rangeListE { $1->setSignedState($2); $$ = GRAMMARP->addRange($1,$3,true); } | integer_atom_type signingE { $1->setSignedState($2); $$ = $1; } - //UNSUP non_integer_type { UNSUP } + | non_integer_type { $$ = $1; } ; data_typeNoRef: // ==IEEE: data_type, excluding class_type etc references @@ -1527,8 +1540,7 @@ delay_value: // ==IEEE:delay_value delayExpr: expr { } - // // Verilator doesn't support yaFLOATNUM/yaTIMENUM, so not in expr - | yaFLOATNUM { } + // // Verilator doesn't support yaTIMENUM, so not in expr | yaTIMENUM { } ; @@ -1713,8 +1725,6 @@ cellpinItemE: // IEEE: named_port_connection + named_parameter_assignment | expr { $$ = new AstPin($1->fileline(),PINNUMINC(),"",$1); } //UNSUP expr ':' expr { } //UNSUP expr ':' expr ':' expr { } - // // Floatnum should only occur with UDPs, but since ports aren't floats, it's legal to round always - | yaFLOATNUM { $$ = new AstPin($1,PINNUMINC(),"",new AstConst($1,AstConst::Unsized32(),(int)(($1<0)?($1-0.5):($1+0.5)))); } ; //************************************************ @@ -2144,6 +2154,7 @@ system_f_call: // IEEE: system_tf_call (as func) // | yD_BITS '(' expr ')' { $$ = new AstAttrOf($1,AstAttrType::EXPR_BITS,$3); } | yD_BITS '(' data_type ')' { $$ = new AstAttrOf($1,AstAttrType::EXPR_BITS,$3); } + | yD_BITSTOREAL '(' expr ')' { $$ = new AstBitsToRealD($1,$3); } | yD_C '(' cStrList ')' { $$ = (v3Global.opt.ignc() ? NULL : new AstUCFunc($1,$3)); } | yD_CLOG2 '(' expr ')' { $$ = new AstCLog2($1,$3); } | yD_COUNTONES '(' expr ')' { $$ = new AstCountOnes($1,$3); } @@ -2153,10 +2164,14 @@ system_f_call: // IEEE: system_tf_call (as func) | yD_FSCANF '(' expr ',' str commaVRDListE ')' { $$ = new AstFScanF($1,*$5,$3,$6); } | yD_SSCANF '(' expr ',' str commaVRDListE ')' { $$ = new AstSScanF($1,*$5,$3,$6); } | yD_ISUNKNOWN '(' expr ')' { $$ = new AstIsUnknown($1,$3); } + | yD_ITOR '(' expr ')' { $$ = new AstIToRD($1,$3); } | yD_ONEHOT '(' expr ')' { $$ = new AstOneHot($1,$3); } | yD_ONEHOT0 '(' expr ')' { $$ = new AstOneHot0($1,$3); } | yD_RANDOM '(' expr ')' { $1->v3error("Unsupported: Seeding $random doesn't map to C++, use $c(\"srand\")"); } | yD_RANDOM parenE { $$ = new AstRand($1); } + | yD_REALTIME parenE { $$ = new AstTimeD($1); } + | yD_REALTOBITS '(' expr ')' { $$ = new AstRealToBits($1,$3); } + | yD_RTOI '(' expr ')' { $$ = new AstRToIS($1,$3); } //| yD_SFORMATF '(' str commaEListE ')' { $$ = new AstSFormatF($1,*$3,false,$4); } // Have AST, just need testing and debug | yD_SIGNED '(' expr ')' { $$ = new AstSigned($1,$3); } | yD_STIME parenE { $$ = new AstSel($1,new AstTime($1),0,32); } @@ -2455,7 +2470,7 @@ expr: // IEEE: part of expression/constant_expression/primary // // // IEEE: primary_literal (minus string, which is handled specially) | yaINTNUM { $$ = new AstConst($1,*$1); } - //UNSUP yaFLOATNUM { UNSUP } + | yaFLOATNUM { $$ = new AstConst($1,AstConst::RealDouble(),$1); } //UNSUP yaTIMENUM { UNSUP } | strAsInt~noStr__IGNORE~ { $$ = $1; } // diff --git a/test_regress/t/t_display_real.pl b/test_regress/t/t_display_real.pl new file mode 100755 index 000000000..0e4c1f63b --- /dev/null +++ b/test_regress/t/t_display_real.pl @@ -0,0 +1,39 @@ +#!/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, + expect=> quotemeta( +'[0] e=0.000000e+00 e1=0.000000e+00 e30=0e+00 e32=0.00e+00 +[0] f=0.000000 f1=0.000000e+00 f30=0e+00 f32=0.00e+00 +[0] g=0 g1=0.000000e+00 g30=0e+00 g32=0.00e+00 + +[0] e=1.000000e+00 e1=1.000000e+00 e30=1e+00 e32=1.00e+00 +[0] f=1.000000 f1=1.000000e+00 f30=1e+00 f32=1.00e+00 +[0] g=1 g1=1.000000e+00 g30=1e+00 g32=1.00e+00 + +[0] e=1.000000e-01 e1=1.000000e-01 e30=1e-01 e32=1.00e-01 +[0] f=0.100000 f1=1.000000e-01 f30=1e-01 f32=1.00e-01 +[0] g=0.1 g1=1.000000e-01 g30=1e-01 g32=1.00e-01 + +[0] e=1.234500e-15 e1=1.234500e-15 e30=1e-15 e32=1.23e-15 +[0] f=0.000000 f1=1.234500e-15 f30=1e-15 f32=1.23e-15 +[0] g=1.2345e-15 g1=1.234500e-15 g30=1e-15 g32=1.23e-15 + +[0] e=2.579000e+15 e1=2.579000e+15 e30=3e+15 e32=2.58e+15 +[0] f=2579000000000000.000000 f1=2.579000e+15 f30=3e+15 f32=2.58e+15 +[0] g=2.579e+15 g1=2.579000e+15 g30=3e+15 g32=2.58e+15 +'), + ); + +ok(1); +1; diff --git a/test_regress/t/t_display_real.v b/test_regress/t/t_display_real.v new file mode 100644 index 000000000..aee9a879e --- /dev/null +++ b/test_regress/t/t_display_real.v @@ -0,0 +1,37 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t; + real n0; initial n0 = 0.0; + real n1; initial n1 = 1.0; + real n2; initial n2 = 0.1; + real n3; initial n3 = 1.2345e-15; + real n4; initial n4 = 2.579e+15; + + initial begin + // Display formatting + $display("[%0t] e=%e e1=%1e e30=%3.0e e32=%3.2e", $time, n0,n0,n0,n0); + $display("[%0t] f=%f f1=%1e f30=%3.0e f32=%3.2e", $time, n0,n0,n0,n0); + $display("[%0t] g=%g g1=%1e g30=%3.0e g32=%3.2e", $time, n0,n0,n0,n0); + $display; + $display("[%0t] e=%e e1=%1e e30=%3.0e e32=%3.2e", $time, n1,n1,n1,n1); + $display("[%0t] f=%f f1=%1e f30=%3.0e f32=%3.2e", $time, n1,n1,n1,n1); + $display("[%0t] g=%g g1=%1e g30=%3.0e g32=%3.2e", $time, n1,n1,n1,n1); + $display; + $display("[%0t] e=%e e1=%1e e30=%3.0e e32=%3.2e", $time, n2,n2,n2,n2); + $display("[%0t] f=%f f1=%1e f30=%3.0e f32=%3.2e", $time, n2,n2,n2,n2); + $display("[%0t] g=%g g1=%1e g30=%3.0e g32=%3.2e", $time, n2,n2,n2,n2); + $display; + $display("[%0t] e=%e e1=%1e e30=%3.0e e32=%3.2e", $time, n3,n3,n3,n3); + $display("[%0t] f=%f f1=%1e f30=%3.0e f32=%3.2e", $time, n3,n3,n3,n3); + $display("[%0t] g=%g g1=%1e g30=%3.0e g32=%3.2e", $time, n3,n3,n3,n3); + $display; + $display("[%0t] e=%e e1=%1e e30=%3.0e e32=%3.2e", $time, n4,n4,n4,n4); + $display("[%0t] f=%f f1=%1e f30=%3.0e f32=%3.2e", $time, n4,n4,n4,n4); + $display("[%0t] g=%g g1=%1e g30=%3.0e g32=%3.2e", $time, n4,n4,n4,n4); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_display_real_noopt.pl b/test_regress/t/t_display_real_noopt.pl new file mode 100755 index 000000000..23b06671d --- /dev/null +++ b/test_regress/t/t_display_real_noopt.pl @@ -0,0 +1,42 @@ +#!/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. + +top_filename("t/t_display_real.v"); + +compile ( + v_flags2 => [$Self->{v3}?"-O0":""], + ); + +execute ( + check_finished=>1, + expect=> quotemeta( +'[0] e=0.000000e+00 e1=0.000000e+00 e30=0e+00 e32=0.00e+00 +[0] f=0.000000 f1=0.000000e+00 f30=0e+00 f32=0.00e+00 +[0] g=0 g1=0.000000e+00 g30=0e+00 g32=0.00e+00 + +[0] e=1.000000e+00 e1=1.000000e+00 e30=1e+00 e32=1.00e+00 +[0] f=1.000000 f1=1.000000e+00 f30=1e+00 f32=1.00e+00 +[0] g=1 g1=1.000000e+00 g30=1e+00 g32=1.00e+00 + +[0] e=1.000000e-01 e1=1.000000e-01 e30=1e-01 e32=1.00e-01 +[0] f=0.100000 f1=1.000000e-01 f30=1e-01 f32=1.00e-01 +[0] g=0.1 g1=1.000000e-01 g30=1e-01 g32=1.00e-01 + +[0] e=1.234500e-15 e1=1.234500e-15 e30=1e-15 e32=1.23e-15 +[0] f=0.000000 f1=1.234500e-15 f30=1e-15 f32=1.23e-15 +[0] g=1.2345e-15 g1=1.234500e-15 g30=1e-15 g32=1.23e-15 + +[0] e=2.579000e+15 e1=2.579000e+15 e30=3e+15 e32=2.58e+15 +[0] f=2579000000000000.000000 f1=2.579000e+15 f30=3e+15 f32=2.58e+15 +[0] g=2.579e+15 g1=2.579000e+15 g30=3e+15 g32=2.58e+15 +'), + ); + +ok(1); +1; diff --git a/test_regress/t/t_dpi_import.v b/test_regress/t/t_dpi_import.v index 4d73d6a04..c6686cfe6 100644 --- a/test_regress/t/t_dpi_import.v +++ b/test_regress/t/t_dpi_import.v @@ -39,9 +39,7 @@ module t (); import "DPI-C" pure function longint dpii_f_longint (input longint i); import "DPI-C" pure function chandle dpii_f_chandle (input chandle i); import "DPI-C" pure function string dpii_f_string (input string i); -`ifndef VERILATOR import "DPI-C" pure function real dpii_f_real (input real i); -`endif `ifndef NO_SHORTREAL import "DPI-C" pure function shortreal dpii_f_shortreal(input shortreal i); `endif @@ -53,9 +51,7 @@ module t (); import "DPI-C" pure function void dpii_v_longint (input longint i, output longint o); import "DPI-C" pure function void dpii_v_chandle (input chandle i, output chandle o); import "DPI-C" pure function void dpii_v_string (input string i, output string o); -`ifndef VERILATOR import "DPI-C" pure function void dpii_v_real (input real i, output real o); -`endif `ifndef NO_SHORTREAL import "DPI-C" pure function void dpii_v_shortreal(input shortreal i, output shortreal o); `endif @@ -93,9 +89,7 @@ module t (); longint i_l, o_l; chandle i_c, o_c; string i_n, o_n; -`ifndef VERILATOR real i_d, o_d; -`endif `ifndef NO_SHORTREAL shortreal i_f, o_f; `endif @@ -122,6 +116,10 @@ module t (); i_y = {1'b1,wide[8-2:0]}; i_s = {1'b1,wide[16-2:0]}; i_l = {1'b1,wide[64-2:0]}; + i_d = 32.1; +`ifndef NO_SHORTREAL + i_f = 30.2; +`endif if (dpii_f_bit (i_b) !== ~i_b) $stop; if (dpii_f_bit8 (i_b8) !== ~i_b8) $stop; @@ -145,9 +143,7 @@ module t (); if (dpii_f_longint (i_l) !== ~i_l) $stop; if (dpii_f_chandle (i_c) !== i_c) $stop; if (dpii_f_string (i_n) != i_n) $stop; -`ifndef VERILATOR if (dpii_f_real (i_d) != i_d+1.5) $stop; -`endif `ifndef NO_SHORTREAL if (dpii_f_shortreal(i_f) != i_f+1.5) $stop; `endif @@ -159,9 +155,7 @@ module t (); dpii_v_longint (i_l,o_l); if (o_l !== ~i_l) $stop; dpii_v_chandle (i_c,o_c); if (o_c !== i_c) $stop; dpii_v_string (i_n,o_n); if (o_n != i_n) $stop; -`ifndef VERILATOR dpii_v_real (i_d,o_d); if (o_d != i_d+1.5) $stop; -`endif `ifndef NO_SHORTREAL dpii_v_shortreal(i_f,o_f); if (o_f != i_f+1.5) $stop; `endif diff --git a/test_regress/t/t_func_bad.pl b/test_regress/t/t_func_bad.pl index c08aae55b..866eb1b72 100755 --- a/test_regress/t/t_func_bad.pl +++ b/test_regress/t/t_func_bad.pl @@ -13,8 +13,6 @@ compile ( expect=> q{%Error: t/t_func_bad.v:\d+: Too few arguments in function call to FUNC 'add' %Error: t/t_func_bad.v:\d+: Too few arguments in function call to FUNC 'add' -%Error: t/t_func_bad.v:\d+: Too few arguments in function call to FUNC 'add' -%Error: t/t_func_bad.v:\d+: Too few arguments in function call to FUNC 'add' %Error: t/t_func_bad.v:\d+: Too many arguments in function call to FUNC 'add' %Error: t/t_func_bad.v:\d+: Too few arguments in function call to TASK 'x' %Error: t/t_func_bad.v:\d+: Too few arguments in function call to TASK 'x' diff --git a/test_regress/t/t_lint_realcvt_bad.pl b/test_regress/t/t_lint_realcvt_bad.pl new file mode 100755 index 000000000..e52c48808 --- /dev/null +++ b/test_regress/t/t_lint_realcvt_bad.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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. + +compile ( + make_top_shell => 0, + make_main => 0, + v_flags2 => ["--lint-only -Wwarn-REALCVT"], + verilator_make_gcc => 0, + fails=>1, + expect=> +'%Warning-REALCVT: t/t_lint_realcvt_bad.v:\d+: Implicit conversion of real to integer +%Warning-REALCVT: Use .* to disable this message. +%Error: Exiting due to.*', + ) if $Self->{v3}; + +ok(1); +1; diff --git a/test_regress/t/t_lint_realcvt_bad.v b/test_regress/t/t_lint_realcvt_bad.v new file mode 100644 index 000000000..54b659e2f --- /dev/null +++ b/test_regress/t/t_lint_realcvt_bad.v @@ -0,0 +1,11 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2011 by Wilson Snyder. + +module sub; + integer i; + initial begin + i = 23.2; + end +endmodule diff --git a/test_regress/t/t_math_real.pl b/test_regress/t/t_math_real.pl new file mode 100755 index 000000000..7058e622f --- /dev/null +++ b/test_regress/t/t_math_real.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_math_real.v b/test_regress/t/t_math_real.v new file mode 100644 index 000000000..e97b46e52 --- /dev/null +++ b/test_regress/t/t_math_real.v @@ -0,0 +1,136 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2011 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. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + integer i; + reg [63:0] b; + real r, r2; + integer cyc=0; + + realtime uninit; + initial if (uninit != 0.0) $stop; + + initial begin + // rtoi truncates + if ($rtoi(36.7) != 36) $stop; + if ($rtoi(36.5) != 36) $stop; + if ($rtoi(36.4) != 36) $stop; + // casting rounds + if ((integer '(36.7)) != 37) $stop; + if ((integer '(36.5)) != 37) $stop; + if ((integer '(36.4)) != 36) $stop; + // assignment rounds + // verilator lint_off REALCVT + i = 36.7; if (i != 37) $stop; + i = 36.5; if (i != 37) $stop; + i = 36.4; if (i != 36) $stop; + r = 10'd38; if (r!=38.0) $stop; + // verilator lint_on REALCVT + // operators + if ((-(1.5)) != -1.5) $stop; + if ((+(1.5)) != 1.5) $stop; + if (((1.5)+(1.25)) != 2.75) $stop; + if (((1.5)-(1.25)) != 0.25) $stop; + if (((1.5)*(1.25)) != 1.875) $stop; + if (((1.5)/(1.25)) != 1.2) $stop; + // + if (((1.5)==(2)) != 1'b0) $stop; // note 2 becomes real 2.0 + if (((1.5)!=(2)) != 1'b1) $stop; + if (((1.5)> (2)) != 1'b0) $stop; + if (((1.5)>=(2)) != 1'b0) $stop; + if (((1.5)< (2)) != 1'b1) $stop; + if (((1.5)<=(2)) != 1'b1) $stop; + if (((1.5)==(1.5)) != 1'b1) $stop; + if (((1.5)!=(1.5)) != 1'b0) $stop; + if (((1.5)> (1.5)) != 1'b0) $stop; + if (((1.5)>=(1.5)) != 1'b1) $stop; + if (((1.5)< (1.5)) != 1'b0) $stop; + if (((1.5)<=(1.5)) != 1'b1) $stop; + if (((1.6)==(1.5)) != 1'b0) $stop; + if (((1.6)!=(1.5)) != 1'b1) $stop; + if (((1.6)> (1.5)) != 1'b1) $stop; + if (((1.6)>=(1.5)) != 1'b1) $stop; + if (((1.6)< (1.5)) != 1'b0) $stop; + if (((1.6)<=(1.5)) != 1'b0) $stop; + // + if (((0.0)?(2.0):(1.1)) != 1.1) $stop; + if (((1.5)?(2.0):(1.1)) != 2.0) $stop; + // + if (!1.7) $stop; + if (!(!0.0)) $stop; + if (1.8 && 0.0) $stop; + if (!(1.8 || 0.0)) $stop; + // + i=0; + for (r=1.0; r<2.0; r=r+0.1) i++; + if (i!=10) $stop; + end + + // Test loop + always @ (posedge clk) begin +`ifdef TEST_VERBOSE + $write("[%0t] cyc==%0d crc=%x result=%x\n",$time, cyc, crc, result); +`endif + cyc <= cyc + 1; + if (cyc==0) begin + // Setup + end + else if (cyc<90) begin + if ($time != {32'h0, $rtoi($realtime)}) $stop; + if ($itor(cyc) != cyc) $stop; + //Unsup: if ((real `($time)) != $realtime) $stop; + r = $itor(cyc*2); + i = $rtoi(r); + if (i!=cyc*2) $stop; + // + r = $itor(cyc)/1.5; + b = $realtobits(r); + r2 = $bitstoreal(b); + if (r != r2) $stop; + // + // Trust the integer math as a comparison + r = $itor(cyc); + if ($rtoi(-r) != -cyc) $stop; + if ($rtoi(+r) != cyc) $stop; + if ($rtoi(r+2.0) != (cyc+2)) $stop; + if ($rtoi(r-2.0) != (cyc-2)) $stop; + if ($rtoi(r*2.0) != (cyc*2)) $stop; + if ($rtoi(r/2.0) != (cyc/2)) $stop; + r2 = (2.0/(r-60)); // When zero, result indeterminate, but no crash + // + r2 = $itor(cyc); + case (r) + (r2-1.0): $stop; + r2: ; + default: $stop; + endcase + // + r = $itor(cyc); + if ((r==50.0) != (cyc==50)) $stop; + if ((r!=50.0) != (cyc!=50)) $stop; + if ((r> 50.0) != (cyc> 50)) $stop; + if ((r>=50.0) != (cyc>=50)) $stop; + if ((r< 50.0) != (cyc< 50)) $stop; + if ((r<=50.0) != (cyc<=50)) $stop; + // + if ($rtoi((r-50.0) ? 10.0 : 20.0) + != (((cyc-50)!=0) ? 10 : 20)) $stop; + // + if ((!(r-50.0)) != (!((cyc-50) != 0))) $stop; + end + else if (cyc==99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_sys_sformat.v b/test_regress/t/t_sys_sformat.v index a5c88da89..e2ce5504c 100644 --- a/test_regress/t/t_sys_sformat.v +++ b/test_regress/t/t_sys_sformat.v @@ -16,6 +16,8 @@ module t; reg [48*8:1] str; reg [48*8:1] str2; + real r; + initial begin n = 4'b1100; q = 64'h1234_5678_abcd_0123; @@ -29,6 +31,15 @@ module t; `ifdef TEST_VERBOSE $display("str2=%0s",str2); `endif if (str2 !== "n=1100 q= 2623536935500120647 w=hello-there12345") $stop; + $swrite(str2, "e=%e", r); + $swrite(str2, "e=%f", r); + $swrite(str2, "e=%g", r); + + r = 0.01; + $swrite(str2, "e=%e f=%f g=%g", r, r, r); +`ifdef TEST_VERBOSE $display("str2=%0s",str2); `endif + if (str2 !== "e=1.000000e-02 f=0.010000 g=0.01") $stop; + $swrite(str2, "mod=%m"); `ifdef TEST_VERBOSE $display("str2=%0s",str2); `endif `ifdef verilator diff --git a/test_regress/t/t_trace_ena.v b/test_regress/t/t_trace_ena.v index d4c53c0b0..8e452797b 100644 --- a/test_regress/t/t_trace_ena.v +++ b/test_regress/t/t_trace_ena.v @@ -15,12 +15,14 @@ module t (/*AUTOARG*/ integer b_trace_off; // verilator tracing_on integer c_trace_on; + real r; always @ (posedge clk) begin if (cyc!=0) begin cyc <= cyc + 1; b_trace_off <= cyc; c_trace_on <= b_trace_off; + r <= r + 0.1; if (cyc==4) begin if (c_trace_on != 2) $stop; end diff --git a/test_regress/t/t_var_types.v b/test_regress/t/t_var_types.v index de726e42e..2005e7e01 100644 --- a/test_regress/t/t_var_types.v +++ b/test_regress/t/t_var_types.v @@ -25,8 +25,8 @@ module t (/*AUTOARG*/); // IEEE: non_integer_type //UNSUP shortreal d_shortreal; - //UNSUP real d_real; - //UNSUP realtime d_realtime; + real d_real; + realtime d_realtime; // Declarations using var var byte v_b;