Fix implicit conversion of floats to wide integers.

This commit is contained in:
Wilson Snyder 2020-03-31 20:42:07 -04:00
parent ebeb645539
commit e6beab4037
8 changed files with 224 additions and 18 deletions

View File

@ -23,6 +23,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
**** Fix assertions with unique case inside, #2199. [hdzhangdoc]
**** Fix implicit conversion of floats to wide integers.
* Verilator 4.030 2020-03-08

View File

@ -695,13 +695,12 @@ static inline QData VL_CVT_Q_D(double lhs) VL_PURE {
union { double d; QData q; } u; u.d=lhs; return u.q; }
/// Return double from QData (numeric)
static inline double VL_ITOR_D_I(IData lhs) VL_PURE {
return static_cast<double>(static_cast<vlsint32_t>(lhs)); }
return static_cast<double>(static_cast<vlsint32_t>(lhs));
}
/// Return QData from double (numeric)
static inline IData VL_RTOI_I_D(double lhs) VL_PURE {
return static_cast<vlsint32_t>(VL_TRUNC(lhs)); }
/// Return QData from double (numeric)
static inline IData VL_RTOIROUND_I_D(double lhs) VL_PURE {
return static_cast<vlsint32_t>(VL_ROUND(lhs)); }
static inline IData VL_RTOI_I_D(double lhs) VL_PURE {
return static_cast<vlsint32_t>(VL_TRUNC(lhs));
}
// Sign extend such that if MSB set, we get ffff_ffff, else 0s
// (Requires clean input)
@ -1320,6 +1319,14 @@ static inline WDataOutP VL_NEGATE_W(int words, WDataOutP owp, WDataInP lwp) VL_M
}
return owp;
}
static void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE {
EData carry = 1;
for (int i = 0; i < words; ++i) {
EData word = ~owp_lwp[i] + carry;
carry = (word < ~owp_lwp[i]);
owp_lwp[i] = word;
}
}
// EMIT_RULE: VL_MUL: oclean=dirty; lclean==clean; rclean==clean;
// EMIT_RULE: VL_DIV: oclean=dirty; lclean==clean; rclean==clean;
@ -2236,6 +2243,50 @@ static inline WDataOutP VL_SEL_WWII(int obits, int lbits, int, int,
return owp;
}
//======================================================================
// Math needing insert/select
/// Return QData from double (numeric)
// EMIT_RULE: VL_RTOIROUND_Q_D: oclean=dirty; lclean==clean/real
static inline QData VL_RTOIROUND_Q_D(int bits, double lhs) VL_PURE {
// IEEE format: [63]=sign [62:52]=exp+1023 [51:0]=mantissa
// This does not need to support subnormals as they are sub-integral
lhs = VL_ROUND(lhs);
if (!lhs) return 0;
QData q = VL_CVT_Q_D(lhs);
int lsb = static_cast<int>((q >> VL_ULL(52)) & VL_MASK_Q(11)) - 1023 - 52;
vluint64_t mantissa = (q & VL_MASK_Q(52)) | (VL_ULL(1) << 52);
vluint64_t out = 0;
if (lsb < 0) {
out = mantissa >> -lsb;
} else if (lsb < 64) {
out = mantissa << lsb;
}
if (lhs < 0) out = -out;
return out;
}
static inline IData VL_RTOIROUND_I_D(int bits, double lhs) VL_PURE {
return static_cast<IData>(VL_RTOIROUND_Q_D(bits, lhs));
}
static inline WDataOutP VL_RTOIROUND_W_D(int obits, WDataOutP owp, double lhs) VL_PURE {
// IEEE format: [63]=sign [62:52]=exp+1023 [51:0]=mantissa
// This does not need to support subnormals as they are sub-integral
lhs = VL_ROUND(lhs);
VL_ZERO_W(obits, owp);
if (!lhs) return owp;
QData q = VL_CVT_Q_D(lhs);
int lsb = static_cast<int>((q >> VL_ULL(52)) & VL_MASK_Q(11)) - 1023 - 52;
vluint64_t mantissa = (q & VL_MASK_Q(52)) | (VL_ULL(1) << 52);
if (lsb < 0) {
VL_SET_WQ(owp, mantissa >> -lsb);
} else if (lsb < obits) {
int lsbword = VL_BITWORD_I(lsb);
_VL_INSERT_WQ(obits, owp, mantissa, lsb + 52, lsb);
}
if (lhs < 0) VL_NEGATE_INPLACE_W(VL_WORDS_I(obits), owp);
return owp;
}
//======================================================================
// Range assignments

View File

@ -1693,6 +1693,7 @@ public:
// METHODS
virtual bool hasDType() const { return true; }
virtual string emitVerilog() = 0; /// Format string for verilog writing; see V3EmitV
// For documentation on emitC format see EmitCStmts::emitOpName
virtual string emitC() = 0;
virtual string emitSimpleOperator() { return ""; }
virtual bool cleanOut() const = 0; // True if output has extra upper bits zero

View File

@ -4585,16 +4585,19 @@ public:
virtual int instrCount() const { return instrCountDouble(); }
};
class AstRToIRoundS : public AstNodeUniop {
// Convert real to integer, with arbitrary sized output (not just "integer" format)
public:
AstRToIRoundS(FileLine* fl, AstNode* lhsp)
: ASTGEN_SUPER(fl, lhsp) { dtypeSetSigned32(); }
: ASTGEN_SUPER(fl, lhsp) {
dtypeSetSigned32();
}
ASTNODE_NODE_FUNCS(RToIRoundS)
virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRToIRoundS(lhs); }
virtual string emitVerilog() { return "%f$rtoi_rounded(%l)"; }
virtual string emitC() { return "VL_RTOIROUND_I_D(%li)"; }
virtual string emitC() { return "VL_RTOIROUND_%nq_D(%nw, %P, %li)"; }
virtual bool cleanOut() const { return false; }
virtual bool cleanLhs() const { return false; } // Eliminated before matters
virtual bool sizeMattersLhs() const { return false; } // Eliminated before matters
virtual bool cleanLhs() const { return false; }
virtual bool sizeMattersLhs() const { return false; }
virtual int instrCount() const { return instrCountDouble(); }
};
class AstIToRD : public AstNodeUniop {

View File

@ -2055,8 +2055,28 @@ V3Number& V3Number::opRToIRoundS(const V3Number& lhs) {
NUM_ASSERT_OP_ARGS1(lhs);
NUM_ASSERT_DOUBLE_ARGS1(lhs);
double v = VL_ROUND(lhs.toDouble());
vlsint32_t i = static_cast<vlsint32_t>(v); // C converts from double to vlsint32
return setLongS(i);
setZero();
union { double d; vluint64_t q; } u;
u.d = v; if (u.d) {}
int exp = static_cast<int>((u.q >> VL_ULL(52)) & VL_MASK_Q(11)) - 1023;
int lsb = exp - 52;
vluint64_t mantissa = (u.q & VL_MASK_Q(52)) | (VL_ULL(1) << 52);
if (v != 0) {
// IEEE format: [63]=sign [62:52]=exp+1023 [51:0]=mantissa
// This does not need to support subnormals as they are sub-integral
for (int bit = 0; bit <= 52; ++bit) {
if (mantissa & (VL_ULL(1) << bit)) {
int outbit = bit + lsb;
if (outbit >= 0) setBit(outbit, 1);
}
}
if (v < 0) {
V3Number noSign = *this;
opNegate(noSign);
}
}
return *this;
}
V3Number& V3Number::opRealToBits(const V3Number& lhs) {
NUM_ASSERT_OP_ARGS1(lhs);

View File

@ -322,11 +322,17 @@ private:
virtual void visit(AstIToRD* nodep) VL_OVERRIDE { visit_Or_Ls32(nodep); }
// Widths: Output integer signed, input real
virtual void visit(AstRToIS* nodep) VL_OVERRIDE { visit_Os32_Lr(nodep); }
virtual void visit(AstRToIRoundS* nodep) VL_OVERRIDE { visit_Os32_Lr(nodep); }
virtual void visit(AstRToIS* nodep) VL_OVERRIDE { visit_Os32_Lr(nodep); }
virtual void visit(AstRToIRoundS* nodep) VL_OVERRIDE {
// Only created here, size comes from upper expression
if (m_vup->prelim()) { // First stage evaluation
iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
}
if (!nodep->dtypep()->widthSized()) nodep->v3fatalSrc("RToIRoundS should be presized");
}
// Widths: Output integer unsigned, input real
virtual void visit(AstRealToBits* nodep) VL_OVERRIDE { visit_Ou64_Lr(nodep); }
virtual void visit(AstRealToBits* nodep) VL_OVERRIDE { visit_Ou64_Lr(nodep); }
// Output integer, input string
virtual void visit(AstLenN* nodep) VL_OVERRIDE { visit_Os32_string(nodep); }
@ -4180,7 +4186,7 @@ private:
underp = spliceCvtD(underp);
underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, FINAL).p());
} else if (!expDTypep->isDouble() && underp->isDouble()) {
underp = spliceCvtS(underp, true); // Round RHS
underp = spliceCvtS(underp, true, expDTypep->width()); // Round RHS
underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, FINAL).p());
} else if (expDTypep->isString() && !underp->dtypep()->isString()) {
underp = spliceCvtString(underp);
@ -4309,7 +4315,7 @@ private:
if (nodep && nodep->isDouble()) {
nodep->v3error("Expected integral (non-" << nodep->dtypep()->prettyDTypeName()
<< ") input to " << nodep->backp()->prettyTypeName());
nodep = spliceCvtS(nodep, true);
nodep = spliceCvtS(nodep, true, 32);
}
return nodep;
}
@ -4328,7 +4334,7 @@ private:
return nodep;
}
}
AstNode* spliceCvtS(AstNode* nodep, bool warnOn) {
AstNode* spliceCvtS(AstNode* nodep, bool warnOn, int width) {
// IEEE-2012 11.8.1: Signed: Type coercion creates signed
// 11.8.2: Argument to convert is self-determined
if (nodep && nodep->dtypep()->skipRefp()->isDouble()) {
@ -4338,6 +4344,7 @@ private:
if (warnOn) nodep->v3warn(REALCVT, "Implicit conversion of real to integer");
AstNode* newp = new AstRToIRoundS(nodep->fileline(), nodep);
linker.relink(newp);
newp->dtypeSetBitSized(width, AstNumeric::SIGNED);
return newp;
} else {
return nodep;

View File

@ -0,0 +1,21 @@
#!/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.
# 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,101 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Copyright 2011 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
`define is_near_real(a,b) (( ((a)<(b)) ? (b)-(a) : (a)-(b)) < (((a)/(b))*0.0001))
`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);
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc=0;
real r;
reg [31:0] v32;
reg [63:0] v64;
reg [95:0] v96;
initial begin
// verilator lint_off REALCVT
v32 = -1.5;
v64 = -1.5;
v96 = -1.5;
// verilator lint_on REALCVT
`checkh(v32, 32'hfffffffe);
`checkh(v64, 64'hfffffffffffffffe);
`checkh(v96, 96'hfffffffffffffffffffffffe);
// verilator lint_off REALCVT
v32 = 12456789012345678912345.5;
v64 = 12456789012345678912345.5;
v96 = 12456789012345678912345.5;
// verilator lint_on REALCVT
`checkh(v32, 32'he5400000);
`checkh(v64, 64'h48acb7d4e5400000);
`checkh(v96, 96'h000002a348acb7d4e5400000);
// verilator lint_off REALCVT
v32 = -12456789012345678912345.5;
v64 = -12456789012345678912345.5;
v96 = -12456789012345678912345.5;
// verilator lint_on REALCVT
`checkh(v32, 32'h1ac00000);
`checkh(v64, 64'hb753482b1ac00000);
`checkh(v96, 96'hfffffd5cb753482b1ac00000);
end
// Test loop
always @ (posedge clk) begin
cyc <= cyc + 1;
if (cyc == 10) begin
r <= 0;
end
else if (cyc == 11) begin
// verilator lint_off REALCVT
v32 = r;
v64 = r;
v96 = r;
// verilator lint_on REALCVT
`checkh(v32, '0);
`checkh(v64, '0);
`checkh(v96, '0);
end
else if (cyc == 20) begin
r <= -5.24567;
end
else if (cyc == 21) begin
// verilator lint_off REALCVT
v32 = r;
v64 = r;
v96 = r;
// verilator lint_on REALCVT
`checkh(v32, 32'hfffffffb);
`checkh(v64, 64'hfffffffffffffffb);
`checkh(v96, 96'hfffffffffffffffffffffffb);
end
else if (cyc == 30) begin
r <= 12456789012345678912345.5;
end
else if (cyc == 31) begin
// verilator lint_off REALCVT
v32 = r;
v64 = r;
v96 = r;
// verilator lint_on REALCVT
`checkh(v32, 32'he5400000);
`checkh(v64, 64'h48acb7d4e5400000);
`checkh(v96, 96'h000002a348acb7d4e5400000);
end
else if (cyc == 99) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule