Skip to content

Commit 9b26cb1

Browse files
committed
SystemVerilog: floating-point casts
Casts from real/shortreal/realtime to integers must use round-to-nearest-ties-to-away. The standard doesn't specify how to round int-to-float or double-to-single. We use RNA for consistency.
1 parent fd24964 commit 9b26cb1

File tree

5 files changed

+43
-14
lines changed

5 files changed

+43
-14
lines changed
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
KNOWNBUG
1+
CORE broken-smt-backend
22
cast_from_real1.sv
33

44
^EXIT=0$
55
^SIGNAL=0$
66
--
77
^warning: ignoring
88
--
9-
Verilog casts from real to integer round, and do not truncate.

regression/verilog/expressions/cast_from_real1.sv

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@ module main;
33
always assert (integer'(1.0) == 1);
44
always assert (integer'(-1.0) == -1);
55

6-
// Casting rounds away from zero (1800-2017 6.12.2)
7-
always assert (integer'(1.9) == 2);
6+
// Casting rounds to nearest-ties-away-from zero (1800-2017 6.12.2)
7+
// Examples from the standard.
8+
always assert (integer'(35.7) == 36);
9+
always assert (integer'(35.5) == 36);
10+
always assert (integer'(35.2) == 35);
11+
always assert (integer'(-1.5) == -2);
12+
always assert (integer'(1.5) == 2);
813

914
endmodule
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
module main;
22

3-
// no rounding
3+
// No rounding.
44
p0: assert final (real'(0) == 0.0);
55
p1: assert final (real'(1) == 1.0);
66

7-
// rounding, away from zero
7+
// Rounding. The standard does not say how. This is RNA.
8+
// Icarus Verilog, Aldec Riviera, Questa, VCS agree.
9+
// Cadence Xcelium disagrees.
810
p2: assert final (real'('hffff_ffff_ffff_ffff) == real'('h1_0000_0000_0000_0000));
9-
p3: assert final (real'(-'sh0_ffff_ffff_ffff_ffff) == real'(-'sh1_0000_0000_0000_0000));
1011

1112
endmodule
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
KNOWNBUG
1+
CORE
22
cast_to_real2.sv
33

44
^EXIT=0$
55
^SIGNAL=0$
66
--
77
^warning: ignoring
88
--
9-
Typechecking yields inconsistent types.

src/verilog/verilog_lowering.cpp

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -251,19 +251,44 @@ exprt verilog_lowering_cast(typecast_exprt expr)
251251
auto &src_type = expr.op().type();
252252
auto &dest_type = expr.type();
253253

254+
// float to int
255+
if(
256+
(src_type.id() == ID_verilog_real ||
257+
src_type.id() == ID_verilog_shortreal ||
258+
src_type.id() == ID_verilog_realtime || src_type.id() == ID_floatbv) &&
259+
(dest_type.id() == ID_signedbv || dest_type.id() == ID_unsignedbv ||
260+
dest_type.id() == ID_bool))
261+
{
262+
// 1800-2017 6.12.2 requires rounding away from zero when converting
263+
// to integers.
264+
auto rm = floatbv_rounding_mode(ieee_floatt::rounding_modet::ROUND_TO_AWAY);
265+
auto rti =
266+
floatbv_round_to_integral_exprt{expr.op(), rm}.with_source_location(
267+
expr.source_location());
268+
auto new_cast = floatbv_typecast_exprt{
269+
rti,
270+
ieee_floatt::rounding_mode_expr(
271+
ieee_floatt::rounding_modet::ROUND_TO_ZERO),
272+
verilog_lowering(dest_type)};
273+
return std::move(new_cast);
274+
}
275+
276+
// float to float, int to float
254277
if(
255278
dest_type.id() == ID_verilog_real ||
256279
dest_type.id() == ID_verilog_shortreal ||
257-
dest_type.id() == ID_verilog_realtime || src_type.id() == ID_floatbv)
280+
dest_type.id() == ID_verilog_realtime)
258281
{
259-
// This may require rounding, hence add rounding mode.
260-
// 1800-2017 6.12.2 requires rounding away from zero,
261-
// which we don't have.
282+
// The standard does not say how to round double precision
283+
// to single precision floating-point. It does not say how
284+
// to round integers to float. We simply use RNA,
285+
// given the explicit requirement to use RNA when converting
286+
// float to integer.
262287
expr.type() = verilog_lowering(dest_type);
263288
auto new_cast = floatbv_typecast_exprt{
264289
expr.op(),
265290
ieee_floatt::rounding_mode_expr(
266-
ieee_floatt::rounding_modet::ROUND_TO_EVEN),
291+
ieee_floatt::rounding_modet::ROUND_TO_AWAY),
267292
verilog_lowering(dest_type)};
268293
return std::move(new_cast);
269294
}

0 commit comments

Comments
 (0)