1 """ IEEE Floating Point Divider
3 Copyright (C) 2019 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
4 Copyright (C) 2019 Jacob Lifshay
7 * http://bugs.libre-riscv.org/show_bug.cgi?id=99
8 * http://bugs.libre-riscv.org/show_bug.cgi?id=43
9 * http://bugs.libre-riscv.org/show_bug.cgi?id=44
12 from nmigen
import Module
, Signal
, Cat
, Const
, Elaboratable
13 from nmigen
.cli
import main
, verilog
16 from ieee754
.fpcommon
.fpbase
import FPNumDecode
, FPNumBaseRecord
17 from nmutil
.singlepipe
import SimpleHandshake
, StageChain
19 from ieee754
.fpcommon
.fpbase
import FPState
, FPID
20 from ieee754
.fpcommon
.getop
import FPADDBaseData
21 from ieee754
.fpcommon
.denorm
import (FPSCData
, FPAddDeNormMod
)
22 from ieee754
.fpmul
.align
import FPAlignModSingle
25 class FPDIVSpecialCasesMod(Elaboratable
):
26 """ special cases: NaNs, infs, zeros, denormalised
27 see "Special Operations"
28 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
31 def __init__(self
, pspec
):
37 return FPADDBaseData(self
.pspec
)
40 return FPSCData(self
.pspec
, False)
42 def setup(self
, m
, i
):
43 """ links module to inputs and outputs
45 m
.submodules
.specialcases
= self
46 m
.d
.comb
+= self
.i
.eq(i
)
51 def elaborate(self
, platform
):
54 #m.submodules.sc_out_z = self.o.z
56 # decode: XXX really should move to separate stage
57 a1
= FPNumBaseRecord(self
.pspec
.width
, False, name
="a1")
58 b1
= FPNumBaseRecord(self
.pspec
.width
, False, name
="b1")
59 m
.submodules
.sc_decode_a
= a1
= FPNumDecode(None, a1
)
60 m
.submodules
.sc_decode_b
= b1
= FPNumDecode(None, b1
)
61 m
.d
.comb
+= [a1
.v
.eq(self
.i
.a
),
67 sabx
= Signal(reset_less
=True) # sign a xor b (sabx, get it?)
68 m
.d
.comb
+= sabx
.eq(a1
.s ^ b1
.s
)
70 abnan
= Signal(reset_less
=True)
71 m
.d
.comb
+= abnan
.eq(a1
.is_nan | b1
.is_nan
)
73 abinf
= Signal(reset_less
=True)
74 m
.d
.comb
+= abinf
.eq(a1
.is_inf
& b1
.is_inf
)
76 with m
.If(self
.i
.ctx
.op
== 0): # DIV
78 # if a is NaN or b is NaN return NaN
80 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
81 m
.d
.comb
+= self
.o
.z
.nan(0)
83 # if a is inf and b is Inf return NaN
85 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
86 m
.d
.comb
+= self
.o
.z
.nan(0)
88 # if a is inf return inf
89 with m
.Elif(a1
.is_inf
):
90 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
91 m
.d
.comb
+= self
.o
.z
.inf(sabx
)
93 # if b is inf return zero
94 with m
.Elif(b1
.is_inf
):
95 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
96 m
.d
.comb
+= self
.o
.z
.zero(sabx
)
98 # if a is zero return zero (or NaN if b is zero)
99 with m
.Elif(a1
.is_zero
):
100 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
101 m
.d
.comb
+= self
.o
.z
.zero(sabx
)
102 # b is zero return NaN
103 with m
.If(b1
.is_zero
):
104 m
.d
.comb
+= self
.o
.z
.nan(0)
106 # if b is zero return Inf
107 with m
.Elif(b1
.is_zero
):
108 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
109 m
.d
.comb
+= self
.o
.z
.inf(sabx
)
111 # Denormalised Number checks next, so pass a/b data through
113 m
.d
.comb
+= self
.o
.out_do_z
.eq(0)
115 with m
.If(self
.i
.ctx
.op
== 1): # SQRT
117 # if a is zero return zero
118 with m
.If(a1
.is_zero
):
119 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
120 m
.d
.comb
+= self
.o
.z
.zero(a1
.s
)
124 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
125 m
.d
.comb
+= self
.o
.z
.nan(0)
127 # if a is inf return inf
128 with m
.Elif(a1
.is_inf
):
129 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
130 m
.d
.comb
+= self
.o
.z
.inf(sabx
)
132 # if a is NaN return NaN
133 with m
.Elif(a1
.is_nan
):
134 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
135 m
.d
.comb
+= self
.o
.z
.nan(0)
137 # Denormalised Number checks next, so pass a/b data through
139 m
.d
.comb
+= self
.o
.out_do_z
.eq(0)
141 with m
.If(self
.i
.ctx
.op
== 2): # RSQRT
143 # if a is NaN return canonical NaN
144 with m
.If(a1
.is_nan
):
145 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
146 m
.d
.comb
+= self
.o
.z
.nan(0)
148 # if a is +/- zero return +/- INF
149 with m
.Elif(a1
.is_zero
):
150 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
151 # this includes the "weird" case 1/sqrt(-0) == -Inf
152 m
.d
.comb
+= self
.o
.z
.inf(a1
.s
)
154 # -ve number is canonical NaN
156 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
157 m
.d
.comb
+= self
.o
.z
.nan(0)
159 # if a is inf return zero (-ve already excluded, above)
160 with m
.Elif(a1
.is_inf
):
161 m
.d
.comb
+= self
.o
.out_do_z
.eq(1)
162 m
.d
.comb
+= self
.o
.z
.zero(0)
164 # Denormalised Number checks next, so pass a/b data through
166 m
.d
.comb
+= self
.o
.out_do_z
.eq(0)
168 m
.d
.comb
+= self
.o
.oz
.eq(self
.o
.z
.v
)
169 m
.d
.comb
+= self
.o
.ctx
.eq(self
.i
.ctx
)
174 class FPDIVSpecialCases(FPState
):
175 """ special cases: NaNs, infs, zeros, denormalised
176 NOTE: some of these are unique to div. see "Special Operations"
177 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
180 def __init__(self
, pspec
):
181 FPState
.__init
__(self
, "special_cases")
182 self
.mod
= FPDIVSpecialCasesMod(pspec
)
183 self
.out_z
= self
.mod
.ospec()
184 self
.out_do_z
= Signal(reset_less
=True)
186 def setup(self
, m
, i
):
187 """ links module to inputs and outputs
189 self
.mod
.setup(m
, i
, self
.out_do_z
)
190 m
.d
.sync
+= self
.out_z
.v
.eq(self
.mod
.out_z
.v
) # only take the output
191 m
.d
.sync
+= self
.out_z
.mid
.eq(self
.mod
.o
.mid
) # (and mid)
195 with m
.If(self
.out_do_z
):
198 m
.next
= "denormalise"
201 class FPDIVSpecialCasesDeNorm(FPState
, SimpleHandshake
):
202 """ special cases: NaNs, infs, zeros, denormalised
205 def __init__(self
, pspec
):
206 FPState
.__init
__(self
, "special_cases")
208 SimpleHandshake
.__init
__(self
, self
) # pipe is its own stage
209 self
.out
= self
.ospec()
212 return FPADDBaseData(self
.pspec
) # SpecialCases ispec
215 return FPSCData(self
.pspec
, False) # Align ospec
217 def setup(self
, m
, i
):
218 """ links module to inputs and outputs
220 smod
= FPDIVSpecialCasesMod(self
.pspec
)
221 dmod
= FPAddDeNormMod(self
.pspec
, False)
222 amod
= FPAlignModSingle(self
.pspec
, False)
224 chain
= StageChain([smod
, dmod
, amod
])
227 # only needed for break-out (early-out)
228 # self.out_do_z = smod.o.out_do_z
232 def process(self
, i
):
236 # for break-out (early-out)
237 #with m.If(self.out_do_z):
240 m
.d
.sync
+= self
.out
.eq(self
.process(None))