mirror of
https://github.com/verilator/verilator.git
synced 2025-04-06 04:32:39 +00:00
Support string compare, icompare, ato* methods, bug1606.
Signed-off-by: Wilson Snyder <wsnyder@wsnyder.org>
This commit is contained in:
parent
ca1b083d5c
commit
c2037ddbc5
2
Changes
2
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
|
||||
|
||||
|
@ -30,4 +30,5 @@ Sebastien Van Cauwenberghe
|
||||
Stefan Wallentowitz
|
||||
Todd Strader
|
||||
Wilson Snyder
|
||||
Yutetsu TAKATSUKASA
|
||||
Yves Mathieu
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user