diff --git a/Changes b/Changes index 8620ccfb2..af7d0c7c1 100644 --- a/Changes +++ b/Changes @@ -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 diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 025749b7f..2152dc059 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -30,4 +30,5 @@ Sebastien Van Cauwenberghe Stefan Wallentowitz Todd Strader Wilson Snyder +Yutetsu TAKATSUKASA Yves Mathieu diff --git a/include/verilated.cpp b/include/verilated.cpp index de2e6412f..d0bc7de08 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -30,7 +30,9 @@ #include "verilated_config.h" +#include #include +#include #include // 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(v); +} + //=========================================================================== // Verilated:: Methods diff --git a/include/verilated_heavy.h b/include/verilated_heavy.h index 9b060942b..c041b56eb 100644 --- a/include/verilated_heavy.h +++ b/include/verilated_heavy.h @@ -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 diff --git a/include/verilatedos.h b/include/verilatedos.h index 856b539a0..e60ea5754 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -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 diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 1071470bc..423fc5171 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -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 diff --git a/src/V3EmitCInlines.cpp b/src/V3EmitCInlines.cpp index bbea7ebed..011e5e24d 100644 --- a/src/V3EmitCInlines.cpp +++ b/src/V3EmitCInlines.cpp @@ -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) { diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 32d78bd63..28940635d 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -26,6 +26,7 @@ #include "V3Ast.h" #include +#include #include #include #include @@ -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(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); diff --git a/src/V3Number.h b/src/V3Number.h index f40f8616d..d4ab06435 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -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); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 2ffe7a706..d67dae471 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -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 "<prettyNameQ()); } else { @@ -3326,6 +3366,7 @@ private: nodep->dtypeSetLogicBool(); } } + void visit_Os32_string(AstNodeUniop* nodep) { // CALLER: LenN // Widths: 32 bit out diff --git a/test_regress/t/t_string_type_methods.v b/test_regress/t/t_string_type_methods.v index 85eadb79f..ccc47cb42 100644 --- a/test_regress/t/t_string_type_methods.v +++ b/test_regress/t/t_string_type_methods.v @@ -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);