update comments
[ieee754fpu.git] / src / add / nmigen_div_experiment.py
1 # IEEE Floating Point Divider (Single Precision)
2 # Copyright (C) Jonathan P Dawson 2013
3 # 2013-12-12
4
5 from nmigen import Module, Signal, Const, Cat
6 from nmigen.cli import main, verilog
7
8 from fpbase import FPNumIn, FPNumOut, FPOpIn, FPOpOut, Overflow, FPBase, FPState
9 from singlepipe import eq
10
11 class Div:
12 def __init__(self, width):
13 self.width = width
14 self.quot = Signal(width) # quotient
15 self.dor = Signal(width) # divisor
16 self.dend = Signal(width) # dividend
17 self.rem = Signal(width) # remainder
18 self.count = Signal(7) # loop count
19
20 self.czero = Const(0, width)
21
22 def reset(self, m):
23 m.d.sync += [
24 self.quot.eq(self.czero),
25 self.rem.eq(self.czero),
26 self.count.eq(Const(0, 7))
27 ]
28
29
30 class FPDIV(FPBase):
31
32 def __init__(self, width):
33 FPBase.__init__(self)
34 self.width = width
35
36 self.in_a = FPOpIn(width)
37 self.in_b = FPOpIn(width)
38 self.out_z = FPOpOut(width)
39
40 self.states = []
41
42 def add_state(self, state):
43 self.states.append(state)
44 return state
45
46 def elaborate(self, platform=None):
47 """ creates the HDL code-fragment for FPDiv
48 """
49 m = Module()
50
51 # Latches
52 a = FPNumIn(None, self.width, False)
53 b = FPNumIn(None, self.width, False)
54 z = FPNumOut(self.width, False)
55
56 div = Div(a.m_width*2 + 3) # double the mantissa width plus g/r/sticky
57
58 of = Overflow()
59 m.submodules.in_a = a
60 m.submodules.in_b = b
61 m.submodules.z = z
62 m.submodules.of = of
63
64 m.d.comb += a.v.eq(self.in_a.v)
65 m.d.comb += b.v.eq(self.in_b.v)
66
67 with m.FSM() as fsm:
68
69 # ******
70 # gets operand a
71
72 with m.State("get_a"):
73 res = self.get_op(m, self.in_a, a, "get_b")
74 m.d.sync += eq([a, self.in_a.ready_o], res)
75
76 # ******
77 # gets operand b
78
79 with m.State("get_b"):
80 res = self.get_op(m, self.in_b, b, "special_cases")
81 m.d.sync += eq([b, self.in_b.ready_o], res)
82
83 # ******
84 # special cases: NaNs, infs, zeros, denormalised
85 # NOTE: some of these are unique to div. see "Special Operations"
86 # https://steve.hollasch.net/cgindex/coding/ieeefloat.html
87
88 with m.State("special_cases"):
89
90 # if a is NaN or b is NaN return NaN
91 with m.If(a.is_nan | b.is_nan):
92 m.next = "put_z"
93 m.d.sync += z.nan(1)
94
95 # if a is Inf and b is Inf return NaN
96 with m.Elif(a.is_inf & b.is_inf):
97 m.next = "put_z"
98 m.d.sync += z.nan(1)
99
100 # if a is inf return inf (or NaN if b is zero)
101 with m.Elif(a.is_inf):
102 m.next = "put_z"
103 m.d.sync += z.inf(a.s ^ b.s)
104
105 # if b is inf return zero
106 with m.Elif(b.is_inf):
107 m.next = "put_z"
108 m.d.sync += z.zero(a.s ^ b.s)
109
110 # if a is zero return zero (or NaN if b is zero)
111 with m.Elif(a.is_zero):
112 m.next = "put_z"
113 # if b is zero return NaN
114 with m.If(b.is_zero):
115 m.d.sync += z.nan(1)
116 with m.Else():
117 m.d.sync += z.zero(a.s ^ b.s)
118
119 # if b is zero return Inf
120 with m.Elif(b.is_zero):
121 m.next = "put_z"
122 m.d.sync += z.inf(a.s ^ b.s)
123
124 # Denormalised Number checks
125 with m.Else():
126 m.next = "normalise_a"
127 self.denormalise(m, a)
128 self.denormalise(m, b)
129
130 # ******
131 # normalise_a
132
133 with m.State("normalise_a"):
134 self.op_normalise(m, a, "normalise_b")
135
136 # ******
137 # normalise_b
138
139 with m.State("normalise_b"):
140 self.op_normalise(m, b, "divide_0")
141
142 # ******
143 # First stage of divide. initialise state
144
145 with m.State("divide_0"):
146 m.next = "divide_1"
147 m.d.sync += [
148 z.s.eq(a.s ^ b.s), # sign
149 z.e.eq(a.e - b.e), # exponent
150 div.dend.eq(a.m<<(a.m_width+3)), # 3 bits for g/r/sticky
151 div.dor.eq(b.m),
152 ]
153 div.reset(m)
154
155 # ******
156 # Second stage of divide.
157
158 with m.State("divide_1"):
159 m.next = "divide_2"
160 m.d.sync += [
161 div.quot.eq(div.quot << 1),
162 div.rem.eq(Cat(div.dend[-1], div.rem[0:])),
163 div.dend.eq(div.dend << 1),
164 ]
165
166 # ******
167 # Third stage of divide.
168 # This stage ends by jumping out to divide_3
169 # However it defaults to jumping to divide_1 (which comes back here)
170
171 with m.State("divide_2"):
172 with m.If(div.rem >= div.dor):
173 m.d.sync += [
174 div.quot[0].eq(1),
175 div.rem.eq(div.rem - div.dor),
176 ]
177 with m.If(div.count == div.width-2):
178 m.next = "divide_3"
179 with m.Else():
180 m.next = "divide_1"
181 m.d.sync += [
182 div.count.eq(div.count + 1),
183 ]
184
185 # ******
186 # Fourth stage of divide.
187
188 with m.State("divide_3"):
189 m.next = "normalise_1"
190 m.d.sync += [
191 z.m.eq(div.quot[3:]),
192 of.guard.eq(div.quot[2]),
193 of.round_bit.eq(div.quot[1]),
194 of.sticky.eq(div.quot[0] | (div.rem != 0))
195 ]
196
197 # ******
198 # First stage of normalisation.
199
200 with m.State("normalise_1"):
201 self.normalise_1(m, z, of, "normalise_2")
202
203 # ******
204 # Second stage of normalisation.
205
206 with m.State("normalise_2"):
207 self.normalise_2(m, z, of, "round")
208
209 # ******
210 # rounding stage
211
212 with m.State("round"):
213 self.roundz(m, z, of.roundz)
214 m.next = "corrections"
215
216 # ******
217 # correction stage
218
219 with m.State("corrections"):
220 self.corrections(m, z, "pack")
221
222 # ******
223 # pack stage
224
225 with m.State("pack"):
226 self.pack(m, z, "put_z")
227
228 # ******
229 # put_z stage
230
231 with m.State("put_z"):
232 self.put_z(m, z, self.out_z, "get_a")
233
234 return m
235
236
237 if __name__ == "__main__":
238 alu = FPDIV(width=32)
239 main(alu, ports=alu.in_a.ports() + alu.in_b.ports() + alu.out_z.ports())
240
241
242 # works... but don't use, just do "python fname.py convert -t v"
243 #print (verilog.convert(alu, ports=[
244 # ports=alu.in_a.ports() + \
245 # alu.in_b.ports() + \
246 # alu.out_z.ports())