diff --git a/Changes b/Changes index fdf8905a7..643c23767 100644 --- a/Changes +++ b/Changes @@ -6,6 +6,8 @@ The contributors that suggested a given feature are shown in []. Thanks! *** Add +verilator+error+limit to see more assertion errors. [Peter Monsson] +*** Support string.toupper and string.tolower. + *** Support $rewind and $ungetc. **** Add -Wpedantic for compliance testing. diff --git a/include/verilated.cpp b/include/verilated.cpp index efb54dcae..dc1528458 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -1754,7 +1754,22 @@ const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE { } //=========================================================================== -// Heavy functions +// Heavy string functions + +std::string VL_TOLOWER_NN(const std::string& ld) VL_MT_SAFE { + std::string out = ld; + for (std::string::iterator it = out.begin(); it != out.end(); ++it) { + *it = tolower(*it); + } + return out; +} +std::string VL_TOUPPER_NN(const std::string& ld) VL_MT_SAFE { + std::string out = ld; + for (std::string::iterator it = out.begin(); it != out.end(); ++it) { + *it = toupper(*it); + } + return out; +} std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) VL_MT_SAFE { // See also _VL_VINT_TO_STRING diff --git a/include/verilated_heavy.h b/include/verilated_heavy.h index b071e5af4..7e0e9dd7a 100644 --- a/include/verilated_heavy.h +++ b/include/verilated_heavy.h @@ -61,6 +61,8 @@ inline std::string VL_REPLICATEN_NNI(int obits,int lbits,int rbits, } inline IData VL_LEN_IN(const std::string& ld) { return ld.length(); } +extern std::string VL_TOLOWER_NN(const std::string& ld); +extern std::string VL_TOUPPER_NN(const std::string& ld); extern IData VL_FOPEN_NI(const std::string& filename, IData mode) VL_MT_SAFE; extern void VL_READMEM_N(bool hex, int width, int depth, int array_lsb, diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 9eb73f70a..4dc7150ca 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -4532,6 +4532,32 @@ public: virtual string emitVerilog() { return "%f$atanh(%l)"; } virtual string emitC() { return "atanh(%li)"; } }; +class AstToLowerN : public AstNodeUniop { + // string.tolower() +public: + AstToLowerN(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + dtypeSetString(); } + ASTNODE_NODE_FUNCS(ToLowerN) + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opToLowerN(lhs); } + virtual string emitVerilog() { return "%l.tolower()"; } + virtual string emitC() { return "VL_TOLOWER_NN(%li)"; } + virtual bool cleanOut() const { return true; } + virtual bool cleanLhs() const { return true; } + virtual bool sizeMattersLhs() const { return false; } +}; +class AstToUpperN : public AstNodeUniop { + // string.toupper() +public: + AstToUpperN(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + dtypeSetString(); } + ASTNODE_NODE_FUNCS(ToUpperN) + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opToUpperN(lhs); } + virtual string emitVerilog() { return "%l.toupper()"; } + virtual string emitC() { return "VL_TOUPPER_NN(%li)"; } + virtual bool cleanOut() const { return true; } + virtual bool cleanLhs() const { return true; } + virtual bool sizeMattersLhs() const { return false; } +}; //====================================================================== // Binary ops diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 8acebbe45..67d11248d 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -2074,6 +2074,25 @@ V3Number& V3Number::opReplN(const V3Number& lhs, uint32_t rhsval) { } return setString(out); } +V3Number& V3Number::opToLowerN(const V3Number& lhs) { + NUM_ASSERT_OP_ARGS1(lhs); + NUM_ASSERT_STRING_ARGS1(lhs); + std::string out = lhs.toString(); + for (std::string::iterator it = out.begin(); it != out.end(); ++it) { + *it = tolower(*it); + } + return setString(out); +} +V3Number& V3Number::opToUpperN(const V3Number& lhs) { + NUM_ASSERT_OP_ARGS1(lhs); + NUM_ASSERT_STRING_ARGS1(lhs); + std::string out = lhs.toString(); + for (std::string::iterator it = out.begin(); it != out.end(); ++it) { + *it = toupper(*it); + } + return setString(out); +} + V3Number& V3Number::opEqN(const V3Number& lhs, const V3Number& rhs) { NUM_ASSERT_OP_ARGS2(lhs, rhs); NUM_ASSERT_STRING_ARGS2(lhs, rhs); diff --git a/src/V3Number.h b/src/V3Number.h index 65aeef905..6a8af81d6 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -288,6 +288,8 @@ public: V3Number& opSel (const V3Number& lhs, uint32_t msbval, uint32_t lsbval); V3Number& opSelInto (const V3Number& lhs, const V3Number& lsb, int width); V3Number& opSelInto (const V3Number& lhs, int lsbval, int width); + V3Number& opToLowerN(const V3Number& lhs); + V3Number& opToUpperN(const V3Number& lhs); V3Number& opCond (const V3Number& lhs, const V3Number& if1s, const V3Number& if0s); V3Number& opCaseEq (const V3Number& lhs, const V3Number& rhs); V3Number& opCaseNeq (const V3Number& lhs, const V3Number& rhs); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 532639c81..85c6d8a7f 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -442,6 +442,18 @@ private: } } } + virtual void visit(AstToLowerN* nodep) { + if (m_vup->prelim()) { + iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); + nodep->dtypeSetString(); + } + } + virtual void visit(AstToUpperN* nodep) { + if (m_vup->prelim()) { + iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); + nodep->dtypeSetString(); + } + } virtual void visit(AstReplicate* nodep) { // IEEE-2012 Table 11-21: // LHS, RHS is self-determined @@ -1804,8 +1816,26 @@ private: } else if (nodep->name() == "realtoa") { methodOkArguments(nodep, 1, 1); replaceWithSFormat(nodep, "%g"); VL_DANGLING(nodep); - } else { + } else if (nodep->name() == "tolower") { + methodOkArguments(nodep, 0, 0); + AstNode* newp = new AstToLowerN(nodep->fileline(), nodep->fromp()->unlinkFrBack()); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + } else if (nodep->name() == "toupper") { + 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() == "atobin" + || nodep->name() == "atohex" + || nodep->name() == "atoi" + || nodep->name() == "atooct" + || nodep->name() == "atoreal" + || nodep->name() == "compare" + || nodep->name() == "icompare" + || nodep->name() == "getc" + || nodep->name() == "putc") { nodep->v3error("Unsupported: built-in string method "<prettyNameQ()); + } else { + nodep->v3error("Unknown built-in string method "<prettyNameQ()); } } diff --git a/test_regress/t/t_string_type_methods.v b/test_regress/t/t_string_type_methods.v index fb71cb06e..85eadb79f 100644 --- a/test_regress/t/t_string_type_methods.v +++ b/test_regress/t/t_string_type_methods.v @@ -20,11 +20,11 @@ module t (/*AUTOARG*/ // Check constification initial begin s="1234"; `checkh(s.len(),4); + s="ab7CD"; `checks(s.toupper(), "AB7CD"); + s="ab7CD"; `checks(s.tolower(), "ab7cd"); `ifndef VERILATOR s="1234"; s.putc(2, "z"); `checks(s, "12z4"); s="1234"; `checkh(s.getc(2), "3"); - s="abCD"; `checks(s.toupper(), "ABCD"); - s="abCD"; `checks(s.tolower(), "abcd"); s="b"; if (s.compare("a") <= 0) $stop; s="b"; if (s.compare("b") != 0) $stop; s="b"; if (s.compare("c") >= 0) $stop; @@ -42,6 +42,9 @@ module t (/*AUTOARG*/ s.octtoa(123); `checks(s, "173"); s.bintoa(123); `checks(s, "1111011"); s.realtoa(1.23); `checks(s, "1.23"); + s = "bAr"; + s = s.toupper; `checks(s, "BAR"); + s = s.tolower; `checks(s, "bar"); end // Check runtime @@ -54,40 +57,48 @@ module t (/*AUTOARG*/ else if (cyc==1) begin `checkh(s.len(),4); end -`ifndef VERILATOR else if (cyc==2) begin +`ifndef VERILATOR s.putc(2, "z"); +`endif end else if (cyc==3) begin +`ifndef VERILATOR `checks(s, "12z4"); `checkh(s.getc(2), "z"); - s="abCD"; +`endif + s="ab3CD"; end else if (cyc==4) begin - `checks(s.toupper(), "ABCD"); - `checks(s.tolower(), "abcd"); + `checks(s.toupper(), "AB3CD"); + `checks(s.tolower(), "ab3cd"); 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.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); - end `endif + end else if (cyc==9) begin s.itoa(123); end