forked from github/verilator
Support cast to numbers from strings.
This commit is contained in:
parent
c6a569df49
commit
b1a95f642f
1
Changes
1
Changes
@ -22,6 +22,7 @@ Verilator 5.007 devel
|
|||||||
* Support interface classes and class implements.
|
* Support interface classes and class implements.
|
||||||
* Support global clocking and $global_clock.
|
* Support global clocking and $global_clock.
|
||||||
* Support class parameters without initial values.
|
* Support class parameters without initial values.
|
||||||
|
* Support cast to numbers from strings.
|
||||||
* Support struct I/O in --lib-create (#3378) (#3892). [Varun Koyyalagunta]
|
* Support struct I/O in --lib-create (#3378) (#3892). [Varun Koyyalagunta]
|
||||||
* Support function calls without parenthesis (#3903) (#3902). [Ryszard Rozak, Antmicro Ltd]
|
* Support function calls without parenthesis (#3903) (#3902). [Ryszard Rozak, Antmicro Ltd]
|
||||||
* Support class extending its parameter (#3904). [Ryszard Rozak, Antmicro Ltd]
|
* Support class extending its parameter (#3904). [Ryszard Rozak, Antmicro Ltd]
|
||||||
|
@ -1868,6 +1868,33 @@ IData VL_ATOI_N(const std::string& str, int base) VL_PURE {
|
|||||||
if (errno != 0) v = 0;
|
if (errno != 0) v = 0;
|
||||||
return static_cast<IData>(v);
|
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
|
// Readmem/writemem
|
||||||
|
@ -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_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;
|
extern IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE;
|
||||||
|
|
||||||
|
@ -4773,6 +4773,21 @@ public:
|
|||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
bool sizeMattersLhs() const override { return false; }
|
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 {
|
class AstNegate final : public AstNodeUniop {
|
||||||
public:
|
public:
|
||||||
AstNegate(FileLine* fl, AstNodeExpr* lhsp)
|
AstNegate(FileLine* fl, AstNodeExpr* lhsp)
|
||||||
|
@ -2212,7 +2212,7 @@ V3Number& V3Number::opAssignNonXZ(const V3Number& lhs, bool ignoreXZ) {
|
|||||||
m_data.str() = lhs.m_data.str();
|
m_data.str() = lhs.m_data.str();
|
||||||
}
|
}
|
||||||
} else if (VL_UNLIKELY(lhs.isString())) {
|
} else if (VL_UNLIKELY(lhs.isString())) {
|
||||||
// Non-compatible types, erase value.
|
// Non-compatible types, see also opAToN()
|
||||||
setZero();
|
setZero();
|
||||||
} else {
|
} else {
|
||||||
// Also handles double as is just bits
|
// Also handles double as is just bits
|
||||||
@ -2224,6 +2224,20 @@ V3Number& V3Number::opAssignNonXZ(const V3Number& lhs, bool ignoreXZ) {
|
|||||||
return *this;
|
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) {
|
V3Number& V3Number::opExtendS(const V3Number& lhs, uint32_t lbits) {
|
||||||
// Note may be a width change during the sign extension
|
// Note may be a width change during the sign extension
|
||||||
NUM_ASSERT_OP_ARGS1(lhs);
|
NUM_ASSERT_OP_ARGS1(lhs);
|
||||||
|
@ -762,6 +762,7 @@ public:
|
|||||||
|
|
||||||
// "N" - string operations
|
// "N" - string operations
|
||||||
V3Number& opAtoN(const V3Number& lhs, int base);
|
V3Number& opAtoN(const V3Number& lhs, int base);
|
||||||
|
V3Number& opNToI(const V3Number& lhs);
|
||||||
V3Number& opPutcN(const V3Number& lhs, const V3Number& rhs, const V3Number& ths);
|
V3Number& opPutcN(const V3Number& lhs, const V3Number& rhs, const V3Number& ths);
|
||||||
V3Number& opGetcN(const V3Number& lhs, const V3Number& rhs);
|
V3Number& opGetcN(const V3Number& lhs, const V3Number& rhs);
|
||||||
V3Number& opSubstrN(const V3Number& lhs, const V3Number& rhs, const V3Number& ths);
|
V3Number& opSubstrN(const V3Number& lhs, const V3Number& rhs, const V3Number& ths);
|
||||||
|
@ -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
|
// Widths: Constant, terminal
|
||||||
void visit(AstTime* nodep) override { nodep->dtypeSetUInt64(); }
|
void visit(AstTime* nodep) override { nodep->dtypeSetUInt64(); }
|
||||||
@ -1935,7 +1942,9 @@ private:
|
|||||||
AstNodeExpr* newp = nullptr;
|
AstNodeExpr* newp = nullptr;
|
||||||
if (bad) {
|
if (bad) {
|
||||||
} else if (const AstBasicDType* const basicp = toDtp->basicp()) {
|
} 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();
|
AstNodeDType* const origDTypep = nodep->dtypep();
|
||||||
const int width = toDtp->width();
|
const int width = toDtp->width();
|
||||||
castSized(nodep, nodep->fromp(), width);
|
castSized(nodep, nodep->fromp(), width);
|
||||||
@ -1945,7 +1954,8 @@ private:
|
|||||||
iterateCheck(nodep, "value", nodep->fromp(), SELF, FINAL, fromDtp, EXTEND_EXP,
|
iterateCheck(nodep, "value", nodep->fromp(), SELF, FINAL, fromDtp, EXTEND_EXP,
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
if (basicp->isDouble() && !nodep->fromp()->isDouble()) {
|
if (newp) {
|
||||||
|
} else if (basicp->isDouble() && !nodep->fromp()->isDouble()) {
|
||||||
if (nodep->fromp()->isSigned()) {
|
if (nodep->fromp()->isSigned()) {
|
||||||
newp = new AstISToRD{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
|
newp = new AstISToRD{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
|
||||||
} else {
|
} else {
|
||||||
|
21
test_regress/t/t_string_to_bit.pl
Executable file
21
test_regress/t/t_string_to_bit.pl
Executable 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;
|
106
test_regress/t/t_string_to_bit.v
Normal file
106
test_regress/t/t_string_to_bit.v
Normal 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
|
Loading…
Reference in New Issue
Block a user