Support cast to numbers from strings.

This commit is contained in:
Wilson Snyder 2023-02-28 23:34:33 -05:00
parent c6a569df49
commit b1a95f642f
9 changed files with 201 additions and 3 deletions

View File

@ -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]

View File

@ -1868,6 +1868,33 @@ IData VL_ATOI_N(const std::string& str, int base) VL_PURE {
if (errno != 0) v = 0;
return static_cast<IData>(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<size_t>(8));
const char* const datap = str.data();
int pos = static_cast<int>(str.length()) - 1;
int bit = 0;
while (bit < obits && pos >= 0) {
out |= static_cast<QData>(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<int>(str.length()) - 1;
int bit = 0;
while (bit < obits && pos >= 0) {
owp[VL_BITWORD_I(bit)] |= static_cast<EData>(datap[pos]) << VL_BITBIT_I(bit);
bit += 8;
--pos;
}
owp[words - 1] &= VL_MASK_E(obits);
}
//===========================================================================
// Readmem/writemem

View File

@ -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;

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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 {

View File

@ -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;

View File

@ -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