forked from github/verilator
Fix implicit conversion of floats to wide integers.
This commit is contained in:
parent
ebeb645539
commit
e6beab4037
2
Changes
2
Changes
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
21
test_regress/t/t_math_real_round.pl
Executable file
21
test_regress/t/t_math_real_round.pl
Executable 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;
|
101
test_regress/t/t_math_real_round.v
Normal file
101
test_regress/t/t_math_real_round.v
Normal 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
|
Loading…
Reference in New Issue
Block a user