diff --git a/include/verilated.cpp b/include/verilated.cpp index 1893e56e6..fa873ee4e 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -284,6 +284,7 @@ void _vl_vsformat(string& output, const char* formatp, va_list ap) { // Note also assumes variables < 64 are not wide, this assumption is // sometimes not true in low-level routines written here in verilated.cpp static VL_THREAD char tmp[VL_VALUE_STRING_MAX_WIDTH]; + static VL_THREAD char tmpf[VL_VALUE_STRING_MAX_WIDTH]; const char* pctp = NULL; // Most recent %##.##g format bool inPct = false; bool widthSet = false; @@ -329,6 +330,16 @@ void _vl_vsformat(string& output, const char* formatp, va_list ap) { output += cstrp; break; } + case 'e': + case 'f': + case 'g': { + double d = va_arg(ap, double); + strncpy(tmpf, pctp, pos-pctp+1); + tmpf[pos-pctp+1] = '\0'; + sprintf(tmp, tmpf, d); + output += tmp; + break; + } default: { // Deal with all read-and-print somethings const int lbits = va_arg(ap, int); diff --git a/include/verilated.h b/include/verilated.h index 692627a44..fb60fbc4f 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -35,6 +35,7 @@ #include #include #include +#include // avoided to reduce compile time // avoided and instead in verilated_heavy.h to reduce compile time using namespace std; @@ -374,6 +375,16 @@ extern FILE* VL_CVT_I_FP(IData lhs); static inline void* VL_CVT_Q_VP(QData lhs) { union { void* fp; QData q; } u; u.q=lhs; return u.fp; } /// Return QData from void* static inline QData VL_CVT_VP_Q(void* fp) { union { void* fp; QData q; } u; u.q=0; u.fp=fp; return u.q; } +/// Return double from QData (bits, not numerically) +static inline double VL_CVT_D_Q(QData lhs) { union { double d; QData q; } u; u.q=lhs; return u.d; } +/// Return QData from double (bits, not numerically) +static inline QData VL_CVT_Q_D(double lhs) { union { double d; QData q; } u; u.d=lhs; return u.q; } +/// Return double from QData (numeric) +static inline double VL_ITOR_D_I(IData lhs) { return ((double)((vlsint32_t)(lhs))); } +/// Return QData from double (numeric) +static inline IData VL_RTOI_I_D(double lhs) { return ((vlsint32_t)(trunc(lhs))); } +/// Return QData from double (numeric) +static inline IData VL_RTOIROUND_I_D(double lhs) { return ((vlsint32_t)(round(lhs))); } // Sign extend such that if MSB set, we get ffff_ffff, else 0s // (Requires clean input) @@ -403,9 +414,11 @@ void _VL_DEBUG_PRINT_W(int lbits, WDataInP iwp); #if defined(SYSTEMC_VERSION) && (SYSTEMC_VERSION>20011000) # define VL_TIME_I() ((IData)(sc_time_stamp().to_default_time_units()*VL_TIME_MULTIPLIER)) # define VL_TIME_Q() ((QData)(sc_time_stamp().to_default_time_units()*VL_TIME_MULTIPLIER)) +# define VL_TIME_D() ((double)(sc_time_stamp().to_default_time_units()*VL_TIME_MULTIPLIER)) #else # define VL_TIME_I() ((IData)(sc_time_stamp()*VL_TIME_MULTIPLIER)) # define VL_TIME_Q() ((QData)(sc_time_stamp()*VL_TIME_MULTIPLIER)) +# define VL_TIME_D() ((double)(sc_time_stamp()*VL_TIME_MULTIPLIER)) extern double sc_time_stamp(); #endif diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 456a3c996..09450f6e2 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -74,7 +74,9 @@ void AstNode::init() { m_clonep = NULL; m_cloneCnt = 0; // Attributes - m_signed = false; + m_numeric = (int)AstNumeric::UNSIGNED; + m_didWidth = false; + m_doingWidth = false; m_width = 0; m_widthMin = 0; m_user1p = NULL; @@ -873,7 +875,7 @@ bool AstNode::sameTreeIter(AstNode* node2p, bool ignNext) { if (this==NULL || node2p==NULL) return false; if (this->type() != node2p->type() || this->width() != node2p->width() - || this->isSigned() != node2p->isSigned() + || this->numeric() != node2p->numeric() || !this->same(node2p)) { return false; } diff --git a/src/V3Ast.h b/src/V3Ast.h index 979d0c732..0d0e94ef2 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -56,6 +56,36 @@ public: //###################################################################### +class AstNumeric { +public: + enum en { + UNSIGNED, + SIGNED, + DOUBLE + // Limited to 2 bits, unless change V3Ast's packing function + }; + enum en m_e; + const char* ascii() const { + static const char* names[] = { + "UNSIGNED","SIGNED","DOUBLE" + }; + return names[m_e]; + }; + inline AstNumeric () {} + inline AstNumeric (en _e) : m_e(_e) {} + explicit inline AstNumeric (int _e) : m_e(static_cast(_e)) {} + operator en () const { return m_e; } + inline bool isDouble() const { return m_e==DOUBLE; } + inline bool isSigned() const { return m_e==SIGNED; } + inline bool isUnsigned() const { return m_e==UNSIGNED; } + }; + inline bool operator== (AstNumeric lhs, AstNumeric rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator== (AstNumeric lhs, AstNumeric::en rhs) { return (lhs.m_e == rhs); } + inline bool operator== (AstNumeric::en lhs, AstNumeric rhs) { return (lhs == rhs.m_e); } + inline ostream& operator<<(ostream& os, AstNumeric rhs) { return os<m_width; m_widthMin=fromp->m_widthMin; }} - void widthSignedFrom(AstNode* fromp) { widthFrom(fromp); signedFrom(fromp); } - void signedFrom(AstNode* fromp) { if (fromp) { m_signed=fromp->m_signed; }} - void isSigned(bool flag) { m_signed=flag; } - bool isSigned() const { return m_signed; } + void widthSignedFrom(AstNode* fromp) { widthFrom(fromp); numericFrom(fromp); } + void numericFrom(AstNode* fromp) { numeric(fromp->numeric()); } + void numeric(AstNumeric flag) { m_numeric = (int)flag; if (flag.isDouble()) width(64,64); } + AstNumeric numeric() const { return AstNumeric(m_numeric); } + bool isDouble() const { return numeric().isDouble(); } + bool isSigned() const { return numeric().isSigned(); } + void isSigned(bool flag) { numeric(flag ? AstNumeric::SIGNED : AstNumeric::UNSIGNED); } + bool isUnsigned() const { return numeric().isUnsigned(); } + void didWidth(bool flag) { m_didWidth=flag; } + bool didWidth() const { return m_didWidth; } + void doingWidth(bool flag) { m_doingWidth=flag; } + bool doingWidth() const { return m_doingWidth; } bool isQuad() const { return (width()>VL_WORDSIZE && width()<=VL_QUADSIZE); } bool isWide() const { return (width()>VL_QUADSIZE); } @@ -986,6 +1026,8 @@ struct AstNodeUniop : public AstNodeMath { virtual void numberOperate(V3Number& out, const V3Number& lhs) = 0; // Set out to evaluation of a AstConst'ed lhs virtual bool cleanLhs() = 0; virtual bool sizeMattersLhs() = 0; // True if output result depends on lhs size + virtual bool signedFlavor() const { return false; } // Signed flavor of nodes with both flavors? + virtual bool doubleFlavor() const { return false; } // D flavor of nodes with both flavors? virtual int instrCount() const { return widthInstrs(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(AstNode*) const { return true; } @@ -1008,6 +1050,7 @@ struct AstNodeBiop : public AstNodeMath { virtual bool sizeMattersLhs() = 0; // True if output result depends on lhs size virtual bool sizeMattersRhs() = 0; // True if output result depends on rhs size virtual bool signedFlavor() const { return false; } // Signed flavor of nodes with both flavors? + virtual bool doubleFlavor() const { return false; } // D flavor of nodes with both flavors? virtual int instrCount() const { return widthInstrs(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(AstNode*) const { return true; } diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 5d164fecf..0cf580ce5 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -1210,6 +1210,7 @@ void EmitCStmts::displayNode(AstNode* nodep, AstScopeName* scopenamep, switch (tolower(pos[0])) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + case '.': // Digits, like %5d, etc. vfmt += pos[0]; inPct = true; // Get more digits @@ -1228,6 +1229,9 @@ void EmitCStmts::displayNode(AstNode* nodep, AstScopeName* scopenamep, case 'h': case 'x': displayArg(nodep,&elistp,isScan, vfmt,'x'); break; case 's': displayArg(nodep,&elistp,isScan, vfmt,'s'); break; + case 'e': displayArg(nodep,&elistp,isScan, vfmt,'e'); break; + case 'f': displayArg(nodep,&elistp,isScan, vfmt,'f'); break; + case 'g': displayArg(nodep,&elistp,isScan, vfmt,'g'); break; case 'm': { if (!scopenamep) nodep->v3fatalSrc("Display with %m but no AstScopeName"); string suffix = scopenamep->scopePrettyName(); diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 608b56875..e34fb8c4c 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -257,6 +257,7 @@ private: switch (tolower(ch)) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + case '.': inPct = true; break; case '%': break; // %% - just output a % diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 5d8690db4..56f4f9158 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -27,6 +27,8 @@ #include #include "V3Number.h" +#define MAX_SPRINTF_DOUBLE_SIZE 100 // Maximum characters with a sprintf %e/%f/%g (probably < 30) + //###################################################################### // Read class functions // CREATION @@ -44,6 +46,7 @@ void V3Number::width(int width, bool sized) { void V3Number::init (FileLine* fileline, int swidth) { m_fileline = fileline; m_signed = false; + m_double = false; m_autoExtend = false; m_fromString = false; width(swidth); @@ -298,6 +301,24 @@ V3Number& V3Number::setLong(uint32_t value) { m_value[0] = value; return *this; } +V3Number& V3Number::setLongS(vlsint32_t value) { + for (int i=0; iv3fatalSrc("Real operation on wrong sized number"); + } + m_double = true; + union { double d; uint32_t u[2]; } u; + u.d = value; + for (int i=2; iv3fatalSrc("Unknown $display format code for number: %"<v3fatalSrc("Real conversion on non real number"); + } + if (VL_UNLIKELY(width()!=64)) { + m_fileline->v3fatalSrc("Real operation on wrong sized number"); + } + union { double d; uint32_t u[2]; } u; + u.u[0] = m_value[0]; u.u[1] = m_value[1]; + return u.d; +} + vlsint32_t V3Number::toSInt() const { if (isSigned()) { uint32_t v = toUInt(); @@ -1053,7 +1100,7 @@ V3Number& V3Number::opShiftR (const V3Number& lhs, const V3Number& rhs) { V3Number& V3Number::opShiftRS (const V3Number& lhs, const V3Number& rhs) { // L(lhs) bit return // The spec says a unsigned >>> still acts as a normal >>. - // We presume it is signed; as that's V3Signed's job to convert to opShiftR + // We presume it is signed; as that's V3Width's job to convert to opShiftR if (rhs.isFourState()) return setAllBitsX(); setZero(); uint32_t rhsval = rhs.toUInt(); @@ -1438,3 +1485,74 @@ V3Number& V3Number::opCond (const V3Number& lhs, const V3Number& if1s, const V3 } return *this; } + +//====================================================================== +// Ops - Floating point + +V3Number& V3Number::opIToRD (const V3Number& lhs) { + return setDouble(lhs.toSInt()); +} +V3Number& V3Number::opRToIS (const V3Number& lhs) { + double v = trunc(lhs.toDouble()); + vlsint32_t i = v; // C converts from double to vlsint32 + return setLongS(i); +} +V3Number& V3Number::opRToIRoundS (const V3Number& lhs) { + double v = round(lhs.toDouble()); + vlsint32_t i = v; // C converts from double to vlsint32 + return setLongS(i); +} +V3Number& V3Number::opRealToBits (const V3Number& lhs) { + // Conveniently our internal format is identical so we can copy bits... + if (lhs.width()!=64 || this->width()!=64) { + m_fileline->v3fatalSrc("Real operation on wrong sized number"); + } + return opAssign(lhs); +} +V3Number& V3Number::opBitsToRealD (const V3Number& lhs) { + // Conveniently our internal format is identical so we can copy bits... + if (lhs.width()!=64 || this->width()!=64) { + m_fileline->v3fatalSrc("Real operation on wrong sized number"); + } + return opAssign(lhs); +} +V3Number& V3Number::opNegateD (const V3Number& lhs) { + return setDouble(- lhs.toDouble()); +} +V3Number& V3Number::opAddD (const V3Number& lhs, const V3Number& rhs) { + return setDouble(lhs.toDouble() + rhs.toDouble()); +} +V3Number& V3Number::opSubD (const V3Number& lhs, const V3Number& rhs) { + return setDouble(lhs.toDouble() - rhs.toDouble()); +} +V3Number& V3Number::opMulD (const V3Number& lhs, const V3Number& rhs) { + return setDouble(lhs.toDouble() * rhs.toDouble()); +} +V3Number& V3Number::opDivD (const V3Number& lhs, const V3Number& rhs) { + // On exceptions, we just generate 'inf' through floating point + // IEEE says it's implementation defined what happens + return setDouble(lhs.toDouble() / rhs.toDouble()); +} +V3Number& V3Number::opPowD (const V3Number& lhs, const V3Number& rhs) { + // On exceptions, we just generate 'inf' through floating point + // IEEE says it's implementation defined what happens + return setDouble(pow(lhs.toDouble(), rhs.toDouble())); +} +V3Number& V3Number::opEqD (const V3Number& lhs, const V3Number& rhs) { + return setSingleBits(lhs.toDouble() == rhs.toDouble()); +} +V3Number& V3Number::opNeqD (const V3Number& lhs, const V3Number& rhs) { + return setSingleBits(lhs.toDouble() != rhs.toDouble()); +} +V3Number& V3Number::opGtD (const V3Number& lhs, const V3Number& rhs) { + return setSingleBits(lhs.toDouble() > rhs.toDouble()); +} +V3Number& V3Number::opGteD (const V3Number& lhs, const V3Number& rhs) { + return setSingleBits(lhs.toDouble() >= rhs.toDouble()); +} +V3Number& V3Number::opLtD (const V3Number& lhs, const V3Number& rhs) { + return setSingleBits(lhs.toDouble() < rhs.toDouble()); +} +V3Number& V3Number::opLteD (const V3Number& lhs, const V3Number& rhs) { + return setSingleBits(lhs.toDouble() <= rhs.toDouble()); +} diff --git a/src/V3Number.h b/src/V3Number.h index 60fea552c..12716f7cc 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -35,6 +35,7 @@ class V3Number { int m_width; // Width as specified/calculated. bool m_sized:1; // True if the user specified the width, else we track it. bool m_signed:1; // True if signed value + bool m_double:1; // True if double real value bool m_fromString:1; // True if from string bool m_autoExtend:1; // True if SystemVerilog extend-to-any-width FileLine* m_fileline; @@ -50,6 +51,8 @@ public: V3Number& setZero(); V3Number& setQuad(vluint64_t value); V3Number& setLong(uint32_t value); + V3Number& setLongS(vlsint32_t value); + V3Number& setDouble(double value); void setBit (int bit, char value) { // Note must be pre-zeroed! if (bit>=m_width) return; if (value=='0'||value==0) m_value [bit/32] &= ~(1UL<<(bit&31)); @@ -132,6 +135,7 @@ public: bool autoExtend() const { return m_autoExtend; } bool isFromString() const { return m_fromString; } bool isSigned() const { return m_signed; } // Only correct for parsing of numbers from strings, otherwise not used (use AstConst::isSigned()) + bool isDouble() const { return m_double; } // Only correct for parsing of numbers from strings, otherwise not used (use AstConst::isSigned()) bool isNegative() const { return bitIs1(width()-1); } bool isFourState() const { for (int i=0;ididSigning()) return; nodep->didSigning(true); nodep->iterateChildren(*this); - nodep->signedFrom(nodep->dtypep()); + nodep->numericFrom(nodep->dtypep()); } virtual void visit(AstNodeVarRef* nodep, AstNUser*) { nodep->varp()->iterate(*this); - nodep->signedFrom(nodep->varp()); + nodep->numericFrom(nodep->varp()); } virtual void visit(AstEnumItemRef* nodep, AstNUser*) { nodep->itemp()->iterate(*this); - nodep->signedFrom(nodep->itemp()); + nodep->numericFrom(nodep->itemp()); } virtual void visit(AstCast* nodep, AstNUser*) { nodep->lhsp()->iterate(*this); nodep->dtypep()->iterate(*this); - nodep->signedFrom(nodep->dtypep()); + nodep->numericFrom(nodep->dtypep()); } virtual void visit(AstConst* nodep, AstNUser*) { // The node got setup with the signed state of the node. @@ -178,18 +178,18 @@ private: nodep->didSigning(true); nodep->iterateChildren(*this); if (nodep->fvarp()) { - nodep->signedFrom(nodep->fvarp()); // Which will get it from fvarp()->dtypep() + 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->signedFrom(nodep->taskp()); + nodep->numericFrom(nodep->taskp()); } virtual void visit(AstRefDType* nodep, AstNUser*) { nodep->iterateChildren(*this); if (nodep->defp()) nodep->defp()->iterate(*this); - nodep->signedFrom(nodep->skipRefp()); + nodep->numericFrom(nodep->skipRefp()); } virtual void visit(AstNodeIf* nodep, AstNUser*) { if (!nodep->castGenIf()) { // for m_paramsOnly diff --git a/src/V3WidthSel.cpp b/src/V3WidthSel.cpp index 94c38c468..bd89946cc 100644 --- a/src/V3WidthSel.cpp +++ b/src/V3WidthSel.cpp @@ -111,12 +111,12 @@ private: // We must make sure sub gets sign of original value AstNode* newp = new AstSub(lhsp->fileline(), lhsp, new AstConst(lhsp->fileline(), AstConst::Unsized32(), rhs)); - newp->signedFrom(lhsp); + newp->numericFrom(lhsp); return newp; } else { // rhs < 0; AstNode* newp = new AstAdd(lhsp->fileline(), lhsp, new AstConst(lhsp->fileline(), AstConst::Unsized32(), -rhs)); - newp->signedFrom(lhsp); + newp->numericFrom(lhsp); return newp; } } @@ -126,7 +126,7 @@ private: AstNode* newp = new AstSub(rhsp->fileline(), new AstConst(rhsp->fileline(), AstConst::Unsized32(), lhs), rhsp); - newp->signedFrom(rhsp); // Important as AstSub default is lhs's sign + newp->numericFrom(rhsp); // Important as AstSub default is lhs's sign return newp; }