From: Luke Kenneth Casson Leighton Date: Sat, 16 Feb 2019 10:51:06 +0000 (+0000) Subject: split out base classes into separate fpbase module X-Git-Tag: ls180-24jan2020~1950 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=91fa69f581e94e0b62f1fb69a158f824ee06299e;p=ieee754fpu.git split out base classes into separate fpbase module --- diff --git a/src/add/fpbase.py b/src/add/fpbase.py new file mode 100644 index 00000000..6ffb0165 --- /dev/null +++ b/src/add/fpbase.py @@ -0,0 +1,239 @@ +# 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 + + diff --git a/src/add/nmigen_add_experiment.py b/src/add/nmigen_add_experiment.py index 9cacf325..ddd7ad07 100644 --- a/src/add/nmigen_add_experiment.py +++ b/src/add/nmigen_add_experiment.py @@ -2,240 +2,10 @@ # 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):