From 662ebece714e0f1e6fdcf2984fa7b647c851ea46 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 7 Dec 2017 19:57:11 -0500 Subject: [PATCH] Support string len() method. --- Changes | 2 + include/verilated_heavy.h | 2 + src/V3AstNodes.h | 12 +++ src/V3Number.cpp | 5 ++ src/V3Number.h | 1 + src/V3Width.cpp | 25 ++++++ test_regress/t/t_string_type_methods.pl | 18 ++++ test_regress/t/t_string_type_methods.v | 115 ++++++++++++++++++++++++ 8 files changed, 180 insertions(+) create mode 100755 test_regress/t/t_string_type_methods.pl create mode 100644 test_regress/t/t_string_type_methods.v diff --git a/Changes b/Changes index 0906db3d6..bb6993224 100644 --- a/Changes +++ b/Changes @@ -6,6 +6,8 @@ The contributors that suggested a given feature are shown in []. Thanks! **** Support > 64 bit decimal $display. +**** Support string len() method. [Victor Besyakov] + **** Fix modport outputs being treated as inputs, bug1246. [Jeff Bush] **** Fix false ALWCOMBORDER on interface references, bug1247. [Josh Redford] diff --git a/include/verilated_heavy.h b/include/verilated_heavy.h index dc5963203..903fb1b3d 100644 --- a/include/verilated_heavy.h +++ b/include/verilated_heavy.h @@ -60,6 +60,8 @@ inline std::string VL_REPLICATEN_NNI(int obits,int lbits,int rbits, const std::s return VL_REPLICATEN_NNQ(obits,lbits,rbits,lhs,rep); } +inline IData VL_LEN_IN(const std::string& ld) { return ld.length(); } + 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, int fnwords, const std::string& ofilename, void* memp, IData start, IData end) VL_MT_SAFE; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 80474ca8e..e014ccfce 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -3545,6 +3545,18 @@ public: virtual int instrCount() const { return 1+V3Number::log2b(width()); } }; +class AstLenN : public AstNodeUniop { + // Length of a string +public: + AstLenN(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + dtypeSetSigned32(); } + ASTNODE_NODE_FUNCS(LenN) + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opLenN(lhs); } + virtual string emitVerilog() { return "%f(%l)"; } + virtual string emitC() { return "VL_LEN_IN(%li)"; } + virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} + virtual bool sizeMattersLhs() {return false;} +}; class AstLogNot : public AstNodeUniop { public: AstLogNot(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 962537f32..434cd0b19 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -1035,6 +1035,11 @@ V3Number& V3Number::opConcat (const V3Number& lhs, const V3Number& rhs) { return *this; } +V3Number& V3Number::opLenN (const V3Number& lhs) { + setQuad(lhs.toString().length()); + return *this; +} + V3Number& V3Number::opRepl (const V3Number& lhs, const V3Number& rhs) { // rhs is # of times to replicate // Hopefully the using routine has a error check too. // See also error in V3Width diff --git a/src/V3Number.h b/src/V3Number.h index c1cb7d2b4..626fbd8a4 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -242,6 +242,7 @@ public: V3Number& opCLog2 (const V3Number& lhs); V3Number& opClean (const V3Number& lhs, uint32_t bits); V3Number& opConcat (const V3Number& lhs, const V3Number& rhs); + V3Number& opLenN (const V3Number& lhs); V3Number& opRepl (const V3Number& lhs, const V3Number& rhs); V3Number& opRepl (const V3Number& lhs, uint32_t rhs); V3Number& opStreamL (const V3Number& lhs, const V3Number& rhs); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 77f3b46b6..005cc2aee 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -330,6 +330,9 @@ private: // Widths: Output integer unsigned, input real virtual void visit(AstRealToBits* nodep) { visit_Ou64_Lr(nodep); } + // Output integer, input string + virtual void visit(AstLenN* nodep) { visit_Os32_string(nodep); } + // Widths: Constant, terminal virtual void visit(AstTime* nodep) { nodep->dtypeSetUInt64(); } virtual void visit(AstTimeD* nodep) { nodep->dtypeSetDouble(); } @@ -1497,6 +1500,7 @@ private: // Find the fromp dtype - should be a class if (!nodep->fromp() || !nodep->fromp()->dtypep()) nodep->v3fatalSrc("Unsized expression"); AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefToEnump(); + AstBasicDType* basicp = fromDtp ? fromDtp->basicp() : NULL; UINFO(9," from dt "<castEnumDType()) { // Method call on enum without following parenthesis, e.g. "ENUM.next" @@ -1613,6 +1617,17 @@ private: nodep->v3error("Unknown built-in array method '"<fromp()->prettyTypeName()<<"'"); } } + else if (basicp && basicp->isString()) { + // Method call on string + if (nodep->name() == "len") { + // Constant value + AstNode* newp = new AstLenN(nodep->fileline(), nodep->fromp()->unlinkFrBack()); + nodep->replaceWith(newp); + pushDeletep(nodep); VL_DANGLING(nodep); + } else { + nodep->v3error("Unsupported: built-in string method '"<fromp()->prettyTypeName()<<"'"); + } + } else { nodep->v3error("Unsupported: Member call on non-enum object '" <fromp()->prettyTypeName()<<"' which is a '"<fromp()->dtypep()->prettyTypeName()<<"'"); @@ -2646,6 +2661,16 @@ private: nodep->dtypeSetLogicBool(); } } + void visit_Os32_string(AstNodeUniop* nodep) { + // CALLER: LenN + // Widths: 32 bit out + if (!nodep->lhsp()) nodep->v3fatalSrc("For unary ops only!"); + if (m_vup->prelim()) { + // See similar handling in visit_cmp_eq_gt where created + iterateCheckString(nodep,"LHS",nodep->lhsp(),BOTH); + nodep->dtypeSetSigned32(); + } + } void visit_negate_not(AstNodeUniop* nodep, bool real_ok) { // CALLER: (real_ok=false) Not diff --git a/test_regress/t/t_string_type_methods.pl b/test_regress/t/t_string_type_methods.pl new file mode 100755 index 000000000..f91289753 --- /dev/null +++ b/test_regress/t/t_string_type_methods.pl @@ -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; diff --git a/test_regress/t/t_string_type_methods.v b/test_regress/t/t_string_type_methods.v new file mode 100644 index 000000000..03e5ca566 --- /dev/null +++ b/test_regress/t/t_string_type_methods.v @@ -0,0 +1,115 @@ +// 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); +`define checkg(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%g' exp='%g'\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0); + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + string s; + + integer cyc=0; + + // Check constification + initial begin + s="1234"; `checkh(s.len(),4); +`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; + s="b"; if (s.icompare("A") < 0) $stop; + s="b"; if (s.icompare("B") != 0) $stop; + s="b"; if (s.icompare("C") >= 0) $stop; + s="101"; `checkh(s.atoi(), 'd101); + s="101"; `checkh(s.atohex(), 'h101); + s="101"; `checkh(s.atooct(), 'o101); + s="101"; `checkh(s.atobin(), 'b101); + s="1.23"; `checkg(s.atoreal(), 1.23); + s.itoa(123); `checks(s, "123"); + s.hextoa(123); `checks(s, "7b"); + s.octtoa(123); `checks(s, "173"); + s.bintoa(123); `checks(s, "1111011"); + s.realtoa(1.23); `checks(s, "1.23"); +`endif + end + + // Check runtime + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc==0) begin + // Setup + s = "1234"; + end + else if (cyc==1) begin + `checkh(s.len(),4); +`ifndef VERILATOR + s.putc(2, "z"); + end + else if (cyc==2) begin + `checks(s, "12z4"); + `checkh(s.getc(2), "z"); + s="abCD"; + end + else if (cyc==3) begin + `checks(s.toupper(), "ABCD"); + `checks(s.tolower(), "abcd"); + s="b"; + end + else if (cyc==5) begin + 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; + s="101"; + end + else if (cyc==7) begin + `checkh(s.atoi(), 'd101); + `checkh(s.atohex(), 'h101); + `checkh(s.atooct(), 'o101); + `checkh(s.atobin(), 'b101); + s="1.23"; + end + else if (cyc==8) begin + `checkg(s.atoreal(), 1.23); + s.itoa(123); + end + else if (cyc==10) begin + `checks(s, "123"); + s.hextoa(123); + end + else if (cyc==11) begin + `checks(s, "7b"); + s.octtoa(123); + end + else if (cyc==12) begin + `checks(s, "173"); + s.bintoa(123); + end + else if (cyc==13) begin + `checks(s, "1111011"); + s.realtoa(1.23); + end + else if (cyc==14) begin + `checks(s, "1.23"); +`endif + end + else if (cyc==99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule