Support string compare, icompare, ato* methods, bug1606.

Signed-off-by: Wilson Snyder <wsnyder@wsnyder.org>
This commit is contained in:
Yutetsu TAKATSUKASA 2019-12-09 19:17:52 -05:00 committed by Wilson Snyder
parent ca1b083d5c
commit c2037ddbc5
11 changed files with 215 additions and 11 deletions

View File

@ -6,6 +6,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
*** Add BOUNDED warning and promote bounded queues to unbounded.
*** Support string compare, icompare, ato* methods, bug1606. [Yutetsu TAKATSUKASA]
* Verilator 4.024 2019-12-08

View File

@ -30,4 +30,5 @@ Sebastien Van Cauwenberghe
Stefan Wallentowitz
Todd Strader
Wilson Snyder
Yutetsu TAKATSUKASA
Yves Mathieu

View File

@ -30,7 +30,9 @@
#include "verilated_config.h"
#include <algorithm>
#include <cctype>
#include <cerrno>
#include <sys/stat.h> // mkdir
#if defined(_WIN32) || defined(__MINGW32__)
@ -1824,6 +1826,17 @@ std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) VL_MT_SAFE {
return std::string(destout, len);
}
IData VL_ATOI_N(const std::string& str, int base) VL_PURE {
std::string str_mod = str; // create a new instance to modify later.
// IEEE 1800-2017 6.16.9 says '_' may exist.
str_mod.erase(std::remove(str_mod.begin(), str_mod.end(), '_'), str_mod.end());
errno = 0;
long v = std::strtol(str_mod.c_str(), NULL, base);
if (errno != 0) v = 0;
return static_cast<IData>(v);
}
//===========================================================================
// Verilated:: Methods

View File

@ -350,4 +350,21 @@ inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, double& rdr)
}
extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE;
//======================================================================
// Strings
inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool ignoreCase) VL_PURE {
// SystemVerilog Language Standard does not allow a string variable to contain '\0'.
// So C functions such as strcmp() can correctly compare strings.
int result;
if (ignoreCase) {
result = VL_STRCASECMP(lhs.c_str(), rhs.c_str());
} else {
result = std::strcmp(lhs.c_str(), rhs.c_str());
}
return result;
}
extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE;
#endif // Guard

View File

@ -423,6 +423,15 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
# endif
#endif
//=========================================================================
// String related OS-specific functions
#ifdef _MSC_VER
# define VL_STRCASECMP _stricmp
#else
# define VL_STRCASECMP strcasecmp
#endif
//=========================================================================
#endif // Guard

View File

@ -4818,6 +4818,47 @@ public:
virtual bool sizeMattersLhs() const { return false; }
};
class AstAtoN : public AstNodeUniop {
// string.atoi(), atobin(), atohex(), atooct(), atoireal()
public:
enum FmtType {ATOI = 10, ATOHEX = 16, ATOOCT = 8, ATOBIN = 2, ATOREAL = -1};
private:
FmtType m_fmt; // Operation type
public:
AstAtoN(FileLine* fl, AstNode* lhsp, FmtType fmt)
: AstNodeUniop(fl, lhsp)
, m_fmt(fmt) {
fmt == ATOREAL ? dtypeSetDouble() : dtypeSetSigned32();
}
ASTNODE_NODE_FUNCS(AtoN)
virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opAtoN(lhs, m_fmt); }
virtual string name() const {
switch (m_fmt) {
case ATOI: return "atoi";
case ATOHEX: return "atohex";
case ATOOCT: return "atooct";
case ATOBIN: return "atobin";
case ATOREAL: return "atoreal";
default: V3ERROR_NA;
}
}
virtual string emitVerilog() { return "%l." + name() + "()"; }
virtual string emitC() {
switch (m_fmt) {
case ATOI: return "VL_ATOI_N(%li, 10)";
case ATOHEX: return "VL_ATOI_N(%li, 16)";
case ATOOCT: return "VL_ATOI_N(%li, 8)";
case ATOBIN: return "VL_ATOI_N(%li, 2)";
case ATOREAL: return "std::atof(%li.c_str())";
default: V3ERROR_NA;
}
}
virtual bool cleanOut() const { return true; }
virtual bool cleanLhs() const { return true; }
virtual bool sizeMattersLhs() const { return false; }
FmtType format() const { return m_fmt; }
};
//======================================================================
// Binary ops
@ -5950,6 +5991,38 @@ public:
virtual string emitC() { return "hypot(%li,%ri)"; }
};
class AstCompareNN : public AstNodeBiop {
// Verilog str.compare() and str.icompare()
private:
bool m_ignoreCase; // True for str.icompare()
public:
AstCompareNN(FileLine* fl, AstNode* lhsp, AstNode* rhsp, bool ignoreCase)
: AstNodeBiop(fl, lhsp, rhsp)
, m_ignoreCase(ignoreCase) {
dtypeSetUInt32();
}
ASTNODE_NODE_FUNCS(CompareNN)
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) {
return new AstCompareNN(this->fileline(), lhsp, rhsp, m_ignoreCase);
}
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) {
out.opCompareNN(lhs, rhs, m_ignoreCase);
}
virtual string name() const { return m_ignoreCase ? "icompare" : "compare"; }
virtual string emitVerilog() {
return m_ignoreCase ? "%k(%l.icompare(%r))" : "%k(%l.compare(%r))";
}
virtual string emitC() {
return m_ignoreCase ? "VL_CMP_NN(%li,%ri,true)" : "VL_CMP_NN(%li,%ri,false)";
}
virtual string emitSimpleOperator() { return ""; }
virtual bool cleanOut() const { return true; }
virtual bool cleanLhs() const { return true; }
virtual bool cleanRhs() const { return true; }
virtual bool sizeMattersLhs() const { return false; }
virtual bool sizeMattersRhs() const { return false; }
};
class AstPast : public AstNodeMath {
// Verilog $past
// Parents: math

View File

@ -58,6 +58,14 @@ class EmitCInlines : EmitCBaseVisitor {
v3Global.needHeavy(true);
iterateChildren(nodep);
}
virtual void visit(AstAtoN* nodep) {
v3Global.needHeavy(true);
iterateChildren(nodep);
}
virtual void visit(AstCompareNN* nodep) {
v3Global.needHeavy(true);
iterateChildren(nodep);
}
// Default
virtual void visit(AstNode* nodep) {

View File

@ -26,6 +26,7 @@
#include "V3Ast.h"
#include <algorithm>
#include <cerrno>
#include <cmath>
#include <cstdarg>
#include <iomanip>
@ -1243,6 +1244,40 @@ V3Number& V3Number::opLogEq(const V3Number& lhs, const V3Number& rhs) {
return opLogAnd(ifa, ifb);
}
V3Number& V3Number::opAtoN(const V3Number& lhs, int base) {
NUM_ASSERT_OP_ARGS1(lhs);
NUM_ASSERT_STRING_ARGS1(lhs);
UASSERT(base == AstAtoN::ATOREAL || base == 2 || base == 8 || base == 10 || base == 16,
"base must be one of AstAtoN::ATOREAL, 2, 8, 10, or 16.");
std::string str = lhs.toString(); // new instance to edit later
if (base == AstAtoN::ATOREAL) return setDouble(std::atof(str.c_str()));
// IEEE 1800-2017 6.16.9 says '_' may exist.
str.erase(std::remove(str.begin(), str.end(), '_'), str.end());
errno = 0;
long v = std::strtol(str.c_str(), NULL, base);
if (errno != 0) v = 0;
return setLongS(static_cast<vlsint32_t>(v));
}
V3Number& V3Number::opCompareNN(const V3Number& lhs, const V3Number& rhs, bool ignoreCase) {
NUM_ASSERT_OP_ARGS2(lhs, rhs);
NUM_ASSERT_STRING_ARGS2(lhs, rhs);
// SystemVerilog Language Standard does not allow a string variable to contain '\0'.
// So C functions such as strcmp() can correctly compare strings.
int result;
string lstring = lhs.toString();
string rstring = rhs.toString();
if (ignoreCase) {
result = VL_STRCASECMP(lstring.c_str(), rstring.c_str());
} else {
result = std::strcmp(lstring.c_str(), rstring.c_str());
}
return setLongS(result);
}
V3Number& V3Number::opEq(const V3Number& lhs, const V3Number& rhs) {
// i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend.
NUM_ASSERT_OP_ARGS2(lhs, rhs);

View File

@ -360,6 +360,8 @@ public:
V3Number& opLteD (const V3Number& lhs, const V3Number& rhs);
// "N" - string operations
V3Number& opAtoN (const V3Number& lhs, int base);
V3Number& opCompareNN(const V3Number& lhs,const V3Number& rhs, bool ignoreCase);
V3Number& opConcatN (const V3Number& lhs, const V3Number& rhs);
V3Number& opReplN (const V3Number& lhs, const V3Number& rhs);
V3Number& opReplN (const V3Number& lhs, uint32_t rhsval);

View File

@ -333,6 +333,30 @@ private:
// Output integer, input string
virtual void visit(AstLenN* nodep) { visit_Os32_string(nodep); }
virtual void visit(AstCompareNN* nodep) {
// CALLER: str.compare(), str.icompare()
// Widths: 32 bit out
UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
if (m_vup->prelim()) {
// See similar handling in visit_cmp_eq_gt where created
iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH);
nodep->dtypeSetSigned32();
}
}
virtual void visit(AstAtoN* nodep) {
// CALLER: str.atobin(), atoi(), atohex(), atooct(), atoreal()
// Width: 64bit floating point for atoreal(), 32bit out for the others
if (m_vup->prelim()) {
// See similar handling in visit_cmp_eq_gt where created
iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
if (nodep->format() == AstAtoN::ATOREAL) {
nodep->dtypeSetDouble();
} else {
nodep->dtypeSetSigned32();
}
}
}
// Widths: Constant, terminal
virtual void visit(AstTime* nodep) { nodep->dtypeSetUInt64(); }
@ -2080,14 +2104,30 @@ private:
methodOkArguments(nodep, 0, 0);
AstNode* newp = new AstToUpperN(nodep->fileline(), nodep->fromp()->unlinkFrBack());
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
} else if (nodep->name() == "compare" || nodep->name() == "icompare") {
const bool ignoreCase = nodep->name()[0] == 'i';
methodOkArguments(nodep, 1, 1);
AstArg* argp = VN_CAST(nodep->pinsp(), Arg);
AstNode* lhs = nodep->fromp()->unlinkFrBack();
AstNode* rhs = argp->exprp()->unlinkFrBack();
AstNode* newp = new AstCompareNN(nodep->fileline(), lhs, rhs, ignoreCase);
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
} else if (nodep->name() == "atobin"
|| nodep->name() == "atohex"
|| nodep->name() == "atoi"
|| nodep->name() == "atooct"
|| nodep->name() == "atoreal"
|| nodep->name() == "compare"
|| nodep->name() == "icompare"
|| nodep->name() == "getc"
|| nodep->name() == "atoreal") {
AstAtoN::FmtType fmt;
if (nodep->name() == "atobin") fmt = AstAtoN::ATOBIN;
else if (nodep->name() == "atohex") fmt = AstAtoN::ATOHEX;
else if (nodep->name() == "atoi") fmt = AstAtoN::ATOI;
else if (nodep->name() == "atooct") fmt = AstAtoN::ATOOCT;
else if (nodep->name() == "atoreal") fmt = AstAtoN::ATOREAL;
else { V3ERROR_NA; fmt = AstAtoN::ATOI; } // dummy assignment to suppress compiler warning
methodOkArguments(nodep, 0, 0);
AstNode* newp = new AstAtoN(nodep->fileline(), nodep->fromp()->unlinkFrBack(), fmt);
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
} else if (nodep->name() == "getc"
|| nodep->name() == "putc") {
nodep->v3error("Unsupported: built-in string method "<<nodep->prettyNameQ());
} else {
@ -3326,6 +3366,7 @@ private:
nodep->dtypeSetLogicBool();
}
}
void visit_Os32_string(AstNodeUniop* nodep) {
// CALLER: LenN
// Widths: 32 bit out

View File

@ -25,9 +25,16 @@ module t (/*AUTOARG*/
`ifndef VERILATOR
s="1234"; s.putc(2, "z"); `checks(s, "12z4");
s="1234"; `checkh(s.getc(2), "3");
`endif
s="b"; if (s.compare("a") <= 0) $stop;
s="b"; if (s.compare("b") != 0) $stop;
s="b"; if (s.compare("c") >= 0) $stop;
s="b"; if (s.compare("A") <= 0) $stop;
s="b"; if (s.compare("B") <= 0) $stop;
s="b"; if (s.compare("C") <= 0) $stop;
s="B"; if (s.compare("a") >= 0) $stop;
s="B"; if (s.compare("b") >= 0) $stop;
s="B"; if (s.compare("c") >= 0) $stop;
s="b"; if (s.icompare("A") < 0) $stop;
s="b"; if (s.icompare("B") != 0) $stop;
s="b"; if (s.icompare("C") >= 0) $stop;
@ -36,7 +43,6 @@ module t (/*AUTOARG*/
s="101"; `checkh(s.atooct(), 'o101);
s="101"; `checkh(s.atobin(), 'b101);
s="1.23"; `checkg(s.atoreal(), 1.23);
`endif
s.itoa(123); `checks(s, "123");
s.hextoa(123); `checks(s, "7b");
s.octtoa(123); `checks(s, "173");
@ -75,29 +81,26 @@ module t (/*AUTOARG*/
s="b";
end
else if (cyc==5) begin
`ifndef VERILATOR
if (s.compare("a") <= 0) $stop;
if (s.compare("b") != 0) $stop;
if (s.compare("c") >= 0) $stop;
if (s.compare("A") <= 0) $stop;
if (s.compare("B") <= 0) $stop;
if (s.compare("C") <= 0) $stop;
if (s.icompare("A") < 0) $stop;
if (s.icompare("B") != 0) $stop;
if (s.icompare("C") >= 0) $stop;
`endif
s="101";
end
else if (cyc==7) begin
`ifndef VERILATOR
`checkh(s.atoi(), 'd101);
`checkh(s.atohex(), 'h101);
`checkh(s.atooct(), 'o101);
`checkh(s.atobin(), 'b101);
`endif
s="1.23";
end
else if (cyc==8) begin
`ifndef VERILATOR
`checkg(s.atoreal(), 1.23);
`endif
end
else if (cyc==9) begin
s.itoa(123);