first initial success with div algorithm
[ieee754fpu.git] / src / add / nmigen_div_experiment.py
1 # IEEE Floating Point Adder (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.quotient = Signal(width)
14 self.divisor = Signal(width)
15 self.dividend = Signal(width)
16 self.remainder = Signal(width)
17 self.count = Signal(6)
18
19 self.czero = Const(0, width)
20
21 def reset(self, m):
22 m.d.sync += [
23 self.quotient.eq(self.czero),
24 self.remainder.eq(self.czero),
25 self.count.eq(Const(0, 6))
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 FPAdd
41 """
42 m = Module()
43
44 # Latches
45 a = FPNum(self.width, 24)
46 b = FPNum(self.width, 24)
47 z = FPNum(self.width, 24)
48
49 div = Div(51)
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 add. 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 # if b is zero return NaN
88 with m.If(b.is_zero()):
89 m.d.sync += z.nan(1)
90 with m.Else():
91 m.d.sync += z.inf(a.s ^ b.s)
92
93 # if b is inf return zero
94 with m.Elif(b.is_inf()):
95 m.next = "put_z"
96 m.d.sync += z.zero(a.s ^ b.s)
97
98 # if a is inf return zero (or NaN if b is zero)
99 with m.Elif(a.is_inf()):
100 m.next = "put_z"
101 # if b is zero return NaN
102 with m.If(b.is_zero()):
103 m.d.sync += z.nan(1)
104 with m.Else():
105 m.d.sync += z.inf(a.s ^ b.s)
106
107 # if b is zero return Inf
108 with m.Elif(b.is_zero()):
109 m.next = "put_z"
110 m.d.sync += z.zero(a.s ^ b.s)
111
112 # Denormalised Number checks
113 with m.Else():
114 m.next = "normalise_a"
115 self.denormalise(m, a)
116 self.denormalise(m, b)
117
118 # ******
119 # normalise_a
120
121 with m.State("normalise_a"):
122 self.op_normalise(m, a, "normalise_b")
123
124 # ******
125 # normalise_b
126
127 with m.State("normalise_b"):
128 self.op_normalise(m, b, "divide_0")
129
130 # ******
131 # First stage of divide. initialise state
132
133 with m.State("divide_0"):
134 m.next = "divide_1"
135 m.d.sync += [
136 z.s.eq(a.s ^ b.s), # sign
137 z.e.eq(a.e - b.e), # exponent
138 div.dividend.eq(a.m<<27),
139 div.divisor.eq(b.m),
140 ]
141 div.reset(m)
142
143 # ******
144 # Second stage of divide.
145
146 with m.State("divide_1"):
147 m.next = "divide_2"
148 m.d.sync += [
149 div.quotient.eq(div.quotient << 1),
150 div.remainder.eq(Cat(div.dividend[50], div.remainder[0:])),
151 div.dividend.eq(div.dividend << 1),
152 ]
153
154 # ******
155 # Third stage of divide.
156
157 with m.State("divide_2"):
158 with m.If(div.remainder >= div.divisor):
159 m.d.sync += [
160 div.quotient[0].eq(1),
161 div.remainder.eq(div.remainder - div.divisor),
162 ]
163 with m.If(div.count == div.width-2):
164 m.next = "divide_3"
165 with m.Else():
166 m.next = "divide_1"
167 m.d.sync += [
168 div.count.eq(div.count + 1),
169 ]
170
171 # ******
172 # Fourth stage of divide.
173
174 with m.State("divide_3"):
175 m.next = "normalise_1"
176 m.d.sync += [
177 z.m.eq(div.quotient[3:27]),
178 of.guard.eq(div.quotient[2]),
179 of.round_bit.eq(div.quotient[1]),
180 of.sticky.eq(div.quotient[0] | (div.remainder != 0))
181 ]
182
183 # ******
184 # First stage of normalisation.
185
186 with m.State("normalise_1"):
187 self.normalise_1(m, z, of, "normalise_2")
188
189 # ******
190 # Second stage of normalisation.
191
192 with m.State("normalise_2"):
193 self.normalise_2(m, z, of, "round")
194
195 # ******
196 # rounding stage
197
198 with m.State("round"):
199 self.roundz(m, z, of, "corrections")
200
201 # ******
202 # correction stage
203
204 with m.State("corrections"):
205 self.corrections(m, z, "pack")
206
207 # ******
208 # pack stage
209
210 with m.State("pack"):
211 self.pack(m, z, "put_z")
212
213 # ******
214 # put_z stage
215
216 with m.State("put_z"):
217 self.put_z(m, z, self.out_z, "get_a")
218
219 return m
220
221
222 if __name__ == "__main__":
223 alu = FPDIV(width=32)
224 main(alu, ports=alu.in_a.ports() + alu.in_b.ports() + alu.out_z.ports())
225
226
227 # works... but don't use, just do "python fname.py convert -t v"
228 #print (verilog.convert(alu, ports=[
229 # ports=alu.in_a.ports() + \
230 # alu.in_b.ports() + \
231 # alu.out_z.ports())