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