mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
Add 'string' printing and comparisons, bug746, bug747, etc.
This commit is contained in:
parent
8b457b9b66
commit
43be4cf2b5
2
Changes
2
Changes
@ -11,6 +11,8 @@ indicates the contributor was also the author of the fix; Thanks!
|
||||
|
||||
** SystemPerl mode is deprecated and now untested.
|
||||
|
||||
*** Add 'string' printing and comparisons, bug746, bug747, etc.
|
||||
|
||||
*** Inline C functions that are used only once, msg1525. [Jie Xu]
|
||||
|
||||
|
||||
|
@ -2597,8 +2597,9 @@ All specify blocks and timing checks are ignored.
|
||||
|
||||
=item string
|
||||
|
||||
String is supported only to the point that they can be passed to DPI
|
||||
imports.
|
||||
String is supported only to the point that they can be assigned,
|
||||
concatenated, compared, and passed to DPI imports. Standard method calls
|
||||
on strings are not supported.
|
||||
|
||||
=item timeunit, timeprecision
|
||||
|
||||
|
@ -343,6 +343,12 @@ void _vl_vsformat(string& output, const char* formatp, va_list ap) {
|
||||
output += cstrp;
|
||||
break;
|
||||
}
|
||||
case '@': { // Verilog/C++ string
|
||||
va_arg(ap, int); // # bits is ignored
|
||||
const string* cstrp = va_arg(ap, const string*);
|
||||
output += *cstrp;
|
||||
break;
|
||||
}
|
||||
case 'e':
|
||||
case 'f':
|
||||
case 'g': {
|
||||
|
@ -41,13 +41,24 @@ inline string VL_CVT_PACK_STR_NQ(QData lhs) {
|
||||
IData lw[2]; VL_SET_WQ(lw, lhs);
|
||||
return VL_CVT_PACK_STR_NW(2, lw);
|
||||
}
|
||||
inline string VL_CVT_PACK_STR_NQ(string lhs) {
|
||||
inline string VL_CVT_PACK_STR_NQ(const string& lhs) {
|
||||
return lhs;
|
||||
}
|
||||
inline string VL_CVT_PACK_STR_NI(IData lhs) {
|
||||
IData lw[1]; lw[0] = lhs;
|
||||
return VL_CVT_PACK_STR_NW(1, lw);
|
||||
}
|
||||
inline string VL_CONCATN_NNN(const string& lhs, const string& rhs) {
|
||||
return lhs+rhs;
|
||||
}
|
||||
inline string VL_REPLICATEN_NNQ(int,int,int rbits, const string& lhs, IData rep) {
|
||||
string out; out.reserve(lhs.length() * rep);
|
||||
for (unsigned times=0; times<rep; times++) out += lhs;
|
||||
return out;
|
||||
}
|
||||
inline string VL_REPLICATEN_NNI(int obits,int lbits,int rbits, const string& lhs, IData rep) {
|
||||
return VL_REPLICATEN_NNQ(obits,lbits,rbits,lhs,rep);
|
||||
}
|
||||
|
||||
extern void VL_SFORMAT_X(int obits_ignored, string &output, const char* formatp, ...);
|
||||
extern string VL_SFORMATF_NX(const char* formatp, ...);
|
||||
|
16
src/V3Ast.h
16
src/V3Ast.h
@ -384,9 +384,8 @@ public:
|
||||
bool isOpaque() const { // IE not a simple number we can bit optimize
|
||||
return (m_e==STRING || m_e==SCOPEPTR || m_e==CHARPTR || m_e==DOUBLE || m_e==FLOAT);
|
||||
}
|
||||
bool isDouble() const {
|
||||
return (m_e==DOUBLE);
|
||||
}
|
||||
bool isDouble() const { return m_e==DOUBLE; }
|
||||
bool isString() const { return m_e==STRING; }
|
||||
};
|
||||
inline bool operator== (AstBasicDTypeKwd lhs, AstBasicDTypeKwd rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstBasicDTypeKwd lhs, AstBasicDTypeKwd::en rhs) { return (lhs.m_e == rhs); }
|
||||
@ -1008,6 +1007,7 @@ public:
|
||||
static int instrCountDouble() { return 8; } ///< Instruction cycles to convert or do floats
|
||||
static int instrCountDoubleDiv() { return 40; } ///< Instruction cycles to divide floats
|
||||
static int instrCountDoubleTrig() { return 200; } ///< Instruction cycles to do triganomics
|
||||
static int instrCountString() { return 100; } ///< Instruction cycles to do string ops
|
||||
static int instrCountCall() { return instrCountBranch()+10; } ///< Instruction cycles to call subroutine
|
||||
static int instrCountTime() { return instrCountCall()+5; } ///< Instruction cycles to determine simulation time
|
||||
|
||||
@ -1042,6 +1042,7 @@ public:
|
||||
bool isWide() const { return (width()>VL_QUADSIZE); }
|
||||
bool isDouble() const;
|
||||
bool isSigned() const;
|
||||
bool isString() const;
|
||||
|
||||
AstNUser* user1p() const {
|
||||
// Slows things down measurably, so disabled by default
|
||||
@ -1119,6 +1120,7 @@ public:
|
||||
void dtypeSetLogicSized(int width, int widthMin, AstNumeric numeric) { dtypep(findLogicDType(width,widthMin,numeric)); }
|
||||
void dtypeSetLogicBool() { dtypep(findLogicBoolDType()); }
|
||||
void dtypeSetDouble() { dtypep(findDoubleDType()); }
|
||||
void dtypeSetString() { dtypep(findStringDType()); }
|
||||
void dtypeSetSigned32() { dtypep(findSigned32DType()); }
|
||||
void dtypeSetUInt32() { dtypep(findUInt32DType()); } // Twostate
|
||||
void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate
|
||||
@ -1126,6 +1128,7 @@ public:
|
||||
// Data type locators
|
||||
AstNodeDType* findLogicBoolDType() { return findBasicDType(AstBasicDTypeKwd::LOGIC); }
|
||||
AstNodeDType* findDoubleDType() { return findBasicDType(AstBasicDTypeKwd::DOUBLE); }
|
||||
AstNodeDType* findStringDType() { return findBasicDType(AstBasicDTypeKwd::STRING); }
|
||||
AstNodeDType* findSigned32DType() { return findBasicDType(AstBasicDTypeKwd::INTEGER); }
|
||||
AstNodeDType* findUInt32DType() { return findBasicDType(AstBasicDTypeKwd::UINT32); } // Twostate
|
||||
AstNodeDType* findUInt64DType() { return findBasicDType(AstBasicDTypeKwd::UINT64); } // Twostate
|
||||
@ -1263,8 +1266,9 @@ public:
|
||||
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 bool signedFlavor() const { return false; } // Signed flavor of nodes with both flavors?
|
||||
virtual bool stringFlavor() const { return false; } // N 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; }
|
||||
@ -1287,8 +1291,9 @@ public:
|
||||
virtual bool cleanRhs() = 0; // True if RHS must have extra upper bits zero
|
||||
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 bool signedFlavor() const { return false; } // Signed flavor of nodes with both flavors?
|
||||
virtual bool stringFlavor() const { return false; } // N 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; }
|
||||
@ -1886,6 +1891,7 @@ inline int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() :
|
||||
inline bool AstNode::width1() const { return dtypep() && dtypep()->width()==1; } // V3Const uses to know it can optimize
|
||||
inline int AstNode::widthInstrs() const { return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1)); }
|
||||
inline bool AstNode::isDouble() const { return dtypep() && dtypep()->castBasicDType() && dtypep()->castBasicDType()->isDouble(); }
|
||||
inline bool AstNode::isString() const { return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString(); }
|
||||
inline bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
|
||||
|
||||
inline bool AstNode::isZero() { return (this->castConst() && this->castConst()->num().isEqZero()); }
|
||||
|
@ -737,7 +737,10 @@ void AstNode::dump(ostream& str) {
|
||||
} else { // V3Broken will throw an error
|
||||
if (dtypep()) str<<" %Error-dtype-exp=null,got="<<(void*)dtypep();
|
||||
}
|
||||
if (name()!="") str<<" "<<V3Number::quoteNameControls(name());
|
||||
if (name()!="") {
|
||||
if (castConst()) str<<" "<<name(); // Already quoted
|
||||
else str<<" "<<V3Number::quoteNameControls(name());
|
||||
}
|
||||
}
|
||||
|
||||
void AstAlways::dump(ostream& str) {
|
||||
@ -850,7 +853,10 @@ void AstNodeDType::dumpSmall(ostream& str) {
|
||||
<<((isSigned()&&!isDouble())?"s":"")
|
||||
<<(isNosign()?"n":"")
|
||||
<<(isDouble()?"d":"")
|
||||
<<"w"<<(widthSized()?"":"u")<<width();
|
||||
<<(isString()?"str":"");
|
||||
if (!isDouble() && !isString()) {
|
||||
str<<"w"<<(widthSized()?"":"u")<<width();
|
||||
}
|
||||
if (!widthSized()) str<<"/"<<widthMin();
|
||||
str<<")";
|
||||
}
|
||||
|
170
src/V3AstNodes.h
170
src/V3AstNodes.h
@ -49,6 +49,8 @@ public:
|
||||
,m_num(num) {
|
||||
if (m_num.isDouble()) {
|
||||
dtypeSetDouble();
|
||||
} else if (m_num.isString()) {
|
||||
dtypeSetString();
|
||||
} else {
|
||||
dtypeSetLogicSized(m_num.width(), m_num.sized()?0:m_num.widthMin(),
|
||||
m_num.isSigned() ? AstNumeric::SIGNED
|
||||
@ -74,6 +76,10 @@ public:
|
||||
AstConst(FileLine* fl, RealDouble, double num)
|
||||
:AstNodeMath(fl)
|
||||
,m_num(V3Number(fl,64)) { m_num.setDouble(num); dtypeSetDouble(); }
|
||||
class String {}; // for creator type-overload selection
|
||||
AstConst(FileLine* fl, String, const string& num)
|
||||
:AstNodeMath(fl)
|
||||
,m_num(V3Number(V3Number::String(), fl, num)) { dtypeSetString(); }
|
||||
class LogicFalse {};
|
||||
AstConst(FileLine* fl, LogicFalse) // Shorthand const 0, know the dtype should be a logic of size 1
|
||||
:AstNodeMath(fl)
|
||||
@ -100,34 +106,6 @@ public:
|
||||
bool isEqAllOnesV() const { return num().isEqAllOnes(widthMin()); }
|
||||
};
|
||||
|
||||
class AstConstString : public AstNodeMath {
|
||||
// A constant string
|
||||
private:
|
||||
string m_name;
|
||||
public:
|
||||
AstConstString(FileLine* fl, const string& name)
|
||||
: AstNodeMath(fl), m_name(name) {
|
||||
rewidth();
|
||||
}
|
||||
void rewidth() {
|
||||
if (m_name.length()==0) {
|
||||
dtypeSetLogicSized(1,1,AstNumeric::UNSIGNED); // 0 width isn't allowed due to historic special cases
|
||||
} else {
|
||||
dtypeSetLogicSized(((int)m_name.length())*8, ((int)m_name.length())*8, AstNumeric::UNSIGNED);
|
||||
}
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(ConstString, CONSTSTRING)
|
||||
virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially
|
||||
virtual string emitC() { V3ERROR_NA; return ""; }
|
||||
virtual bool cleanOut() { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(name()); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return name()==samep->castConstString()->name(); }
|
||||
virtual int instrCount() const { return 2; } // C just loads a pointer
|
||||
virtual string name() const { return m_name; }
|
||||
void name(const string& flag) { m_name = flag; rewidth(); }
|
||||
};
|
||||
|
||||
class AstRange : public AstNode {
|
||||
// Range specification, for use under variables and cells
|
||||
private:
|
||||
@ -400,6 +378,7 @@ public:
|
||||
bool isBitLogic() const { return keyword().isBitLogic(); }
|
||||
bool isDouble() const { return keyword().isDouble(); }
|
||||
bool isOpaque() const { return keyword().isOpaque(); }
|
||||
bool isString() const { return keyword().isString(); }
|
||||
bool isSloppy() const { return keyword().isSloppy(); }
|
||||
bool isZeroInit() const { return keyword().isZeroInit(); }
|
||||
bool isRanged() const { return rangep() || m.m_nrange.ranged(); }
|
||||
@ -935,6 +914,7 @@ private:
|
||||
bool m_isPulldown:1; // Tri0
|
||||
bool m_isPullup:1; // Tri1
|
||||
bool m_isIfaceParent:1; // dtype is reference to interface present in this module
|
||||
bool m_noSubst:1; // Do not substitute out references
|
||||
bool m_trace:1; // Trace this variable
|
||||
|
||||
void init() {
|
||||
@ -947,6 +927,7 @@ private:
|
||||
m_attrClockEn=false; m_attrScBv=false; m_attrIsolateAssign=false; m_attrSFormat=false;
|
||||
m_fileDescr=false; m_isConst=false; m_isStatic=false; m_isPulldown=false; m_isPullup=false;
|
||||
m_isIfaceParent=false;
|
||||
m_noSubst=false;
|
||||
m_trace=false;
|
||||
}
|
||||
public:
|
||||
@ -1039,7 +1020,9 @@ public:
|
||||
void isIfaceParent(bool flag) { m_isIfaceParent = flag; }
|
||||
void funcLocal(bool flag) { m_funcLocal = flag; }
|
||||
void funcReturn(bool flag) { m_funcReturn = flag; }
|
||||
void trace(bool flag) { m_trace=flag; }
|
||||
void noSubst(bool flag) { m_noSubst=flag; }
|
||||
bool noSubst() const { return m_noSubst; }
|
||||
void trace(bool flag) { m_trace=flag; }
|
||||
// METHODS
|
||||
virtual void name(const string& name) { m_name = name; }
|
||||
virtual string directionName() const { return (isInout() ? "inout" : isInput() ? "input"
|
||||
@ -3521,10 +3504,10 @@ public:
|
||||
};
|
||||
|
||||
class AstCvtPackString : public AstNodeUniop {
|
||||
// Convert to Verilator Packed Pack (aka Pack)
|
||||
// Convert to Verilator Packed String (aka verilog "string")
|
||||
public:
|
||||
AstCvtPackString(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) {
|
||||
dtypeSetUInt64(); } // Really, width should be dtypep -> STRING
|
||||
dtypeSetString(); } // Really, width should be dtypep -> STRING
|
||||
ASTNODE_NODE_FUNCS(CvtPackString, CVTPACKSTRING)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs) { V3ERROR_NA; }
|
||||
virtual string emitVerilog() { return "%f$_CAST(%l)"; }
|
||||
@ -3794,6 +3777,21 @@ public:
|
||||
virtual int instrCount() const { return instrCountDouble(); }
|
||||
virtual bool doubleFlavor() const { return true; }
|
||||
};
|
||||
class AstEqN : public AstNodeBiCom {
|
||||
public:
|
||||
AstEqN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) {
|
||||
dtypeSetLogicBool(); }
|
||||
ASTNODE_NODE_FUNCS(EqN, EQN)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opEqN(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 instrCountString(); }
|
||||
virtual bool stringFlavor() const { return true; }
|
||||
};
|
||||
class AstNeq : public AstNodeBiCom {
|
||||
public:
|
||||
AstNeq(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) {
|
||||
@ -3822,6 +3820,21 @@ public:
|
||||
virtual int instrCount() const { return instrCountDouble(); }
|
||||
virtual bool doubleFlavor() const { return true; }
|
||||
};
|
||||
class AstNeqN : public AstNodeBiCom {
|
||||
public:
|
||||
AstNeqN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) {
|
||||
dtypeSetLogicBool(); }
|
||||
ASTNODE_NODE_FUNCS(NeqN, NEQN)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opNeqN(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 instrCountString(); }
|
||||
virtual bool stringFlavor() const { return true; }
|
||||
};
|
||||
class AstLt : public AstNodeBiop {
|
||||
public:
|
||||
AstLt(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
|
||||
@ -3864,6 +3877,21 @@ public:
|
||||
virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;}
|
||||
virtual bool signedFlavor() const { return true; }
|
||||
};
|
||||
class AstLtN : public AstNodeBiop {
|
||||
public:
|
||||
AstLtN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
|
||||
dtypeSetLogicBool(); }
|
||||
ASTNODE_NODE_FUNCS(LtN, LTN)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLtN(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 instrCountString(); }
|
||||
virtual bool stringFlavor() const { return true; }
|
||||
};
|
||||
class AstGt : public AstNodeBiop {
|
||||
public:
|
||||
AstGt(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
|
||||
@ -3906,6 +3934,21 @@ public:
|
||||
virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;}
|
||||
virtual bool signedFlavor() const { return true; }
|
||||
};
|
||||
class AstGtN : public AstNodeBiop {
|
||||
public:
|
||||
AstGtN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
|
||||
dtypeSetLogicBool(); }
|
||||
ASTNODE_NODE_FUNCS(GtN, GTN)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGtN(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 instrCountString(); }
|
||||
virtual bool stringFlavor() const { return true; }
|
||||
};
|
||||
class AstGte : public AstNodeBiop {
|
||||
public:
|
||||
AstGte(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
|
||||
@ -3949,6 +3992,21 @@ public:
|
||||
virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;}
|
||||
virtual bool signedFlavor() const { return true; }
|
||||
};
|
||||
class AstGteN : public AstNodeBiop {
|
||||
public:
|
||||
AstGteN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
|
||||
dtypeSetLogicBool(); }
|
||||
ASTNODE_NODE_FUNCS(GteN, GTEN)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGteN(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 instrCountString(); }
|
||||
virtual bool stringFlavor() const { return true; }
|
||||
};
|
||||
class AstLte : public AstNodeBiop {
|
||||
public:
|
||||
AstLte(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
|
||||
@ -3992,6 +4050,21 @@ public:
|
||||
virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;}
|
||||
virtual bool signedFlavor() const { return true; }
|
||||
};
|
||||
class AstLteN : public AstNodeBiop {
|
||||
public:
|
||||
AstLteN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
|
||||
dtypeSetLogicBool(); }
|
||||
ASTNODE_NODE_FUNCS(LteN, LTEN)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLteN(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 instrCountString(); }
|
||||
virtual bool stringFlavor() const { return true; }
|
||||
};
|
||||
class AstShiftL : public AstNodeBiop {
|
||||
public:
|
||||
AstShiftL(FileLine* fl, AstNode* lhsp, AstNode* rhsp, int setwidth=0)
|
||||
@ -4352,6 +4425,22 @@ public:
|
||||
virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;}
|
||||
virtual int instrCount() const { return widthInstrs()*2; }
|
||||
};
|
||||
class AstConcatN : public AstNodeBiop {
|
||||
// String concatenate
|
||||
public:
|
||||
AstConcatN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
|
||||
dtypeSetString();
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(ConcatN, CONCATN)
|
||||
virtual string emitVerilog() { return "%f{%l, %k%r}"; }
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opConcatN(lhs,rhs); }
|
||||
virtual string emitC() { return "VL_CONCATN_NNN(%li, %ri)"; }
|
||||
virtual bool cleanOut() {return true;}
|
||||
virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;}
|
||||
virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;}
|
||||
virtual int instrCount() const { return instrCountString(); }
|
||||
virtual bool stringFlavor() const { return true; }
|
||||
};
|
||||
class AstReplicate : public AstNodeBiop {
|
||||
// Also used as a "Uniop" flavor of Concat, e.g. "{a}"
|
||||
// Verilog {rhs{lhs}} - Note rhsp() is the replicate value, not the lhsp()
|
||||
@ -4377,6 +4466,25 @@ public:
|
||||
virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;}
|
||||
virtual int instrCount() const { return widthInstrs()*2; }
|
||||
};
|
||||
class AstReplicateN : public AstNodeBiop {
|
||||
// String replicate
|
||||
private:
|
||||
void init() { dtypeSetString(); }
|
||||
public:
|
||||
AstReplicateN(FileLine* fl, AstNode* lhsp, AstNode* rhsp)
|
||||
: AstNodeBiop(fl, lhsp, rhsp) { init(); }
|
||||
AstReplicateN(FileLine* fl, AstNode* lhsp, uint32_t repCount)
|
||||
: AstNodeBiop(fl, lhsp, new AstConst(fl, repCount)) { init(); }
|
||||
ASTNODE_NODE_FUNCS(ReplicateN, REPLICATEN)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opReplN(lhs,rhs); }
|
||||
virtual string emitVerilog() { return "%f{%r{%k%l}}"; }
|
||||
virtual string emitC() { return "VL_REPLICATEN_NN%rq(0,0,%rw, %li, %ri)"; }
|
||||
virtual bool cleanOut() {return false;}
|
||||
virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;}
|
||||
virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;}
|
||||
virtual int instrCount() const { return widthInstrs()*2; }
|
||||
virtual bool stringFlavor() const { return true; }
|
||||
};
|
||||
class AstStreamL : public AstNodeStream {
|
||||
// Verilog {rhs{lhs}} - Note rhsp() is the slice size, not the lhsp()
|
||||
public:
|
||||
|
@ -624,7 +624,7 @@ private:
|
||||
void replaceConstString (AstNode* oldp, const string& num) {
|
||||
// Replace oldp node with a constant set to specified value
|
||||
UASSERT (oldp, "Null old\n");
|
||||
AstNode* newp = new AstConstString(oldp->fileline(), num);
|
||||
AstNode* newp = new AstConst(oldp->fileline(), AstConst::String(), num);
|
||||
if (debug()>5) oldp->dumpTree(cout," const_old: ");
|
||||
if (debug()>5) newp->dumpTree(cout," _new: ");
|
||||
oldp->replaceWith(newp);
|
||||
@ -1457,6 +1457,7 @@ private:
|
||||
&& v3Global.opt.oConst()
|
||||
&& !(nodep->varp()->isFuncLocal() // Default value, not a "known" constant for this usage
|
||||
&& nodep->varp()->isInput())
|
||||
&& !nodep->varp()->noSubst()
|
||||
&& !nodep->varp()->isSigPublic())
|
||||
|| nodep->varp()->isParam())) {
|
||||
if (operandConst(valuep)) {
|
||||
@ -2141,22 +2142,28 @@ private:
|
||||
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 ("AstEqN {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 ("AstGtN {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 ("AstGteN {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 ("AstLtN {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 ("AstLteN {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 ("AstNeqN {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)");
|
||||
|
@ -620,6 +620,10 @@ public:
|
||||
// Put out constant set to the specified variable, or given variable in a string
|
||||
if (nodep->num().isFourState()) {
|
||||
nodep->v3error("Unsupported: 4-state numbers in this context");
|
||||
} else if (nodep->num().isString()) {
|
||||
putbs("string(");
|
||||
putsQuoted(nodep->num().toString());
|
||||
puts(")");
|
||||
} else if (nodep->isWide()) {
|
||||
putbs("VL_CONST_W_");
|
||||
puts(cvtToStr(VL_WORDS_I(nodep->num().widthMin())));
|
||||
@ -680,9 +684,6 @@ public:
|
||||
emitConstant(nodep, NULL, "");
|
||||
}
|
||||
}
|
||||
virtual void visit(AstConstString* nodep, AstNUser*) {
|
||||
putsQuoted(nodep->name());
|
||||
}
|
||||
|
||||
// Just iterate
|
||||
virtual void visit(AstNetlist* nodep, AstNUser*) {
|
||||
@ -1168,17 +1169,20 @@ void EmitCStmts::emitOpName(AstNode* nodep, const string& format,
|
||||
|
||||
struct EmitDispState {
|
||||
string m_format; // "%s" and text from user
|
||||
vector<char> m_argsChar; // Format of each argument to be printed
|
||||
vector<AstNode*> m_argsp; // Each argument to be printed
|
||||
vector<string> m_argsFunc; // Function before each argument to be printed
|
||||
EmitDispState() { clear(); }
|
||||
void clear() {
|
||||
m_format = "";
|
||||
m_argsChar.clear();
|
||||
m_argsp.clear();
|
||||
m_argsFunc.clear();
|
||||
}
|
||||
void pushFormat(const string& fmt) { m_format += fmt; }
|
||||
void pushFormat(char fmt) { m_format += fmt; }
|
||||
void pushArg(AstNode* nodep, const string& func) {
|
||||
void pushArg(char fmtChar, AstNode* nodep, const string& func) {
|
||||
m_argsChar.push_back(fmtChar);
|
||||
m_argsp.push_back(nodep); m_argsFunc.push_back(func);
|
||||
}
|
||||
} emitDispState;
|
||||
@ -1231,6 +1235,7 @@ void EmitCStmts::displayEmit(AstNode* nodep, bool isScan) {
|
||||
// Arguments
|
||||
for (unsigned i=0; i < emitDispState.m_argsp.size(); i++) {
|
||||
puts(",");
|
||||
char fmt = emitDispState.m_argsChar[i];
|
||||
AstNode* argp = emitDispState.m_argsp[i];
|
||||
string func = emitDispState.m_argsFunc[i];
|
||||
ofp()->indentInc();
|
||||
@ -1238,8 +1243,10 @@ void EmitCStmts::displayEmit(AstNode* nodep, bool isScan) {
|
||||
if (func!="") puts(func);
|
||||
if (argp) {
|
||||
if (isScan) puts("&(");
|
||||
else if (fmt == '@') puts("&(");
|
||||
argp->iterate(*this);
|
||||
if (isScan) puts(")");
|
||||
else if (fmt == '@') puts(")");
|
||||
}
|
||||
ofp()->indentDec();
|
||||
}
|
||||
@ -1288,8 +1295,8 @@ void EmitCStmts::displayArg(AstNode* dispp, AstNode** elistp, bool isScan,
|
||||
pfmt = string("%") + vfmt + fmtLetter;
|
||||
}
|
||||
emitDispState.pushFormat(pfmt);
|
||||
emitDispState.pushArg(NULL,cvtToStr(argp->widthMin()));
|
||||
emitDispState.pushArg(argp,"");
|
||||
emitDispState.pushArg(' ',NULL,cvtToStr(argp->widthMin()));
|
||||
emitDispState.pushArg(fmtLetter,argp,"");
|
||||
|
||||
// Next parameter
|
||||
*elistp = (*elistp)->nextp();
|
||||
@ -1328,6 +1335,7 @@ void EmitCStmts::displayNode(AstNode* nodep, AstScopeName* scopenamep,
|
||||
break;
|
||||
// Special codes
|
||||
case '~': displayArg(nodep,&elistp,isScan, vfmt,'d'); break; // Signed decimal
|
||||
case '@': displayArg(nodep,&elistp,isScan, vfmt,'@'); break; // Packed string
|
||||
// Spec: h d o b c l
|
||||
case 'b': displayArg(nodep,&elistp,isScan, vfmt,'b'); break;
|
||||
case 'c': displayArg(nodep,&elistp,isScan, vfmt,'c'); break;
|
||||
@ -1345,7 +1353,7 @@ void EmitCStmts::displayNode(AstNode* nodep, AstScopeName* scopenamep,
|
||||
string suffix = scopenamep->scopePrettyName();
|
||||
if (suffix=="") emitDispState.pushFormat("%S");
|
||||
else emitDispState.pushFormat("%N"); // Add a . when needed
|
||||
emitDispState.pushArg(NULL, "vlSymsp->name()");
|
||||
emitDispState.pushArg(' ',NULL, "vlSymsp->name()");
|
||||
emitDispState.pushFormat(suffix);
|
||||
break;
|
||||
}
|
||||
|
@ -553,10 +553,6 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||
virtual void visit(AstConst* nodep, AstNUser*) {
|
||||
putfs(nodep,nodep->num().ascii(true,true));
|
||||
}
|
||||
virtual void visit(AstConstString* nodep, AstNUser*) {
|
||||
putfs(nodep,"");
|
||||
putsQuoted(nodep->name());
|
||||
}
|
||||
|
||||
// Just iterate
|
||||
virtual void visit(AstTopScope* nodep, AstNUser*) {
|
||||
|
@ -365,6 +365,8 @@ string V3Number::ascii(bool prefixed, bool cleanVerilog) const {
|
||||
if (width()!=64) out<<"%E-bad-width-double";
|
||||
else out<<toDouble();
|
||||
return out.str();
|
||||
} else if (isString()) {
|
||||
return '"'+toString()+'"';
|
||||
} else {
|
||||
if ((m_value[words()-1] | m_valueX[words()-1]) & ~hiWordMask()) out<<"%E-hidden-bits";
|
||||
}
|
||||
@ -443,6 +445,7 @@ bool V3Number::displayedFmtLegal(char format) {
|
||||
case 's': return true;
|
||||
case 't': return true;
|
||||
case 'x': return true;
|
||||
case '@': return true; // Packed string
|
||||
case '~': return true; // Signed decimal
|
||||
default: return false;
|
||||
}
|
||||
@ -498,6 +501,9 @@ string V3Number::displayed(const string& vformat) const {
|
||||
str += (char)(v);
|
||||
return str;
|
||||
}
|
||||
case '@': { // Packed string
|
||||
return toString();
|
||||
}
|
||||
case 's': {
|
||||
// Spec says always drop leading zeros, this isn't quite right, we space pad.
|
||||
int bit=this->width()-1;
|
||||
@ -606,6 +612,7 @@ vlsint64_t V3Number::toSQuad() const {
|
||||
string V3Number::toString() const {
|
||||
UASSERT(!isFourState(),"toString with 4-state "<<*this);
|
||||
// Spec says always drop leading zeros, this isn't quite right, we space pad.
|
||||
if (isString()) return m_stringVal;
|
||||
int bit=this->width()-1;
|
||||
bool start=true;
|
||||
while ((bit%8)!=7) bit++;
|
||||
@ -1651,3 +1658,38 @@ V3Number& V3Number::opLtD (const V3Number& lhs, const V3Number& rhs) {
|
||||
V3Number& V3Number::opLteD (const V3Number& lhs, const V3Number& rhs) {
|
||||
return setSingleBits(lhs.toDouble() <= rhs.toDouble());
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Ops - String
|
||||
|
||||
V3Number& V3Number::opConcatN (const V3Number& lhs, const V3Number& rhs) {
|
||||
return setString(lhs.toString() + rhs.toString());
|
||||
}
|
||||
V3Number& V3Number::opReplN (const V3Number& lhs, const V3Number& rhs) {
|
||||
return opReplN(lhs, rhs.toUInt());
|
||||
}
|
||||
V3Number& V3Number::opReplN (const V3Number& lhs, uint32_t rhsval) {
|
||||
string out; out.reserve(lhs.toString().length() * rhsval);
|
||||
for (unsigned times=0; times<rhsval; times++) {
|
||||
out += lhs.toString();
|
||||
}
|
||||
return setString(out);
|
||||
}
|
||||
V3Number& V3Number::opEqN (const V3Number& lhs, const V3Number& rhs) {
|
||||
return setSingleBits(lhs.toString() == rhs.toString());
|
||||
}
|
||||
V3Number& V3Number::opNeqN (const V3Number& lhs, const V3Number& rhs) {
|
||||
return setSingleBits(lhs.toString() != rhs.toString());
|
||||
}
|
||||
V3Number& V3Number::opGtN (const V3Number& lhs, const V3Number& rhs) {
|
||||
return setSingleBits(lhs.toString() > rhs.toString());
|
||||
}
|
||||
V3Number& V3Number::opGteN (const V3Number& lhs, const V3Number& rhs) {
|
||||
return setSingleBits(lhs.toString() >= rhs.toString());
|
||||
}
|
||||
V3Number& V3Number::opLtN (const V3Number& lhs, const V3Number& rhs) {
|
||||
return setSingleBits(lhs.toString() < rhs.toString());
|
||||
}
|
||||
V3Number& V3Number::opLteN (const V3Number& lhs, const V3Number& rhs) {
|
||||
return setSingleBits(lhs.toString() <= rhs.toString());
|
||||
}
|
||||
|
@ -35,13 +35,16 @@ class V3Number {
|
||||
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_isString:1; // True if string
|
||||
bool m_fromString:1; // True if from string literal
|
||||
bool m_autoExtend:1; // True if SystemVerilog extend-to-any-width
|
||||
FileLine* m_fileline;
|
||||
vector<uint32_t> m_value; // The Value, with bit 0 being in bit 0 of this vector (unless X/Z)
|
||||
vector<uint32_t> m_valueX; // Each bit is true if it's X or Z, 10=z, 11=x
|
||||
string m_stringVal; // If isString, the value of the string
|
||||
// METHODS
|
||||
V3Number& setSingleBits(char value);
|
||||
V3Number& setString(const string& str) { m_isString=true; m_stringVal=str; return *this; }
|
||||
void opCleanThis();
|
||||
public:
|
||||
FileLine* fileline() const { return m_fileline; }
|
||||
@ -116,19 +119,22 @@ private:
|
||||
V3Number& opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool is_modulus);
|
||||
|
||||
public:
|
||||
class VerilogStringLiteral {}; // for creator type-overload selection
|
||||
// CONSTRUCTORS
|
||||
V3Number(FileLine* fileline) { init(fileline, 1); }
|
||||
V3Number(FileLine* fileline, int width) { init(fileline, width); } // 0=unsized
|
||||
V3Number(FileLine* fileline, int width, uint32_t value) { init(fileline, width); m_value[0]=value; opCleanThis(); }
|
||||
V3Number(FileLine* fileline, const char* source); // Create from a verilog 32'hxxxx number.
|
||||
class VerilogStringLiteral {}; // for creator type-overload selection
|
||||
V3Number(VerilogStringLiteral, FileLine* fileline, const string& vvalue);
|
||||
class String {};
|
||||
V3Number(String, FileLine* fileline, const string& value) { init(fileline, 0); setString(value); }
|
||||
|
||||
private:
|
||||
void init(FileLine* fileline, int swidth) {
|
||||
m_fileline = fileline;
|
||||
m_signed = false;
|
||||
m_double = false;
|
||||
m_isString = false;
|
||||
m_autoExtend = false;
|
||||
m_fromString = false;
|
||||
width(swidth);
|
||||
@ -166,6 +172,8 @@ public:
|
||||
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())
|
||||
void isDouble(bool flag) { m_double=flag; } // Only if have 64 bit value loaded, and want to indicate it's real
|
||||
bool isString() const { return m_isString; }
|
||||
void isString(bool flag) { m_isString=flag; }
|
||||
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;}
|
||||
@ -295,6 +303,17 @@ public:
|
||||
V3Number& opGteD (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opLtD (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opLteD (const V3Number& lhs, const V3Number& rhs);
|
||||
|
||||
// "N" - string operations
|
||||
V3Number& opConcatN (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opReplN (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opReplN (const V3Number& lhs, uint32_t rhs);
|
||||
V3Number& opEqN (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opNeqN (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opGtN (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opGteN (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opLtN (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opLteN (const V3Number& lhs, const V3Number& rhs);
|
||||
};
|
||||
inline ostream& operator<<(ostream& os, V3Number rhs) { return os<<rhs.ascii(); }
|
||||
|
||||
|
@ -148,7 +148,7 @@ private:
|
||||
&& nodep->firstAbovep()->castArraySel()) { // ArraySel's are pointer refs, ignore
|
||||
} else {
|
||||
UINFO(4,"Cre Temp: "<<nodep<<endl);
|
||||
createDeepTemp(nodep);
|
||||
createDeepTemp(nodep, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -180,13 +180,14 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void createDeepTemp(AstNode* nodep) {
|
||||
void createDeepTemp(AstNode* nodep, bool noSubst) {
|
||||
if (debug()>8) nodep->dumpTree(cout,"deepin:");
|
||||
|
||||
AstNRelinker linker;
|
||||
nodep->unlinkFrBack(&linker);
|
||||
|
||||
AstVar* varp = getBlockTemp(nodep);
|
||||
if (noSubst) varp->noSubst(true); // Do not remove varrefs to this in V3Const
|
||||
// Replace node tree with reference to var
|
||||
AstVarRef* newp = new AstVarRef (nodep->fileline(), varp, false);
|
||||
linker.relink(newp);
|
||||
@ -236,7 +237,7 @@ private:
|
||||
if (noopt && !nodep->user1()) {
|
||||
// Need to do this even if not wide, as e.g. a select may be on a wide operator
|
||||
UINFO(4,"Deep temp for LHS/RHS\n");
|
||||
createDeepTemp(nodep->rhsp());
|
||||
createDeepTemp(nodep->rhsp(), false);
|
||||
}
|
||||
}
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
@ -337,7 +338,7 @@ private:
|
||||
&& !nodep->condp()->castVarRef()) {
|
||||
// We're going to need the expression several times in the expanded code,
|
||||
// so might as well make it a common expression
|
||||
createDeepTemp(nodep->condp());
|
||||
createDeepTemp(nodep->condp(), false);
|
||||
}
|
||||
checkNode(nodep);
|
||||
}
|
||||
@ -361,6 +362,17 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstSFormatF* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
// Any strings sent to a display must be var of string data type,
|
||||
// to avoid passing a pointer to a temporary.
|
||||
for (AstNode* expp=nodep->exprsp(); expp; expp = expp->nextp()) {
|
||||
if (expp->dtypep()->basicp()->isString()
|
||||
&& !expp->castVarRef()) {
|
||||
createDeepTemp(expp, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
|
@ -258,6 +258,9 @@ private:
|
||||
return entryp;
|
||||
}
|
||||
}
|
||||
inline bool isSubstVar(AstVar* nodep) {
|
||||
return nodep->isStatementTemp() && !nodep->noSubst();
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
||||
@ -266,7 +269,7 @@ private:
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
bool hit=false;
|
||||
if (AstVarRef* varrefp = nodep->lhsp()->castVarRef()) {
|
||||
if (varrefp->varp()->isStatementTemp()) {
|
||||
if (isSubstVar(varrefp->varp())) {
|
||||
SubstVarEntry* entryp = getEntryp(varrefp);
|
||||
hit = true;
|
||||
if (m_ops > SUBST_MAX_OPS_SUBST) {
|
||||
@ -281,7 +284,7 @@ private:
|
||||
else if (AstWordSel* wordp = nodep->lhsp()->castWordSel()) {
|
||||
if (AstVarRef* varrefp = wordp->lhsp()->castVarRef()) {
|
||||
if (wordp->rhsp()->castConst()
|
||||
&& varrefp->varp()->isStatementTemp()) {
|
||||
&& isSubstVar(varrefp->varp())) {
|
||||
int word = wordp->rhsp()->castConst()->toUInt();
|
||||
SubstVarEntry* entryp = getEntryp(varrefp);
|
||||
hit = true;
|
||||
@ -314,7 +317,7 @@ private:
|
||||
nodep->rhsp()->accept(*this);
|
||||
AstVarRef* varrefp = nodep->lhsp()->castVarRef();
|
||||
AstConst* constp = nodep->rhsp()->castConst();
|
||||
if (varrefp && varrefp->varp()->isStatementTemp()
|
||||
if (varrefp && isSubstVar(varrefp->varp())
|
||||
&& !varrefp->lvalue()
|
||||
&& constp) {
|
||||
// Nicely formed lvalues handled in NodeAssign
|
||||
@ -344,7 +347,7 @@ private:
|
||||
nodep->varp()->user2(m_assignStep);
|
||||
UINFO(9, " ASSIGNstep u2="<<nodep->varp()->user2()<<" "<<nodep<<endl);
|
||||
}
|
||||
if (nodep->varp()->isStatementTemp()) {
|
||||
if (isSubstVar(nodep->varp())) {
|
||||
SubstVarEntry* entryp = getEntryp (nodep);
|
||||
if (nodep->lvalue()) {
|
||||
UINFO(8," ASSIGNcpx "<<nodep<<endl);
|
||||
|
148
src/V3Width.cpp
148
src/V3Width.cpp
@ -229,6 +229,13 @@ private:
|
||||
virtual void visit(AstLteD* nodep, AstNUser* vup) { visit_cmp_real(nodep,vup); }
|
||||
virtual void visit(AstGtD* nodep, AstNUser* vup) { visit_cmp_real(nodep,vup); }
|
||||
virtual void visit(AstGteD* nodep, AstNUser* vup) { visit_cmp_real(nodep,vup); }
|
||||
// ... String compares
|
||||
virtual void visit(AstEqN* nodep, AstNUser* vup) { visit_cmp_string(nodep,vup); }
|
||||
virtual void visit(AstNeqN* nodep, AstNUser* vup) { visit_cmp_string(nodep,vup); }
|
||||
virtual void visit(AstLtN* nodep, AstNUser* vup) { visit_cmp_string(nodep,vup); }
|
||||
virtual void visit(AstLteN* nodep, AstNUser* vup) { visit_cmp_string(nodep,vup); }
|
||||
virtual void visit(AstGtN* nodep, AstNUser* vup) { visit_cmp_string(nodep,vup); }
|
||||
virtual void visit(AstGteN* nodep, AstNUser* vup) { visit_cmp_string(nodep,vup); }
|
||||
|
||||
// Widths: out width = lhs width = rhs width
|
||||
// Signed: Output signed iff LHS & RHS signed.
|
||||
@ -368,6 +375,29 @@ private:
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (nodep->lhsp()->isString()
|
||||
|| nodep->rhsp()->isString()) {
|
||||
AstNode* newp = new AstConcatN (nodep->fileline(),nodep->lhsp()->unlinkFrBack(),
|
||||
nodep->rhsp()->unlinkFrBack());
|
||||
nodep->replaceWith(newp);
|
||||
pushDeletep(nodep); nodep=NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (vup->c()->final()) {
|
||||
if (!nodep->dtypep()->widthSized()) {
|
||||
// See also error in V3Number
|
||||
nodep->v3warn(WIDTHCONCAT,"Unsized numbers/parameters not allowed in concatenations.");
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstConcatN* nodep, AstNUser* vup) {
|
||||
// String concatenate.
|
||||
// Already did AstConcat simplifications
|
||||
if (vup->c()->prelim()) {
|
||||
iterateCheckString(nodep,"LHS",nodep->lhsp(),BOTH);
|
||||
iterateCheckString(nodep,"RHS",nodep->rhsp(),BOTH);
|
||||
nodep->dtypeSetString();
|
||||
}
|
||||
if (vup->c()->final()) {
|
||||
if (!nodep->dtypep()->widthSized()) {
|
||||
@ -390,9 +420,38 @@ 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->dtypeSetLogicSized((nodep->lhsp()->width() * times),
|
||||
(nodep->lhsp()->widthMin() * times),
|
||||
AstNumeric::UNSIGNED);
|
||||
if (nodep->lhsp()->isString()) {
|
||||
AstNode* newp = new AstReplicateN(nodep->fileline(),nodep->lhsp()->unlinkFrBack(),
|
||||
nodep->rhsp()->unlinkFrBack());
|
||||
nodep->replaceWith(newp);
|
||||
pushDeletep(nodep); nodep=NULL;
|
||||
return;
|
||||
} else {
|
||||
nodep->dtypeSetLogicSized((nodep->lhsp()->width() * times),
|
||||
(nodep->lhsp()->widthMin() * times),
|
||||
AstNumeric::UNSIGNED);
|
||||
}
|
||||
}
|
||||
if (vup->c()->final()) {
|
||||
if (!nodep->dtypep()->widthSized()) {
|
||||
// See also error in V3Number
|
||||
nodep->v3warn(WIDTHCONCAT,"Unsized numbers/parameters not allowed in replications.");
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstReplicateN* nodep, AstNUser* vup) {
|
||||
// Replicate with string
|
||||
if (vup->c()->prelim()) {
|
||||
iterateCheckString(nodep,"LHS",nodep->lhsp(),BOTH);
|
||||
iterateCheckSizedSelf(nodep,"RHS",nodep->rhsp(),SELF,BOTH);
|
||||
V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change
|
||||
AstConst* constp = nodep->rhsp()->castConst();
|
||||
if (!constp) { nodep->v3error("Replication value isn't a constant."); return; }
|
||||
uint32_t times = constp->toUInt();
|
||||
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->dtypeSetString();
|
||||
}
|
||||
if (vup->c()->final()) {
|
||||
if (!nodep->dtypep()->widthSized()) {
|
||||
@ -658,9 +717,6 @@ private:
|
||||
// We don't size the constant until we commit the widths, as need parameters
|
||||
// to remain unsized, and numbers to remain unsized to avoid backp() warnings
|
||||
}
|
||||
virtual void visit(AstConstString* nodep, AstNUser* vup) {
|
||||
nodep->rewidth();
|
||||
}
|
||||
virtual void visit(AstRand* nodep, AstNUser* vup) {
|
||||
if (vup->c()->prelim()) {
|
||||
nodep->dtypeSetSigned32(); // Says the spec
|
||||
@ -1701,6 +1757,13 @@ private:
|
||||
if (argp) argp=argp->nextp();
|
||||
break;
|
||||
}
|
||||
case 's': { // Convert string to pack string
|
||||
if (argp && argp->dtypep()->basicp()->isString()) { // Convert it
|
||||
ch = '@';
|
||||
}
|
||||
if (argp) argp=argp->nextp();
|
||||
break;
|
||||
}
|
||||
default: { // Most operators, just move to next argument
|
||||
if (argp) argp=argp->nextp();
|
||||
break;
|
||||
@ -2201,6 +2264,12 @@ private:
|
||||
iterateCheckReal(nodep,"LHS",nodep->lhsp(),FINAL);
|
||||
iterateCheckReal(nodep,"RHS",nodep->rhsp(),FINAL);
|
||||
}
|
||||
} else if (nodep->lhsp()->isString() || nodep->rhsp()->isString()) {
|
||||
if (AstNodeBiop* newp=replaceWithNVersion(nodep)) { nodep=NULL;
|
||||
nodep = newp; // Process new node instead
|
||||
iterateCheckString(nodep,"LHS",nodep->lhsp(),FINAL);
|
||||
iterateCheckString(nodep,"RHS",nodep->rhsp(),FINAL);
|
||||
}
|
||||
} else {
|
||||
bool signedFl = nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned();
|
||||
if (AstNodeBiop* newp=replaceWithUOrSVersion(nodep, signedFl)) { nodep=NULL;
|
||||
@ -2230,6 +2299,19 @@ private:
|
||||
nodep->dtypeSetLogicBool();
|
||||
}
|
||||
}
|
||||
void visit_cmp_string(AstNodeBiop* nodep, AstNUser* vup) {
|
||||
// CALLER: EqN, LtN
|
||||
// Widths: 1 bit out, lhs width == rhs width
|
||||
// String compare (not output)
|
||||
// Real if and only if real_allow set
|
||||
if (!nodep->rhsp()) nodep->v3fatalSrc("For binary ops only!");
|
||||
if (vup->c()->prelim()) {
|
||||
// See similar handling in visit_cmp_eq_gt where created
|
||||
iterateCheckString(nodep,"LHS",nodep->lhsp(),BOTH);
|
||||
iterateCheckString(nodep,"RHS",nodep->rhsp(),BOTH);
|
||||
nodep->dtypeSetLogicBool();
|
||||
}
|
||||
}
|
||||
|
||||
void visit_negate_not(AstNodeUniop* nodep, AstNUser* vup, bool real_ok) {
|
||||
// CALLER: (real_ok=false) Not
|
||||
@ -2596,6 +2678,15 @@ private:
|
||||
underp = iterateCheck(nodep,side,underp,SELF,FINAL,expDTypep,EXTEND_EXP);
|
||||
}
|
||||
}
|
||||
void iterateCheckString (AstNode* nodep, const char* side, AstNode* underp, Stage stage) {
|
||||
if (stage & PRELIM) {
|
||||
underp = underp->acceptSubtreeReturnEdits(*this,WidthVP(SELF,PRELIM).p());
|
||||
}
|
||||
if (stage & FINAL) {
|
||||
AstNodeDType* expDTypep = nodep->findStringDType();
|
||||
underp = iterateCheck(nodep,side,underp,SELF,FINAL,expDTypep,EXTEND_EXP);
|
||||
}
|
||||
}
|
||||
void iterateCheckSizedSelf (AstNode* nodep, const char* side, AstNode* underp,
|
||||
Determ determ, Stage stage) {
|
||||
// Coerce child to any sized-number data type; child is self-determined i.e. isolated from expected type.
|
||||
@ -2679,6 +2770,9 @@ private:
|
||||
} else if (!expDTypep->isDouble() && underp->isDouble()) {
|
||||
underp = spliceCvtS(underp, true); // Round RHS
|
||||
underp = underp->acceptSubtreeReturnEdits(*this,WidthVP(SELF,FINAL).p());
|
||||
} else if (expDTypep->isString() && !underp->dtypep()->isString()) {
|
||||
underp = spliceCvtString(underp);
|
||||
underp = underp->acceptSubtreeReturnEdits(*this,WidthVP(SELF,FINAL).p());
|
||||
} else {
|
||||
AstBasicDType* expBasicp = expDTypep->basicp();
|
||||
AstBasicDType* underBasicp = underp->dtypep()->basicp();
|
||||
@ -2822,6 +2916,20 @@ private:
|
||||
return nodep;
|
||||
}
|
||||
}
|
||||
AstNode* spliceCvtString(AstNode* nodep) {
|
||||
// IEEE-2012 11.8.1: Signed: Type coercion creates signed
|
||||
// 11.8.2: Argument to convert is self-determined
|
||||
if (nodep && !nodep->dtypep()->basicp()->isString()) {
|
||||
UINFO(6," spliceCvtString: "<<nodep<<endl);
|
||||
AstNRelinker linker;
|
||||
nodep->unlinkFrBack(&linker);
|
||||
AstNode* newp = new AstCvtPackString(nodep->fileline(), nodep);
|
||||
linker.relink(newp);
|
||||
return newp;
|
||||
} else {
|
||||
return nodep;
|
||||
}
|
||||
}
|
||||
AstNodeBiop* replaceWithUOrSVersion(AstNodeBiop* nodep, bool signedFlavorNeeded) {
|
||||
// Given a signed/unsigned node type, create the opposite type
|
||||
// Return new node or NULL if nothing
|
||||
@ -2904,6 +3012,34 @@ private:
|
||||
pushDeletep(nodep); nodep=NULL;
|
||||
return newp;
|
||||
}
|
||||
AstNodeBiop* replaceWithNVersion(AstNodeBiop* nodep) {
|
||||
// Given a signed/unsigned node type, replace with string version
|
||||
// Return new node or NULL if nothing
|
||||
if (nodep->stringFlavor()) {
|
||||
return NULL;
|
||||
}
|
||||
FileLine* fl = nodep->fileline();
|
||||
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
|
||||
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
|
||||
AstNodeBiop* newp = NULL;
|
||||
// No width change on output;... // All below have bool or double outputs
|
||||
switch (nodep->type()) {
|
||||
case AstType::atEQ: case AstType::atEQCASE: newp = new AstEqN (fl,lhsp,rhsp); break;
|
||||
case AstType::atNEQ: case AstType::atNEQCASE: newp = new AstNeqN (fl,lhsp,rhsp); break;
|
||||
case AstType::atGT: case AstType::atGTS: newp = new AstGtN (fl,lhsp,rhsp); break;
|
||||
case AstType::atGTE: case AstType::atGTES: newp = new AstGteN (fl,lhsp,rhsp); break;
|
||||
case AstType::atLT: case AstType::atLTS: newp = new AstLtN (fl,lhsp,rhsp); break;
|
||||
case AstType::atLTE: case AstType::atLTES: newp = new AstLteN (fl,lhsp,rhsp); break;
|
||||
default:
|
||||
nodep->v3fatalSrc("Node needs conversion to string, but bad case: "<<nodep<<endl);
|
||||
break;
|
||||
}
|
||||
UINFO(6," ReplaceWithNVersion: "<<nodep<<" w/ "<<newp<<endl);
|
||||
nodep->replaceWith(newp);
|
||||
// No width change; the default created type (bool or string) is correct
|
||||
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
|
||||
|
@ -73,6 +73,7 @@ module sub (/*AUTOARG*/
|
||||
if (vec[2][1] !== 32'h0201) $stop;
|
||||
if (vec[2][2] !== 32'h0202) $stop;
|
||||
if (r != 1.234) $stop;
|
||||
$display("%s",s);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
18
test_regress/t/t_string.pl
Executable file
18
test_regress/t/t_string.pl
Executable file
@ -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;
|
91
test_regress/t/t_string.v
Normal file
91
test_regress/t/t_string.v
Normal file
@ -0,0 +1,91 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2014 by Wilson Snyder.
|
||||
|
||||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||
`define checks(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=\"%s\" exp=\"%s\"\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
|
||||
integer cyc=0;
|
||||
|
||||
reg [4*8:1] vstr;
|
||||
const string s = "a"; // Check static assignment
|
||||
string s2;
|
||||
string s3;
|
||||
reg eq;
|
||||
|
||||
// Operators == != < <= > >= {a,b} {a{b}} a[b]
|
||||
// a.len, a.putc, a.getc, a.toupper, a.tolower, a.compare, a.icompare, a.substr
|
||||
// a.atoi, a.atohex, a.atooct, a.atobin, a.atoreal,
|
||||
// a.itoa, a.hextoa, a.octoa, a.bintoa, a.realtoa
|
||||
|
||||
initial begin
|
||||
$sformat(vstr, "s=%s", s);
|
||||
`checks(vstr, "s=a");
|
||||
`checks(s, "a");
|
||||
`checks({s,s,s}, "aaa");
|
||||
`checks({4{s}}, "aaaa");
|
||||
// Constification
|
||||
`checkh(s == "a", 1'b1);
|
||||
`checkh(s == "b", 1'b0);
|
||||
`checkh(s != "a", 1'b0);
|
||||
`checkh(s != "b", 1'b1);
|
||||
`checkh(s > " ", 1'b1);
|
||||
`checkh(s > "a", 1'b0);
|
||||
`checkh(s >= "a", 1'b1);
|
||||
`checkh(s >= "b", 1'b0);
|
||||
`checkh(s < "a", 1'b0);
|
||||
`checkh(s < "b", 1'b1);
|
||||
`checkh(s <= " ", 1'b0);
|
||||
`checkh(s <= "a", 1'b1);
|
||||
end
|
||||
|
||||
// Test loop
|
||||
always @ (posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc==0) begin
|
||||
// Setup
|
||||
s2 = "c0";
|
||||
end
|
||||
else if (cyc==1) begin
|
||||
$sformat(vstr, "s2%s", s2);
|
||||
`checks(vstr, "s2c0");
|
||||
end
|
||||
else if (cyc==2) begin
|
||||
s3 = s2;
|
||||
$sformat(vstr, "s2%s", s3);
|
||||
`checks(vstr, "s2c0");
|
||||
end
|
||||
else if (cyc==3) begin
|
||||
s2 = "a";
|
||||
s3 = "b";
|
||||
end
|
||||
else if (cyc==4) begin
|
||||
`checks({s2,s3}, "ab");
|
||||
`checks({3{s3}}, "bbb");
|
||||
`checkh(s == "a", 1'b1);
|
||||
`checkh(s == "b", 1'b0);
|
||||
`checkh(s != "a", 1'b0);
|
||||
`checkh(s != "b", 1'b1);
|
||||
`checkh(s > " ", 1'b1);
|
||||
`checkh(s > "a", 1'b0);
|
||||
`checkh(s >= "a", 1'b1);
|
||||
`checkh(s >= "b", 1'b0);
|
||||
`checkh(s < "a", 1'b0);
|
||||
`checkh(s < "b", 1'b1);
|
||||
`checkh(s <= " ", 1'b0);
|
||||
`checkh(s <= "a", 1'b1);
|
||||
end
|
||||
else if (cyc==99) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
Loading…
Reference in New Issue
Block a user