split out base classes into separate fpbase module
[ieee754fpu.git] / src / add / fpbase.py
1 # IEEE Floating Point Adder (Single Precision)
2 # Copyright (C) Jonathan P Dawson 2013
3 # 2013-12-12
4
5 from nmigen import Signal, Cat, Const
6
7
8 class FPNum:
9 """ Floating-point Number Class, variable-width TODO (currently 32-bit)
10
11 Contains signals for an incoming copy of the value, decoded into
12 sign / exponent / mantissa.
13 Also contains encoding functions, creation and recognition of
14 zero, NaN and inf (all signed)
15
16 Four extra bits are included in the mantissa: the top bit
17 (m[-1]) is effectively a carry-overflow. The other three are
18 guard (m[2]), round (m[1]), and sticky (m[0])
19 """
20 def __init__(self, width, m_width=None):
21 self.width = width
22 if m_width is None:
23 m_width = width - 5 # mantissa extra bits (top,guard,round)
24 self.m_width = m_width
25 self.v = Signal(width) # Latched copy of value
26 self.m = Signal(m_width) # Mantissa
27 self.e = Signal((10, True)) # Exponent: 10 bits, signed
28 self.s = Signal() # Sign bit
29
30 self.mzero = Const(0, (m_width, False))
31 self.m1s = Const(-1, (m_width, False))
32 self.P128 = Const(128, (10, True))
33 self.P127 = Const(127, (10, True))
34 self.N127 = Const(-127, (10, True))
35 self.N126 = Const(-126, (10, True))
36
37 def decode(self, v):
38 """ decodes a latched value into sign / exponent / mantissa
39
40 bias is subtracted here, from the exponent. exponent
41 is extended to 10 bits so that subtract 127 is done on
42 a 10-bit number
43 """
44 args = [0] * (self.m_width-24) + [v[0:23]] # pad with extra zeros
45 return [self.m.eq(Cat(*args)), # mantissa
46 self.e.eq(v[23:31] - self.P127), # exp (minus bias)
47 self.s.eq(v[31]), # sign
48 ]
49
50 def create(self, s, e, m):
51 """ creates a value from sign / exponent / mantissa
52
53 bias is added here, to the exponent
54 """
55 return [
56 self.v[31].eq(s), # sign
57 self.v[23:31].eq(e + self.P127), # exp (add on bias)
58 self.v[0:23].eq(m) # mantissa
59 ]
60
61 def shift_down(self):
62 """ shifts a mantissa down by one. exponent is increased to compensate
63
64 accuracy is lost as a result in the mantissa however there are 3
65 guard bits (the latter of which is the "sticky" bit)
66 """
67 return [self.e.eq(self.e + 1),
68 self.m.eq(Cat(self.m[0] | self.m[1], self.m[2:], 0))
69 ]
70
71 def nan(self, s):
72 return self.create(s, self.P128, 1<<22)
73
74 def inf(self, s):
75 return self.create(s, self.P128, 0)
76
77 def zero(self, s):
78 return self.create(s, self.N127, 0)
79
80 def is_nan(self):
81 return (self.e == self.P128) & (self.m != 0)
82
83 def is_inf(self):
84 return (self.e == self.P128) & (self.m == 0)
85
86 def is_zero(self):
87 return (self.e == self.N127) & (self.m == self.mzero)
88
89 def is_overflowed(self):
90 return (self.e > self.P127)
91
92 def is_denormalised(self):
93 return (self.e == self.N126) & (self.m[23] == 0)
94
95
96 class FPOp:
97 def __init__(self, width):
98 self.width = width
99
100 self.v = Signal(width)
101 self.stb = Signal()
102 self.ack = Signal()
103
104 def ports(self):
105 return [self.v, self.stb, self.ack]
106
107
108 class Overflow:
109 def __init__(self):
110 self.guard = Signal() # tot[2]
111 self.round_bit = Signal() # tot[1]
112 self.sticky = Signal() # tot[0]
113
114
115 class FPBase:
116 """ IEEE754 Floating Point Base Class
117
118 contains common functions for FP manipulation, such as
119 extracting and packing operands, normalisation, denormalisation,
120 rounding etc.
121 """
122
123 def get_op(self, m, op, v, next_state):
124 """ this function moves to the next state and copies the operand
125 when both stb and ack are 1.
126 acknowledgement is sent by setting ack to ZERO.
127 """
128 with m.If((op.ack) & (op.stb)):
129 m.next = next_state
130 m.d.sync += [
131 v.decode(op.v),
132 op.ack.eq(0)
133 ]
134 with m.Else():
135 m.d.sync += op.ack.eq(1)
136
137 def denormalise(self, m, a):
138 """ denormalises a number
139 """
140 with m.If(a.e == a.N127):
141 m.d.sync += a.e.eq(-126) # limit a exponent
142 with m.Else():
143 m.d.sync += a.m[-1].eq(1) # set top mantissa bit
144
145 def op_normalise(self, m, op, of, next_state):
146 """ operand normalisation
147 NOTE: just like "align", this one keeps going round every clock
148 until the result's exponent is within acceptable "range"
149 """
150 with m.If((op.m[-1] == 0)): # check last bit of mantissa
151 m.d.sync +=[
152 op.e.eq(op.e - 1), # DECREASE exponent
153 op.m.eq(op.m << 1), # shift mantissa UP
154 ]
155 with m.Else():
156 m.next = next_state
157
158 def normalise_1(self, m, z, of, next_state):
159 """ first stage normalisation
160
161 NOTE: just like "align", this one keeps going round every clock
162 until the result's exponent is within acceptable "range"
163 NOTE: the weirdness of reassigning guard and round is due to
164 the extra mantissa bits coming from tot[0..2]
165 """
166 with m.If((z.m[-1] == 0) & (z.e > z.N126)):
167 m.d.sync +=[
168 z.e.eq(z.e - 1), # DECREASE exponent
169 z.m.eq(z.m << 1), # shift mantissa UP
170 z.m[0].eq(of.guard), # steal guard bit (was tot[2])
171 of.guard.eq(of.round_bit), # steal round_bit (was tot[1])
172 of.round_bit.eq(0), # reset round bit
173 ]
174 with m.Else():
175 m.next = next_state
176
177 def normalise_2(self, m, z, of, next_state):
178 """ second stage normalisation
179
180 NOTE: just like "align", this one keeps going round every clock
181 until the result's exponent is within acceptable "range"
182 NOTE: the weirdness of reassigning guard and round is due to
183 the extra mantissa bits coming from tot[0..2]
184 """
185 with m.If(z.e < z.N126):
186 m.d.sync +=[
187 z.e.eq(z.e + 1), # INCREASE exponent
188 z.m.eq(z.m >> 1), # shift mantissa DOWN
189 of.guard.eq(z.m[0]),
190 of.round_bit.eq(of.guard),
191 of.sticky.eq(of.sticky | of.round_bit)
192 ]
193 with m.Else():
194 m.next = next_state
195
196 def roundz(self, m, z, of, next_state):
197 """ performs rounding on the output. TODO: different kinds of rounding
198 """
199 m.next = next_state
200 with m.If(of.guard & (of.round_bit | of.sticky | z.m[0])):
201 m.d.sync += z.m.eq(z.m + 1) # mantissa rounds up
202 with m.If(z.m == z.m1s): # all 1s
203 m.d.sync += z.e.eq(z.e + 1) # exponent rounds up
204
205 def corrections(self, m, z, next_state):
206 """ denormalisation and sign-bug corrections
207 """
208 m.next = next_state
209 # denormalised, correct exponent to zero
210 with m.If(z.is_denormalised()):
211 m.d.sync += z.m.eq(-127)
212 # FIX SIGN BUG: -a + a = +0.
213 with m.If((z.e == z.N126) & (z.m[0:] == 0)):
214 m.d.sync += z.s.eq(0)
215
216 def pack(self, m, z, next_state):
217 """ packs the result into the output (detects overflow->Inf)
218 """
219 m.next = next_state
220 # if overflow occurs, return inf
221 with m.If(z.is_overflowed()):
222 m.d.sync += z.inf(0)
223 with m.Else():
224 m.d.sync += z.create(z.s, z.e, z.m)
225
226 def put_z(self, m, z, out_z, next_state):
227 """ put_z: stores the result in the output. raises stb and waits
228 for ack to be set to 1 before moving to the next state.
229 resets stb back to zero when that occurs, as acknowledgement.
230 """
231 m.d.sync += [
232 out_z.stb.eq(1),
233 out_z.v.eq(z.v)
234 ]
235 with m.If(out_z.stb & out_z.ack):
236 m.d.sync += out_z.stb.eq(0)
237 m.next = next_state
238
239