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