diff --git a/Changes b/Changes index a636b9292..18d9f6ec3 100644 --- a/Changes +++ b/Changes @@ -22,6 +22,7 @@ Verilator 5.007 devel * Support interface classes and class implements. * Support global clocking and $global_clock. * Support class parameters without initial values. +* Support cast to numbers from strings. * Support struct I/O in --lib-create (#3378) (#3892). [Varun Koyyalagunta] * Support function calls without parenthesis (#3903) (#3902). [Ryszard Rozak, Antmicro Ltd] * Support class extending its parameter (#3904). [Ryszard Rozak, Antmicro Ltd] diff --git a/include/verilated.cpp b/include/verilated.cpp index c44e17253..5ccc66e7b 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -1868,6 +1868,33 @@ IData VL_ATOI_N(const std::string& str, int base) VL_PURE { if (errno != 0) v = 0; return static_cast(v); } +IData VL_NTOI_I(int obits, const std::string& str) VL_PURE { return VL_NTOI_Q(obits, str); } +QData VL_NTOI_Q(int obits, const std::string& str) VL_PURE { + QData out = 0; + const size_t procLen = std::min(str.length(), static_cast(8)); + const char* const datap = str.data(); + int pos = static_cast(str.length()) - 1; + int bit = 0; + while (bit < obits && pos >= 0) { + out |= static_cast(datap[pos]) << VL_BITBIT_Q(bit); + bit += 8; + --pos; + } + return out & VL_MASK_Q(obits); +} +void VL_NTOI_W(int obits, WDataOutP owp, const std::string& str) VL_PURE { + const int words = VL_WORDS_I(obits); + for (int i = 0; i < words; ++i) owp[i] = 0; + const char* const datap = str.data(); + int pos = static_cast(str.length()) - 1; + int bit = 0; + while (bit < obits && pos >= 0) { + owp[VL_BITWORD_I(bit)] |= static_cast(datap[pos]) << VL_BITBIT_I(bit); + bit += 8; + --pos; + } + owp[words - 1] &= VL_MASK_E(obits); +} //=========================================================================== // Readmem/writemem diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index 665fce9c8..bf39e3c12 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -2164,6 +2164,9 @@ inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool igno } extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE; +extern IData VL_NTOI_I(int obits, const std::string& str) VL_PURE; +extern QData VL_NTOI_Q(int obits, const std::string& str) VL_PURE; +extern void VL_NTOI_W(int obits, WDataOutP owp, const std::string& str) VL_PURE; extern IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE; diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 3376c64b9..6190da6cb 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -4773,6 +4773,21 @@ public: bool cleanLhs() const override { return true; } bool sizeMattersLhs() const override { return false; } }; +class AstNToI final : public AstNodeUniop { + // String to any-size integral +public: + AstNToI(FileLine* fl, AstNodeExpr* lhsp, AstNodeDType* dtypep = nullptr) + : ASTGEN_SUPER_NToI(fl, lhsp) { + this->dtypep(dtypep); + } + ASTGEN_MEMBERS_AstNToI; + void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNToI(lhs); } + string emitVerilog() override { return "'(%l)"; } + string emitC() override { return "VL_NTOI_%nq(%nw, %P, %li)"; } + bool cleanOut() const override { return true; } + bool cleanLhs() const override { return true; } + bool sizeMattersLhs() const override { return false; } +}; class AstNegate final : public AstNodeUniop { public: AstNegate(FileLine* fl, AstNodeExpr* lhsp) diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 6b241fac3..b99717d60 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -2212,7 +2212,7 @@ V3Number& V3Number::opAssignNonXZ(const V3Number& lhs, bool ignoreXZ) { m_data.str() = lhs.m_data.str(); } } else if (VL_UNLIKELY(lhs.isString())) { - // Non-compatible types, erase value. + // Non-compatible types, see also opAToN() setZero(); } else { // Also handles double as is just bits @@ -2224,6 +2224,20 @@ V3Number& V3Number::opAssignNonXZ(const V3Number& lhs, bool ignoreXZ) { return *this; } +V3Number& V3Number::opNToI(const V3Number& lhs) { + // String to packed number + NUM_ASSERT_OP_ARGS1(lhs); + NUM_ASSERT_STRING_ARGS1(lhs); + setZero(); + const string& str = lhs.toString(); + for (size_t n = 0; n < str.length(); ++n) { + const char c = str[str.length() - 1 - n]; + for (size_t cbit = 0; cbit < 8; ++cbit) + setBit(n * 8 + cbit, VL_BITISSET_I(c, cbit) ? 1 : 0); + } + return *this; +} + V3Number& V3Number::opExtendS(const V3Number& lhs, uint32_t lbits) { // Note may be a width change during the sign extension NUM_ASSERT_OP_ARGS1(lhs); diff --git a/src/V3Number.h b/src/V3Number.h index f9b6bdc05..25b59706a 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -762,6 +762,7 @@ public: // "N" - string operations V3Number& opAtoN(const V3Number& lhs, int base); + V3Number& opNToI(const V3Number& lhs); V3Number& opPutcN(const V3Number& lhs, const V3Number& rhs, const V3Number& ths); V3Number& opGetcN(const V3Number& lhs, const V3Number& rhs); V3Number& opSubstrN(const V3Number& lhs, const V3Number& rhs, const V3Number& ths); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index ab815b617..62b5ed5cf 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -463,6 +463,13 @@ private: } } } + void visit(AstNToI* nodep) override { + // Created here, should be already sized + if (m_vup->prelim()) { + UASSERT_OBJ(nodep->dtypep(), nodep, "NToI should be sized when created"); + iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); + } + } // Widths: Constant, terminal void visit(AstTime* nodep) override { nodep->dtypeSetUInt64(); } @@ -1935,7 +1942,9 @@ private: AstNodeExpr* newp = nullptr; if (bad) { } else if (const AstBasicDType* const basicp = toDtp->basicp()) { - if (!basicp->isDouble() && !fromDtp->isDouble()) { + if (!basicp->isString() && fromDtp->isString()) { + newp = new AstNToI{nodep->fileline(), nodep->fromp()->unlinkFrBack(), toDtp}; + } else if (!basicp->isDouble() && !fromDtp->isDouble()) { AstNodeDType* const origDTypep = nodep->dtypep(); const int width = toDtp->width(); castSized(nodep, nodep->fromp(), width); @@ -1945,7 +1954,8 @@ private: iterateCheck(nodep, "value", nodep->fromp(), SELF, FINAL, fromDtp, EXTEND_EXP, false); } - if (basicp->isDouble() && !nodep->fromp()->isDouble()) { + if (newp) { + } else if (basicp->isDouble() && !nodep->fromp()->isDouble()) { if (nodep->fromp()->isSigned()) { newp = new AstISToRD{nodep->fileline(), nodep->fromp()->unlinkFrBack()}; } else { diff --git a/test_regress/t/t_string_to_bit.pl b/test_regress/t/t_string_to_bit.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_string_to_bit.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env 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. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_string_to_bit.v b/test_regress/t/t_string_to_bit.v new file mode 100644 index 000000000..b8b30be23 --- /dev/null +++ b/test_regress/t/t_string_to_bit.v @@ -0,0 +1,106 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.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); + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + integer cyc = 0; + + string str0; + string str1; + string str2; + + typedef bit [31:0] bit_t; + typedef logic [31:0] logic_t; + typedef bit [55:0] quad_t; + typedef bit [87:0] wide_t; + + bit_t bdata[3]; + bit_t ldata[3]; + quad_t qdata[3]; + wide_t wdata[3]; + + initial begin + str0 = "sm"; + str1 = "medium"; + str2 = "veryverylongwilltruncate"; + bdata[0] = bit_t'(str0); + bdata[1] = bit_t'(str1); + bdata[2] = bit_t'(str2); + `checks(bdata[0], "sm"); + `checks(bdata[1], "dium"); + `checks(bdata[2], "cate"); + if (bdata[0] != 32'h0000736d) $stop; + if (bdata[1] != 32'h6469756d) $stop; + ldata[0] = logic_t'(str0); + ldata[1] = logic_t'(str1); + ldata[2] = logic_t'(str2); + `checks(ldata[0], "sm"); + `checks(ldata[1], "dium"); + `checks(ldata[2], "cate"); + qdata[0] = quad_t'(str0); + qdata[1] = quad_t'(str1); + qdata[2] = quad_t'(str2); + `checks(qdata[0], "sm"); + `checks(qdata[1], "medium"); + `checks(qdata[2], "runcate"); + wdata[0] = wide_t'(str0); + wdata[1] = wide_t'(str1); + wdata[2] = wide_t'(str2); + `checks(wdata[0], "sm"); + `checks(wdata[1], "medium"); + `checks(wdata[2], "illtruncate"); + end + + // Test loop + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc == 1) begin + str0 = "z"; + str1 = "zmedi"; + str2 = "ziggylonglonglongtruncate"; + end + else if (cyc == 2) begin + bdata[0] = bit_t'(str0); + bdata[1] = bit_t'(str1); + bdata[2] = bit_t'(str2); + ldata[0] = logic_t'(str0); + ldata[1] = logic_t'(str1); + ldata[2] = logic_t'(str2); + qdata[0] = quad_t'(str0); + qdata[1] = quad_t'(str1); + qdata[2] = quad_t'(str2); + wdata[0] = wide_t'(str0); + wdata[1] = wide_t'(str1); + wdata[2] = wide_t'(str2); + end + else if (cyc == 3) begin + `checks(bdata[0], "z"); + `checks(bdata[1], "medi"); + `checks(bdata[2], "cate"); + `checks(ldata[0], "z"); + `checks(ldata[1], "medi"); + `checks(ldata[2], "cate"); + `checks(qdata[0], "z"); + `checks(qdata[1], "zmedi"); + `checks(qdata[2], "runcate"); + `checks(wdata[0], "z"); + `checks(wdata[1], "zmedi"); + `checks(wdata[2], "ongtruncate"); + end + // + else if (cyc==99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule