Add 'string' printing and comparisons, bug746, bug747, etc.

This commit is contained in:
Wilson Snyder 2014-11-28 15:01:50 -05:00
parent 8b457b9b66
commit 43be4cf2b5
18 changed files with 541 additions and 68 deletions

View File

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

View File

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

View File

@ -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': {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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*) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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