--- /dev/null
+# IEEE Floating Point Adder (Single Precision)
+# Copyright (C) Jonathan P Dawson 2013
+# 2013-12-12
+
+from nmigen import Signal, Cat, Const
+
+
+class FPNum:
+ """ Floating-point Number Class, variable-width TODO (currently 32-bit)
+
+ Contains signals for an incoming copy of the value, decoded into
+ sign / exponent / mantissa.
+ Also contains encoding functions, creation and recognition of
+ zero, NaN and inf (all signed)
+
+ Four extra bits are included in the mantissa: the top bit
+ (m[-1]) is effectively a carry-overflow. The other three are
+ guard (m[2]), round (m[1]), and sticky (m[0])
+ """
+ def __init__(self, width, m_width=None):
+ self.width = width
+ if m_width is None:
+ m_width = width - 5 # mantissa extra bits (top,guard,round)
+ self.m_width = m_width
+ self.v = Signal(width) # Latched copy of value
+ self.m = Signal(m_width) # Mantissa
+ self.e = Signal((10, True)) # Exponent: 10 bits, signed
+ self.s = Signal() # Sign bit
+
+ self.mzero = Const(0, (m_width, False))
+ self.m1s = Const(-1, (m_width, False))
+ self.P128 = Const(128, (10, True))
+ self.P127 = Const(127, (10, True))
+ self.N127 = Const(-127, (10, True))
+ self.N126 = Const(-126, (10, True))
+
+ def decode(self, v):
+ """ decodes a latched value into sign / exponent / mantissa
+
+ bias is subtracted here, from the exponent. exponent
+ is extended to 10 bits so that subtract 127 is done on
+ a 10-bit number
+ """
+ args = [0] * (self.m_width-24) + [v[0:23]] # pad with extra zeros
+ return [self.m.eq(Cat(*args)), # mantissa
+ self.e.eq(v[23:31] - self.P127), # exp (minus bias)
+ self.s.eq(v[31]), # sign
+ ]
+
+ def create(self, s, e, m):
+ """ creates a value from sign / exponent / mantissa
+
+ bias is added here, to the exponent
+ """
+ return [
+ self.v[31].eq(s), # sign
+ self.v[23:31].eq(e + self.P127), # exp (add on bias)
+ self.v[0:23].eq(m) # mantissa
+ ]
+
+ def shift_down(self):
+ """ shifts a mantissa down by one. exponent is increased to compensate
+
+ accuracy is lost as a result in the mantissa however there are 3
+ guard bits (the latter of which is the "sticky" bit)
+ """
+ return [self.e.eq(self.e + 1),
+ self.m.eq(Cat(self.m[0] | self.m[1], self.m[2:], 0))
+ ]
+
+ def nan(self, s):
+ return self.create(s, self.P128, 1<<22)
+
+ def inf(self, s):
+ return self.create(s, self.P128, 0)
+
+ def zero(self, s):
+ return self.create(s, self.N127, 0)
+
+ def is_nan(self):
+ return (self.e == self.P128) & (self.m != 0)
+
+ def is_inf(self):
+ return (self.e == self.P128) & (self.m == 0)
+
+ def is_zero(self):
+ return (self.e == self.N127) & (self.m == self.mzero)
+
+ def is_overflowed(self):
+ return (self.e > self.P127)
+
+ def is_denormalised(self):
+ return (self.e == self.N126) & (self.m[23] == 0)
+
+
+class FPOp:
+ def __init__(self, width):
+ self.width = width
+
+ self.v = Signal(width)
+ self.stb = Signal()
+ self.ack = Signal()
+
+ def ports(self):
+ return [self.v, self.stb, self.ack]
+
+
+class Overflow:
+ def __init__(self):
+ self.guard = Signal() # tot[2]
+ self.round_bit = Signal() # tot[1]
+ self.sticky = Signal() # tot[0]
+
+
+class FPBase:
+ """ IEEE754 Floating Point Base Class
+
+ contains common functions for FP manipulation, such as
+ extracting and packing operands, normalisation, denormalisation,
+ rounding etc.
+ """
+
+ def get_op(self, m, op, v, next_state):
+ """ this function moves to the next state and copies the operand
+ when both stb and ack are 1.
+ acknowledgement is sent by setting ack to ZERO.
+ """
+ with m.If((op.ack) & (op.stb)):
+ m.next = next_state
+ m.d.sync += [
+ v.decode(op.v),
+ op.ack.eq(0)
+ ]
+ with m.Else():
+ m.d.sync += op.ack.eq(1)
+
+ def denormalise(self, m, a):
+ """ denormalises a number
+ """
+ with m.If(a.e == a.N127):
+ m.d.sync += a.e.eq(-126) # limit a exponent
+ with m.Else():
+ m.d.sync += a.m[-1].eq(1) # set top mantissa bit
+
+ def op_normalise(self, m, op, of, next_state):
+ """ operand normalisation
+ NOTE: just like "align", this one keeps going round every clock
+ until the result's exponent is within acceptable "range"
+ """
+ with m.If((op.m[-1] == 0)): # check last bit of mantissa
+ m.d.sync +=[
+ op.e.eq(op.e - 1), # DECREASE exponent
+ op.m.eq(op.m << 1), # shift mantissa UP
+ ]
+ with m.Else():
+ m.next = next_state
+
+ def normalise_1(self, m, z, of, next_state):
+ """ first stage normalisation
+
+ NOTE: just like "align", this one keeps going round every clock
+ until the result's exponent is within acceptable "range"
+ NOTE: the weirdness of reassigning guard and round is due to
+ the extra mantissa bits coming from tot[0..2]
+ """
+ with m.If((z.m[-1] == 0) & (z.e > z.N126)):
+ m.d.sync +=[
+ z.e.eq(z.e - 1), # DECREASE exponent
+ z.m.eq(z.m << 1), # shift mantissa UP
+ z.m[0].eq(of.guard), # steal guard bit (was tot[2])
+ of.guard.eq(of.round_bit), # steal round_bit (was tot[1])
+ of.round_bit.eq(0), # reset round bit
+ ]
+ with m.Else():
+ m.next = next_state
+
+ def normalise_2(self, m, z, of, next_state):
+ """ second stage normalisation
+
+ NOTE: just like "align", this one keeps going round every clock
+ until the result's exponent is within acceptable "range"
+ NOTE: the weirdness of reassigning guard and round is due to
+ the extra mantissa bits coming from tot[0..2]
+ """
+ with m.If(z.e < z.N126):
+ m.d.sync +=[
+ z.e.eq(z.e + 1), # INCREASE exponent
+ z.m.eq(z.m >> 1), # shift mantissa DOWN
+ of.guard.eq(z.m[0]),
+ of.round_bit.eq(of.guard),
+ of.sticky.eq(of.sticky | of.round_bit)
+ ]
+ with m.Else():
+ m.next = next_state
+
+ def roundz(self, m, z, of, next_state):
+ """ performs rounding on the output. TODO: different kinds of rounding
+ """
+ m.next = next_state
+ with m.If(of.guard & (of.round_bit | of.sticky | z.m[0])):
+ m.d.sync += z.m.eq(z.m + 1) # mantissa rounds up
+ with m.If(z.m == z.m1s): # all 1s
+ m.d.sync += z.e.eq(z.e + 1) # exponent rounds up
+
+ def corrections(self, m, z, next_state):
+ """ denormalisation and sign-bug corrections
+ """
+ m.next = next_state
+ # denormalised, correct exponent to zero
+ with m.If(z.is_denormalised()):
+ m.d.sync += z.m.eq(-127)
+ # FIX SIGN BUG: -a + a = +0.
+ with m.If((z.e == z.N126) & (z.m[0:] == 0)):
+ m.d.sync += z.s.eq(0)
+
+ def pack(self, m, z, next_state):
+ """ packs the result into the output (detects overflow->Inf)
+ """
+ m.next = next_state
+ # if overflow occurs, return inf
+ with m.If(z.is_overflowed()):
+ m.d.sync += z.inf(0)
+ with m.Else():
+ m.d.sync += z.create(z.s, z.e, z.m)
+
+ def put_z(self, m, z, out_z, next_state):
+ """ put_z: stores the result in the output. raises stb and waits
+ for ack to be set to 1 before moving to the next state.
+ resets stb back to zero when that occurs, as acknowledgement.
+ """
+ m.d.sync += [
+ out_z.stb.eq(1),
+ out_z.v.eq(z.v)
+ ]
+ with m.If(out_z.stb & out_z.ack):
+ m.d.sync += out_z.stb.eq(0)
+ m.next = next_state
+
+
# Copyright (C) Jonathan P Dawson 2013
# 2013-12-12
-from nmigen import Module, Signal, Cat, Const
+from nmigen import Module, Signal
from nmigen.cli import main, verilog
-
-class FPNum:
- """ Floating-point Number Class, variable-width TODO (currently 32-bit)
-
- Contains signals for an incoming copy of the value, decoded into
- sign / exponent / mantissa.
- Also contains encoding functions, creation and recognition of
- zero, NaN and inf (all signed)
-
- Four extra bits are included in the mantissa: the top bit
- (m[-1]) is effectively a carry-overflow. The other three are
- guard (m[2]), round (m[1]), and sticky (m[0])
- """
- def __init__(self, width, m_width=None):
- self.width = width
- if m_width is None:
- m_width = width - 5 # mantissa extra bits (top,guard,round)
- self.m_width = m_width
- self.v = Signal(width) # Latched copy of value
- self.m = Signal(m_width) # Mantissa
- self.e = Signal((10, True)) # Exponent: 10 bits, signed
- self.s = Signal() # Sign bit
-
- self.mzero = Const(0, (m_width, False))
- self.m1s = Const(-1, (m_width, False))
- self.P128 = Const(128, (10, True))
- self.P127 = Const(127, (10, True))
- self.N127 = Const(-127, (10, True))
- self.N126 = Const(-126, (10, True))
-
- def decode(self, v):
- """ decodes a latched value into sign / exponent / mantissa
-
- bias is subtracted here, from the exponent. exponent
- is extended to 10 bits so that subtract 127 is done on
- a 10-bit number
- """
- args = [0] * (self.m_width-24) + [v[0:23]] # pad with extra zeros
- return [self.m.eq(Cat(*args)), # mantissa
- self.e.eq(v[23:31] - self.P127), # exp (minus bias)
- self.s.eq(v[31]), # sign
- ]
-
- def create(self, s, e, m):
- """ creates a value from sign / exponent / mantissa
-
- bias is added here, to the exponent
- """
- return [
- self.v[31].eq(s), # sign
- self.v[23:31].eq(e + self.P127), # exp (add on bias)
- self.v[0:23].eq(m) # mantissa
- ]
-
- def shift_down(self):
- """ shifts a mantissa down by one. exponent is increased to compensate
-
- accuracy is lost as a result in the mantissa however there are 3
- guard bits (the latter of which is the "sticky" bit)
- """
- return [self.e.eq(self.e + 1),
- self.m.eq(Cat(self.m[0] | self.m[1], self.m[2:], 0))
- ]
-
- def nan(self, s):
- return self.create(s, self.P128, 1<<22)
-
- def inf(self, s):
- return self.create(s, self.P128, 0)
-
- def zero(self, s):
- return self.create(s, self.N127, 0)
-
- def is_nan(self):
- return (self.e == self.P128) & (self.m != 0)
-
- def is_inf(self):
- return (self.e == self.P128) & (self.m == 0)
-
- def is_zero(self):
- return (self.e == self.N127) & (self.m == self.mzero)
-
- def is_overflowed(self):
- return (self.e > self.P127)
-
- def is_denormalised(self):
- return (self.e == self.N126) & (self.m[23] == 0)
-
-
-class FPOp:
- def __init__(self, width):
- self.width = width
-
- self.v = Signal(width)
- self.stb = Signal()
- self.ack = Signal()
-
- def ports(self):
- return [self.v, self.stb, self.ack]
-
-
-class Overflow:
- def __init__(self):
- self.guard = Signal() # tot[2]
- self.round_bit = Signal() # tot[1]
- self.sticky = Signal() # tot[0]
-
-
-class FPBase:
- """ IEEE754 Floating Point Base Class
-
- contains common functions for FP manipulation, such as
- extracting and packing operands, normalisation, denormalisation,
- rounding etc.
- """
-
- def get_op(self, m, op, v, next_state):
- """ this function moves to the next state and copies the operand
- when both stb and ack are 1.
- acknowledgement is sent by setting ack to ZERO.
- """
- with m.If((op.ack) & (op.stb)):
- m.next = next_state
- m.d.sync += [
- v.decode(op.v),
- op.ack.eq(0)
- ]
- with m.Else():
- m.d.sync += op.ack.eq(1)
-
- def denormalise(self, m, a):
- """ denormalises a number
- """
- with m.If(a.e == a.N127):
- m.d.sync += a.e.eq(-126) # limit a exponent
- with m.Else():
- m.d.sync += a.m[-1].eq(1) # set top mantissa bit
-
- def op_normalise(self, m, op, of, next_state):
- """ operand normalisation
- NOTE: just like "align", this one keeps going round every clock
- until the result's exponent is within acceptable "range"
- """
- with m.If((op.m[-1] == 0)): # check last bit of mantissa
- m.d.sync +=[
- op.e.eq(op.e - 1), # DECREASE exponent
- op.m.eq(op.m << 1), # shift mantissa UP
- ]
- with m.Else():
- m.next = next_state
-
- def normalise_1(self, m, z, of, next_state):
- """ first stage normalisation
-
- NOTE: just like "align", this one keeps going round every clock
- until the result's exponent is within acceptable "range"
- NOTE: the weirdness of reassigning guard and round is due to
- the extra mantissa bits coming from tot[0..2]
- """
- with m.If((z.m[-1] == 0) & (z.e > z.N126)):
- m.d.sync +=[
- z.e.eq(z.e - 1), # DECREASE exponent
- z.m.eq(z.m << 1), # shift mantissa UP
- z.m[0].eq(of.guard), # steal guard bit (was tot[2])
- of.guard.eq(of.round_bit), # steal round_bit (was tot[1])
- of.round_bit.eq(0), # reset round bit
- ]
- with m.Else():
- m.next = next_state
-
- def normalise_2(self, m, z, of, next_state):
- """ second stage normalisation
-
- NOTE: just like "align", this one keeps going round every clock
- until the result's exponent is within acceptable "range"
- NOTE: the weirdness of reassigning guard and round is due to
- the extra mantissa bits coming from tot[0..2]
- """
- with m.If(z.e < z.N126):
- m.d.sync +=[
- z.e.eq(z.e + 1), # INCREASE exponent
- z.m.eq(z.m >> 1), # shift mantissa DOWN
- of.guard.eq(z.m[0]),
- of.round_bit.eq(of.guard),
- of.sticky.eq(of.sticky | of.round_bit)
- ]
- with m.Else():
- m.next = next_state
-
- def roundz(self, m, z, of, next_state):
- """ performs rounding on the output. TODO: different kinds of rounding
- """
- m.next = next_state
- with m.If(of.guard & (of.round_bit | of.sticky | z.m[0])):
- m.d.sync += z.m.eq(z.m + 1) # mantissa rounds up
- with m.If(z.m == z.m1s): # all 1s
- m.d.sync += z.e.eq(z.e + 1) # exponent rounds up
-
- def corrections(self, m, z, next_state):
- """ denormalisation and sign-bug corrections
- """
- m.next = next_state
- # denormalised, correct exponent to zero
- with m.If(z.is_denormalised()):
- m.d.sync += z.m.eq(-127)
- # FIX SIGN BUG: -a + a = +0.
- with m.If((z.e == z.N126) & (z.m[0:] == 0)):
- m.d.sync += z.s.eq(0)
-
- def pack(self, m, z, next_state):
- """ packs the result into the output (detects overflow->Inf)
- """
- m.next = next_state
- # if overflow occurs, return inf
- with m.If(z.is_overflowed()):
- m.d.sync += z.inf(0)
- with m.Else():
- m.d.sync += z.create(z.s, z.e, z.m)
-
- def put_z(self, m, z, out_z, next_state):
- """ put_z: stores the result in the output. raises stb and waits
- for ack to be set to 1 before moving to the next state.
- resets stb back to zero when that occurs, as acknowledgement.
- """
- m.d.sync += [
- out_z.stb.eq(1),
- out_z.v.eq(z.v)
- ]
- with m.If(out_z.stb & out_z.ack):
- m.d.sync += out_z.stb.eq(0)
- m.next = next_state
+from fpbase import FPNum, FPOp, Overflow, FPBase
class FPADD(FPBase):