-# IEEE Floating Point Adder (Single Precision)
-# Copyright (C) Jonathan P Dawson 2013
-# 2013-12-12
+"""IEEE754 Floating Point Library
-from nmigen import Signal, Cat, Const, Mux, Module, Elaboratable
-from math import log
+Copyright (C) 2019 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+Copyright (C) 2019,2022 Jacob Lifshay <programmerjake@gmail.com>
+
+"""
+
+
+from nmigen import (Signal, Cat, Const, Mux, Module, Elaboratable, Array,
+ Value, Shape, signed, unsigned)
+from nmigen.utils import bits_for
from operator import or_
from functools import reduce
from nmutil.pipeline import ObjectProxy
import unittest
import math
+import enum
+
+try:
+ from nmigen.hdl.smtlib2 import RoundingModeEnum
+ _HAVE_SMTLIB2 = True
+except ImportError:
+ _HAVE_SMTLIB2 = False
+
+# value so FPRoundingMode.to_smtlib2 can detect when no default is supplied
+_raise_err = object()
+
+
+class FPRoundingMode(enum.Enum):
+ # matches the FPSCR.RN field values, but includes some extra
+ # values (>= 0b100) used in miscellaneous instructions.
+
+ # naming matches smtlib2 names, doc strings are the OpenPower ISA
+ # specification's names (v3.1 section 7.3.2.6 --
+ # matches values in section 4.3.6).
+ RNE = 0b00
+ """Round to Nearest Even
+
+ Rounds to the nearest representable floating-point number, ties are
+ rounded to the number with the even mantissa. Treats +-Infinity as if
+ it were a normalized floating-point number when deciding which number
+ is closer when rounding. See IEEE754 spec. for details.
+ """
+
+ ROUND_NEAREST_TIES_TO_EVEN = RNE
+ DEFAULT = RNE
+
+ RTZ = 0b01
+ """Round towards Zero
+
+ If the result is exactly representable as a floating-point number, return
+ that, otherwise return the nearest representable floating-point value
+ with magnitude smaller than the exact answer.
+ """
+
+ ROUND_TOWARDS_ZERO = RTZ
+
+ RTP = 0b10
+ """Round towards +Infinity
+
+ If the result is exactly representable as a floating-point number, return
+ that, otherwise return the nearest representable floating-point value
+ that is numerically greater than the exact answer. This can round up to
+ +Infinity.
+ """
+
+ ROUND_TOWARDS_POSITIVE = RTP
+
+ RTN = 0b11
+ """Round towards -Infinity
+
+ If the result is exactly representable as a floating-point number, return
+ that, otherwise return the nearest representable floating-point value
+ that is numerically less than the exact answer. This can round down to
+ -Infinity.
+ """
+
+ ROUND_TOWARDS_NEGATIVE = RTN
+
+ RNA = 0b100
+ """Round to Nearest Away
+
+ Rounds to the nearest representable floating-point number, ties are
+ rounded to the number with the maximum magnitude. Treats +-Infinity as if
+ it were a normalized floating-point number when deciding which number
+ is closer when rounding. See IEEE754 spec. for details.
+ """
+
+ ROUND_NEAREST_TIES_TO_AWAY = RNA
+
+ RTOP = 0b101
+ """Round to Odd, unsigned zeros are Positive
+
+ Not in smtlib2.
+
+ If the result is exactly representable as a floating-point number, return
+ that, otherwise return the nearest representable floating-point value
+ that has an odd mantissa.
+
+ If the result is zero but with otherwise undetermined sign
+ (e.g. `1.0 - 1.0`), the sign is positive.
+
+ This rounding mode is used for instructions with Round To Odd enabled,
+ and `FPSCR.RN != RTN`.
+
+ This is useful to avoid double-rounding errors when doing arithmetic in a
+ larger type (e.g. f128) but where the answer should be a smaller type
+ (e.g. f80).
+ """
+
+ ROUND_TO_ODD_UNSIGNED_ZEROS_ARE_POSITIVE = RTOP
+
+ RTON = 0b110
+ """Round to Odd, unsigned zeros are Negative
+
+ Not in smtlib2.
+
+ If the result is exactly representable as a floating-point number, return
+ that, otherwise return the nearest representable floating-point value
+ that has an odd mantissa.
+
+ If the result is zero but with otherwise undetermined sign
+ (e.g. `1.0 - 1.0`), the sign is negative.
+
+ This rounding mode is used for instructions with Round To Odd enabled,
+ and `FPSCR.RN == RTN`.
+
+ This is useful to avoid double-rounding errors when doing arithmetic in a
+ larger type (e.g. f128) but where the answer should be a smaller type
+ (e.g. f80).
+ """
+
+ ROUND_TO_ODD_UNSIGNED_ZEROS_ARE_NEGATIVE = RTON
+
+ @staticmethod
+ def make_array(f):
+ l = [None] * len(FPRoundingMode)
+ for rm in FPRoundingMode:
+ l[rm.value] = f(rm)
+ return Array(l)
+
+ def overflow_rounds_to_inf(self, sign):
+ """returns true if an overflow should round to `inf`,
+ false if it should round to `max_normal`
+ """
+ not_sign = ~sign if isinstance(sign, Value) else not sign
+ if self is FPRoundingMode.RNE:
+ return True
+ elif self is FPRoundingMode.RTZ:
+ return False
+ elif self is FPRoundingMode.RTP:
+ return not_sign
+ elif self is FPRoundingMode.RTN:
+ return sign
+ elif self is FPRoundingMode.RNA:
+ return True
+ elif self is FPRoundingMode.RTOP:
+ return False
+ else:
+ assert self is FPRoundingMode.RTON
+ return False
+
+ def underflow_rounds_to_zero(self, sign):
+ """returns true if an underflow should round to `zero`,
+ false if it should round to `min_denormal`
+ """
+ not_sign = ~sign if isinstance(sign, Value) else not sign
+ if self is FPRoundingMode.RNE:
+ return True
+ elif self is FPRoundingMode.RTZ:
+ return True
+ elif self is FPRoundingMode.RTP:
+ return sign
+ elif self is FPRoundingMode.RTN:
+ return not_sign
+ elif self is FPRoundingMode.RNA:
+ return True
+ elif self is FPRoundingMode.RTOP:
+ return False
+ else:
+ assert self is FPRoundingMode.RTON
+ return False
+
+ def zero_sign(self):
+ """which sign an exact zero result should have when it isn't
+ otherwise determined, e.g. for `1.0 - 1.0`.
+ """
+ if self is FPRoundingMode.RNE:
+ return False
+ elif self is FPRoundingMode.RTZ:
+ return False
+ elif self is FPRoundingMode.RTP:
+ return False
+ elif self is FPRoundingMode.RTN:
+ return True
+ elif self is FPRoundingMode.RNA:
+ return False
+ elif self is FPRoundingMode.RTOP:
+ return False
+ else:
+ assert self is FPRoundingMode.RTON
+ return True
+
+ if _HAVE_SMTLIB2:
+ def to_smtlib2(self, default=_raise_err):
+ """return the corresponding smtlib2 rounding mode for `self`. If
+ there is no corresponding smtlib2 rounding mode, then return
+ `default` if specified, else raise `ValueError`.
+ """
+ if self is FPRoundingMode.RNE:
+ return RoundingModeEnum.RNE
+ elif self is FPRoundingMode.RTZ:
+ return RoundingModeEnum.RTZ
+ elif self is FPRoundingMode.RTP:
+ return RoundingModeEnum.RTP
+ elif self is FPRoundingMode.RTN:
+ return RoundingModeEnum.RTN
+ elif self is FPRoundingMode.RNA:
+ return RoundingModeEnum.RNA
+ else:
+ assert self in (FPRoundingMode.RTOP, FPRoundingMode.RTON)
+ if default is _raise_err:
+ raise ValueError(
+ "no corresponding smtlib2 rounding mode", self)
+ return default
+
+
class FPFormat:
""" Check for equality. """
if not isinstance(other, FPFormat):
return NotImplemented
- return (self.e_width == other.e_width and
- self.m_width == other.m_width and
- self.has_int_bit == other.has_int_bit and
- self.has_sign == other.has_sign)
+ return (self.e_width == other.e_width
+ and self.m_width == other.m_width
+ and self.has_int_bit == other.has_int_bit
+ and self.has_sign == other.has_sign)
@staticmethod
def standard(width):
retval += f", {self.has_sign}"
return retval + ")"
- def get_sign(self, x):
- """ returns the sign of its input number, x (assumes number is signed)
+ def get_sign_field(self, x):
+ """ returns the sign bit of its input number, x
+ (assumes FPFormat is set to signed - has_sign=True)
"""
return x >> (self.e_width + self.m_width)
+ def get_exponent_field(self, x):
+ """ returns the raw exponent of its input number, x (no bias subtracted)
+ """
+ x = ((x >> self.m_width) & self.exponent_inf_nan)
+ return x
+
def get_exponent(self, x):
""" returns the exponent of its input number, x
"""
- x = ((x >> self.m_width) & self.exponent_inf_nan)
+ x = self.get_exponent_field(x)
+ if isinstance(x, Value) and not x.shape().signed:
+ # convert x to signed without changing its value,
+ # since exponents can be negative
+ x |= Const(0, signed(1))
return x - self.exponent_bias
- def get_mantissa(self, x):
+ def get_exponent_value(self, x):
+ """ returns the exponent of its input number, x, adjusted for the
+ mathematically correct subnormal exponent.
+ """
+ x = self.get_exponent_field(x)
+ if isinstance(x, Value) and not x.shape().signed:
+ # convert x to signed without changing its value,
+ # since exponents can be negative
+ x |= Const(0, signed(1))
+ return x + (x == self.exponent_denormal_zero) - self.exponent_bias
+
+ def get_mantissa_field(self, x):
""" returns the mantissa of its input number, x
"""
return x & self.mantissa_mask
+ def get_mantissa_value(self, x):
+ """ returns the mantissa of its input number, x, but with the
+ implicit bit, if any, made explicit.
+ """
+ if self.has_int_bit:
+ return self.get_mantissa_field(x)
+ exponent_field = self.get_exponent_field(x)
+ mantissa_field = self.get_mantissa_field(x)
+ implicit_bit = exponent_field != self.exponent_denormal_zero
+ return (implicit_bit << self.fraction_width) | mantissa_field
+
def is_zero(self, x):
- """ returns true if x is subnormal (exp at minimum
+ """ returns true if x is +/- zero
"""
- e_sub = self.exponent_denormal_zero - self.exponent_bias
- print ("e_sub", e_sub)
- return self.get_exponent(x) == e_sub and self.get_mantissa(x) == 0
+ return (self.get_exponent(x) == self.e_sub) & \
+ (self.get_mantissa_field(x) == 0)
def is_subnormal(self, x):
- """ returns true if x is subnormal (exp at minimum
+ """ returns true if x is subnormal (exp at minimum)
"""
- e_sub = self.exponent_denormal_zero - self.exponent_bias
- print ("e_sub", e_sub)
- return self.get_exponent(x) == e_sub and self.get_mantissa(x) != 0
+ return (self.get_exponent(x) == self.e_sub) & \
+ (self.get_mantissa_field(x) != 0)
def is_inf(self, x):
""" returns true if x is infinite
"""
- return (self.get_exponent(x) == self.emax and
- self.get_mantissa(x) == 0)
+ return (self.get_exponent(x) == self.e_max) & \
+ (self.get_mantissa_field(x) == 0)
def is_nan(self, x):
- """ returns true if x is nan
+ """ returns true if x is a nan (quiet or signalling)
+ """
+ return (self.get_exponent(x) == self.e_max) & \
+ (self.get_mantissa_field(x) != 0)
+
+ def is_quiet_nan(self, x):
+ """ returns true if x is a quiet nan
"""
- highbit = 1<<self.m_width
- return (self.get_exponent(x) == self.emax and
- self.get_mantissa(x) != 0 and
- self.get_mantissa(x) & highbit == 0)
+ highbit = 1 << (self.m_width - 1)
+ return (self.get_exponent(x) == self.e_max) & \
+ (self.get_mantissa_field(x) != 0) & \
+ (self.get_mantissa_field(x) & highbit != 0)
- def is_nan_signalling(self, x):
+ def to_quiet_nan(self, x):
+ """ converts `x` to a quiet NaN """
+ highbit = 1 << (self.m_width - 1)
+ return x | highbit | self.exponent_mask
+
+ def quiet_nan(self, sign=0):
+ """ return the default quiet NaN with sign `sign` """
+ return self.to_quiet_nan(self.zero(sign))
+
+ def zero(self, sign=0):
+ """ return zero with sign `sign` """
+ return (sign != 0) << (self.e_width + self.m_width)
+
+ def inf(self, sign=0):
+ """ return infinity with sign `sign` """
+ return self.zero(sign) | self.exponent_mask
+
+ def is_nan_signaling(self, x):
""" returns true if x is a signalling nan
"""
- highbit = 1<<self.m_width
- return (self.get_exponent(x) == self.emax and
- self.get_mantissa(x) & highbit != 0)
+ highbit = 1 << (self.m_width - 1)
+ return (self.get_exponent(x) == self.e_max) & \
+ (self.get_mantissa_field(x) != 0) & \
+ (self.get_mantissa_field(x) & highbit) == 0
@property
def width(self):
@property
def mantissa_mask(self):
- """ Get the value of the exponent field designating infinity/NaN. """
+ """ Get a mantissa mask based on the mantissa width """
return (1 << self.m_width) - 1
+ @property
+ def exponent_mask(self):
+ """ Get an exponent mask """
+ return self.exponent_inf_nan << self.m_width
+
@property
def exponent_inf_nan(self):
""" Get the value of the exponent field designating infinity/NaN. """
return (1 << self.e_width) - 1
@property
- def emax(self):
+ def e_max(self):
""" get the maximum exponent (minus bias)
"""
return self.exponent_inf_nan - self.exponent_bias
@property
+ def e_sub(self):
+ return self.exponent_denormal_zero - self.exponent_bias
+ @property
def exponent_denormal_zero(self):
""" Get the value of the exponent field designating denormal/zero. """
return 0
""" Get the number of mantissa bits that are fraction bits. """
return self.m_width - self.has_int_bit
+ @staticmethod
+ def from_pspec(pspec):
+ width = getattr(pspec, "width", None)
+ assert width is None or isinstance(width, int)
+ fpformat = getattr(pspec, "fpformat", None)
+ if fpformat is None:
+ assert width is not None, \
+ "neither pspec.width nor pspec.fpformat were set"
+ fpformat = FPFormat.standard(width)
+ else:
+ assert isinstance(fpformat, FPFormat)
+ assert width == fpformat.width
+ return fpformat
+
class TestFPFormat(unittest.TestCase):
""" very quick test for FPFormat
self.assertEqual(f64.get_exponent(x), 1)
x = Float64(1.5).bits
- m = f64.get_mantissa(x)
+ m = f64.get_mantissa_field(x)
print (hex(x), hex(m))
self.assertEqual(m, 0x8000000000000)
- s = f64.get_sign(x)
+ s = f64.get_sign_field(x)
print (hex(x), hex(s))
self.assertEqual(s, 0)
x = Float64(-1.5).bits
- s = f64.get_sign(x)
+ s = f64.get_sign_field(x)
print (hex(x), hex(s))
self.assertEqual(s, 1)
self.assertEqual(f32.get_exponent(x), 1)
x = Float32(1.5).bits
- m = f32.get_mantissa(x)
+ m = f32.get_mantissa_field(x)
print (hex(x), hex(m))
self.assertEqual(m, 0x400000)
x = Float32(-1.0).sqrt()
x = x.bits
i = f32.is_nan(x)
- print (hex(x), "nan", f32.get_exponent(x), f32.emax,
- f32.get_mantissa(x), i)
+ print (hex(x), "nan", f32.get_exponent(x), f32.e_max,
+ f32.get_mantissa_field(x), i)
self.assertEqual(i, True)
# Inf test
x = Float32(1e36) * Float32(1e36) * Float32(1e36)
x = x.bits
i = f32.is_inf(x)
- print (hex(x), "inf", f32.get_exponent(x), f32.emax,
- f32.get_mantissa(x), i)
+ print (hex(x), "inf", f32.get_exponent(x), f32.e_max,
+ f32.get_mantissa_field(x), i)
self.assertEqual(i, True)
# subnormal
x = Float32(1e-41)
x = x.bits
i = f32.is_subnormal(x)
- print (hex(x), "sub", f32.get_exponent(x), f32.emax,
- f32.get_mantissa(x), i)
+ print (hex(x), "sub", f32.get_exponent(x), f32.e_max,
+ f32.get_mantissa_field(x), i)
self.assertEqual(i, True)
x = Float32(0.0)
x = x.bits
i = f32.is_subnormal(x)
- print (hex(x), "sub", f32.get_exponent(x), f32.emax,
- f32.get_mantissa(x), i)
+ print (hex(x), "sub", f32.get_exponent(x), f32.e_max,
+ f32.get_mantissa_field(x), i)
self.assertEqual(i, False)
# zero
i = f32.is_zero(x)
- print (hex(x), "zero", f32.get_exponent(x), f32.emax,
- f32.get_mantissa(x), i)
+ print (hex(x), "zero", f32.get_exponent(x), f32.e_max,
+ f32.get_mantissa_field(x), i)
self.assertEqual(i, True)
-class MultiShiftR:
+
+class MultiShiftR(Elaboratable):
def __init__(self, width):
self.width = width
- self.smax = int(log(width) / log(2))
+ self.smax = bits_for(width - 1)
self.i = Signal(width, reset_less=True)
self.s = Signal(self.smax, reset_less=True)
self.o = Signal(width, reset_less=True)
def __init__(self, width):
self.width = width
- self.smax = int(log(width) / log(2))
+ self.smax = bits_for(width - 1)
def lshift(self, op, s):
res = op << s
class FPNumBaseRecord:
- """ Floating-point Base Number Class
+ """ Floating-point Base Number Class.
+
+ This class is designed to be passed around in other data structures
+ (between pipelines and between stages). Its "friend" is FPNumBase,
+ which is a *module*. The reason for the discernment is because
+ nmigen modules that are not added to submodules results in the
+ irritating "Elaboration" warning. Despite not *needing* FPNumBase
+ in many cases to be added as a submodule (because it is just data)
+ this was not possible to solve without splitting out the data from
+ the module.
"""
- def __init__(self, width, m_extra=True, e_extra=False):
+ def __init__(self, width=None, m_extra=True, e_extra=False, name=None,
+ fpformat=None):
+ if name is None:
+ name = ""
+ # assert false, "missing name"
+ else:
+ name += "_"
+ if fpformat is None:
+ assert isinstance(width, int)
+ fpformat = FPFormat.standard(width)
+ else:
+ assert isinstance(fpformat, FPFormat)
+ if width is None:
+ width = fpformat.width
+ assert isinstance(width, int)
+ assert width == fpformat.width
self.width = width
- m_width = {16: 11, 32: 24, 64: 53}[width] # 1 extra bit (overflow)
- e_width = {16: 7, 32: 10, 64: 13}[width] # 2 extra bits (overflow)
+ self.fpformat = fpformat
+ assert not fpformat.has_int_bit
+ assert fpformat.has_sign
+ m_width = fpformat.m_width + 1 # 1 extra bit (overflow)
+ e_width = fpformat.e_width + 2 # 2 extra bits (overflow)
e_max = 1 << (e_width-3)
self.rmw = m_width - 1 # real mantissa width (not including extras)
self.e_max = e_max
self.e_start = self.rmw
self.e_end = self.rmw + self.e_width - 2 # for decoding
- self.v = Signal(width, reset_less=True) # Latched copy of value
- self.m = Signal(m_width, reset_less=True) # Mantissa
- self.e = Signal((e_width, True), reset_less=True) # exp+2 bits, signed
- self.s = Signal(reset_less=True) # Sign bit
+ self.v = Signal(width, reset_less=True,
+ name=name+"v") # Latched copy of value
+ self.m = Signal(m_width, reset_less=True, name=name+"m") # Mantissa
+ self.e = Signal(signed(e_width),
+ reset_less=True, name=name+"e") # exp+2 bits, signed
+ self.s = Signal(reset_less=True, name=name+"s") # Sign bit
self.fp = self
self.drop_in(self)
e_max = self.e_max
e_width = self.e_width
- self.mzero = Const(0, (m_width, False))
+ self.mzero = Const(0, unsigned(m_width))
m_msb = 1 << (self.m_width-2)
- self.msb1 = Const(m_msb, (m_width, False))
- self.m1s = Const(-1, (m_width, False))
- self.P128 = Const(e_max, (e_width, True))
- self.P127 = Const(e_max-1, (e_width, True))
- self.N127 = Const(-(e_max-1), (e_width, True))
- self.N126 = Const(-(e_max-2), (e_width, True))
+ self.msb1 = Const(m_msb, unsigned(m_width))
+ self.m1s = Const(-1, unsigned(m_width))
+ self.P128 = Const(e_max, signed(e_width))
+ self.P127 = Const(e_max-1, signed(e_width))
+ self.N127 = Const(-(e_max-1), signed(e_width))
+ self.N126 = Const(-(e_max-2), signed(e_width))
def create(self, s, e, m):
""" creates a value from sign / exponent / mantissa
def nan(self, s):
return self.create(*self._nan(s))
+ def quieted_nan(self, other):
+ assert isinstance(other, FPNumBaseRecord)
+ assert self.width == other.width
+ return self.create(other.s, self.fp.P128,
+ other.v[0:self.e_start] | (1 << (self.e_start - 1)))
+
def inf(self, s):
return self.create(*self._inf(s))
+ def max_normal(self, s):
+ return self.create(s, self.fp.P127, ~0)
+
+ def min_denormal(self, s):
+ return self.create(s, self.fp.N127, 1)
+
def zero(self, s):
return self.create(*self._zero(s))
self.is_overflowed = Signal(reset_less=True)
self.is_denormalised = Signal(reset_less=True)
self.exp_128 = Signal(reset_less=True)
- self.exp_sub_n126 = Signal((e_width, True), reset_less=True)
+ self.exp_sub_n126 = Signal(signed(e_width), reset_less=True)
self.exp_lt_n126 = Signal(reset_less=True)
self.exp_zero = Signal(reset_less=True)
self.exp_gt_n126 = Signal(reset_less=True)
return self.exp_gt127
def _is_denormalised(self):
+ # XXX NOT to be used for "official" quiet NaN tests!
+ # particularly when the MSB has been extended
return (self.exp_n126) & (self.m_msbzero)
def __init__(self, width, s_max=None):
if s_max is None:
- s_max = int(log(width) / log(2))
- self.smax = s_max
+ s_max = bits_for(width - 1)
+ self.smax = Shape.cast(s_max)
self.m = Signal(width, reset_less=True)
self.inp = Signal(width, reset_less=True)
self.diff = Signal(s_max, reset_less=True)
m_mask = Signal(self.width, reset_less=True)
smask = Signal(self.width, reset_less=True)
stickybit = Signal(reset_less=True)
- maxslen = Signal(self.smax, reset_less=True)
- maxsleni = Signal(self.smax, reset_less=True)
+ # XXX GRR frickin nuisance https://github.com/nmigen/nmigen/issues/302
+ maxslen = Signal(self.smax.width, reset_less=True)
+ maxsleni = Signal(self.smax.width, reset_less=True)
sm = MultiShift(self.width-1)
m0s = Const(0, self.width-1)
]
-class Overflow: # (Elaboratable):
+class Overflow:
+ # TODO: change FFLAGS to be FPSCR's status flags
+ FFLAGS_NV = Const(1<<4, 5) # invalid operation
+ FFLAGS_DZ = Const(1<<3, 5) # divide by zero
+ FFLAGS_OF = Const(1<<2, 5) # overflow
+ FFLAGS_UF = Const(1<<1, 5) # underflow
+ FFLAGS_NX = Const(1<<0, 5) # inexact
def __init__(self, name=None):
if name is None:
name = ""
self.round_bit = Signal(reset_less=True, name=name+"round") # tot[1]
self.sticky = Signal(reset_less=True, name=name+"sticky") # tot[0]
self.m0 = Signal(reset_less=True, name=name+"m0") # mantissa bit 0
+ self.fpflags = Signal(5, reset_less=True, name=name+"fflags")
+
+ self.sign = Signal(reset_less=True, name=name+"sign")
+ """sign bit -- 1 means negative, 0 means positive"""
+
+ self.rm = Signal(FPRoundingMode, name=name+"rm",
+ reset=FPRoundingMode.DEFAULT)
+ """rounding mode"""
#self.roundz = Signal(reset_less=True)
yield self.round_bit
yield self.sticky
yield self.m0
+ yield self.fpflags
+ yield self.sign
+ yield self.rm
def eq(self, inp):
return [self.guard.eq(inp.guard),
self.round_bit.eq(inp.round_bit),
self.sticky.eq(inp.sticky),
- self.m0.eq(inp.m0)]
+ self.m0.eq(inp.m0),
+ self.fpflags.eq(inp.fpflags),
+ self.sign.eq(inp.sign),
+ self.rm.eq(inp.rm)]
@property
- def roundz(self):
+ def roundz_rne(self):
+ """true if the mantissa should be rounded up for `rm == RNE`
+
+ assumes the rounding mode is `ROUND_NEAREST_TIES_TO_EVEN`
+ """
return self.guard & (self.round_bit | self.sticky | self.m0)
+ @property
+ def roundz_rna(self):
+ """true if the mantissa should be rounded up for `rm == RNA`
+
+ assumes the rounding mode is `ROUND_NEAREST_TIES_TO_AWAY`
+ """
+ return self.guard
+
+ @property
+ def roundz_rtn(self):
+ """true if the mantissa should be rounded up for `rm == RTN`
+
+ assumes the rounding mode is `ROUND_TOWARDS_NEGATIVE`
+ """
+ return self.sign & (self.guard | self.round_bit | self.sticky)
+
+ @property
+ def roundz_rto(self):
+ """true if the mantissa should be rounded up for `rm in (RTOP, RTON)`
+
+ assumes the rounding mode is `ROUND_TO_ODD_UNSIGNED_ZEROS_ARE_POSITIVE`
+ or `ROUND_TO_ODD_UNSIGNED_ZEROS_ARE_NEGATIVE`
+ """
+ return ~self.m0 & (self.guard | self.round_bit | self.sticky)
+
+ @property
+ def roundz_rtp(self):
+ """true if the mantissa should be rounded up for `rm == RTP`
+
+ assumes the rounding mode is `ROUND_TOWARDS_POSITIVE`
+ """
+ return ~self.sign & (self.guard | self.round_bit | self.sticky)
+
+ @property
+ def roundz_rtz(self):
+ """true if the mantissa should be rounded up for `rm == RTZ`
+
+ assumes the rounding mode is `ROUND_TOWARDS_ZERO`
+ """
+ return False
+
+ @property
+ def roundz(self):
+ """true if the mantissa should be rounded up for the current rounding
+ mode `self.rm`
+ """
+ d = {
+ FPRoundingMode.RNA: self.roundz_rna,
+ FPRoundingMode.RNE: self.roundz_rne,
+ FPRoundingMode.RTN: self.roundz_rtn,
+ FPRoundingMode.RTOP: self.roundz_rto,
+ FPRoundingMode.RTON: self.roundz_rto,
+ FPRoundingMode.RTP: self.roundz_rtp,
+ FPRoundingMode.RTZ: self.roundz_rtz,
+ }
+ return FPRoundingMode.make_array(lambda rm: d[rm])[self.rm]
+
class OverflowMod(Elaboratable, Overflow):
def __init__(self, name=None):
def elaborate(self, platform):
m = Module()
- m.d.comb += self.roundz_out.eq(self.roundz)
+ m.d.comb += self.roundz_out.eq(self.roundz) # roundz is a property
return m