Internals: Real2: Create numeric class; no functional change intended

This commit is contained in:
Wilson Snyder 2011-07-23 19:58:34 -04:00
parent 59c3c536c7
commit d88d85c172
10 changed files with 235 additions and 19 deletions

View File

@ -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);

View File

@ -35,6 +35,7 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
// <iostream> avoided to reduce compile time
// <string> 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

View File

@ -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;
}

View File

@ -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<en>(_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<<rhs.ascii(); }
//######################################################################
class AstPragmaType {
public:
enum en {
@ -689,7 +719,9 @@ class AstNode {
static int s_cloneCntGbl; // Count of which userp is set
// Attributes
bool m_signed:1; // Node is signed
uint32_t m_numeric:2; // Node is real/signed - important that bitfields remain unsigned
bool m_didWidth:1; // Did V3Width computation
bool m_doingWidth:1; // Inside V3Width
// // Space for more bools here
int m_width; // Bit width of operation
@ -812,10 +844,18 @@ public:
bool widthSized() const { return !m_widthMin || m_widthMin==m_width; }
void width(int width, int sized) { m_width=width; m_widthMin=sized; }
void widthFrom(AstNode* fromp) { if (fromp) { m_width=fromp->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; }

View File

@ -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();

View File

@ -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 %

View File

@ -27,6 +27,8 @@
#include <algorithm>
#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; i<words(); i++) m_value[i]=m_valueX[i] = 0;
union { uint32_t u; vlsint32_t s; } u;
u.s = value;
m_value[0] = u.u;
return *this;
}
V3Number& V3Number::setDouble(double value) {
if (VL_UNLIKELY(width()!=64)) {
m_fileline->v3fatalSrc("Real operation on wrong sized number");
}
m_double = true;
union { double d; uint32_t u[2]; } u;
u.d = value;
for (int i=2; i<words(); i++) m_value[i]=m_valueX[i] = 0;
m_value[0] = u.u[0]; m_value[1] = u.u[1];
return *this;
}
V3Number& V3Number::setSingleBits(char value) {
for (int i=1/*upper*/; i<words(); i++) m_value[i]=m_valueX[i] = 0;
m_value[0] = (value=='1'||value=='x'||value==1||value==3);
@ -333,6 +354,10 @@ V3Number& V3Number::setMask(int nbits) {
string V3Number::ascii(bool prefixed, bool cleanVerilog) const {
ostringstream out;
if (isDouble()) {
out<<toDouble();
return out.str();
}
if (prefixed) {
if (sized()) {
out<<width()<<"'";
@ -375,6 +400,9 @@ bool V3Number::displayedFmtLegal(char format) {
case 'b': return true;
case 'c': return true;
case 'd': return true; // Unsigned decimal
case 'e': return true;
case 'f': return true;
case 'g': return true;
case 'h': return true;
case 'o': return true;
case 's': return true;
@ -390,7 +418,7 @@ string V3Number::displayed(const string& vformat) const {
UASSERT(pos != vformat.end() && pos[0]=='%', "display with non format argument "<<*this);
pos++;
string fmtsize;
for (; pos != vformat.end() && isdigit(pos[0]); pos++) {
for (; pos != vformat.end() && (isdigit(pos[0]) || pos[0]=='.'); pos++) {
fmtsize += pos[0];
}
string str;
@ -479,6 +507,13 @@ string V3Number::displayed(const string& vformat) const {
}
return str;
}
case 'e':
case 'f':
case 'g': {
char tmp[MAX_SPRINTF_DOUBLE_SIZE];
sprintf(tmp, vformat.c_str(), toDouble());
return tmp;
}
default:
m_fileline->v3fatalSrc("Unknown $display format code for number: %"<<pos[0]);
return "ERR";
@ -494,6 +529,18 @@ uint32_t V3Number::toUInt() const {
return m_value[0];
}
double V3Number::toDouble() const {
if (VL_UNLIKELY(!isDouble())) {
m_fileline->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());
}

View File

@ -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;i<words();i++) {if (m_valueX[i]) return true;} return false; }
bool hasZ() const { for(int i=0;i<words();i++) {if((~m_value[i]) & m_valueX[i]) return true;} return false;}
@ -151,6 +155,7 @@ public:
vluint64_t toUQuad() const;
vlsint64_t toSQuad() const;
string toString() const;
double toDouble() const;
uint32_t toHash() const;
uint32_t dataWord(int word) const;
uint32_t countOnes() const;
@ -236,6 +241,25 @@ public:
V3Number& opLte (const V3Number& lhs, const V3Number& rhs);
V3Number& opLteS (const V3Number& lhs, const V3Number& rhs); // Signed
// "D" - double (aka real) math
V3Number& opIToRD (const V3Number& lhs);
V3Number& opRToIS (const V3Number& lhs);
V3Number& opRToIRoundS (const V3Number& lhs);
V3Number& opRealToBits (const V3Number& lhs);
V3Number& opBitsToRealD(const V3Number& lhs);
V3Number& opNegateD (const V3Number& lhs);
V3Number& opAddD (const V3Number& lhs, const V3Number& rhs);
V3Number& opSubD (const V3Number& lhs, const V3Number& rhs);
V3Number& opMulD (const V3Number& lhs, const V3Number& rhs);
V3Number& opDivD (const V3Number& lhs, const V3Number& rhs);
V3Number& opPowD (const V3Number& lhs, const V3Number& rhs);
// Comparisons
V3Number& opEqD (const V3Number& lhs, const V3Number& rhs);
V3Number& opNeqD (const V3Number& lhs, const V3Number& rhs);
V3Number& opGtD (const V3Number& lhs, const V3Number& rhs);
V3Number& opGteD (const V3Number& lhs, const V3Number& rhs);
V3Number& opLtD (const V3Number& lhs, const V3Number& rhs);
V3Number& opLteD (const V3Number& lhs, const V3Number& rhs);
};
inline ostream& operator<<(ostream& os, V3Number rhs) { return os<<rhs.ascii(); }

View File

@ -152,20 +152,20 @@ private:
if (nodep->didSigning()) 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

View File

@ -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;
}