bd1b4c062843835bf1e213ba466a0ff36d68be10
[ieee754fpu.git] / src / ieee754 / fpadd / align.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
6 from nmigen.cli import main, verilog
7
8 from ieee754.fpcommon.modbase import FPModBase
9 from ieee754.fpcommon.fpbase import FPNumBaseRecord
10 from ieee754.fpcommon.fpbase import MultiShiftRMerge
11 from ieee754.fpcommon.denorm import FPSCData
12 from ieee754.fpcommon.getop import FPPipeContext
13
14
15 class FPNumIn2Ops:
16
17 def __init__(self, pspec):
18 width = pspec.width
19 self.a = FPNumBaseRecord(width)
20 self.b = FPNumBaseRecord(width)
21 self.z = FPNumBaseRecord(width, False)
22 self.out_do_z = Signal(reset_less=True)
23 self.oz = Signal(width, reset_less=True)
24 self.ctx = FPPipeContext(pspec)
25 self.muxid = self.ctx.muxid
26
27 def eq(self, i):
28 return [self.z.eq(i.z), self.out_do_z.eq(i.out_do_z), self.oz.eq(i.oz),
29 self.a.eq(i.a), self.b.eq(i.b), self.ctx.eq(i.ctx)]
30
31
32 class FPAddAlignMultiMod:
33
34 def __init__(self, width):
35 self.in_a = FPNumBaseRecord(width)
36 self.in_b = FPNumBaseRecord(width)
37 self.out_a = FPNumBaseRecord(width)
38 self.out_b = FPNumBaseRecord(width)
39 self.exp_eq = Signal(reset_less=True)
40
41 def elaborate(self, platform):
42 # This one however (single-cycle) will do the shift
43 # in one go.
44
45 m = Module()
46
47 #m.submodules.align_in_a = self.in_a
48 #m.submodules.align_in_b = self.in_b
49 #m.submodules.align_out_a = self.out_a
50 #m.submodules.align_out_b = self.out_b
51
52 # NOTE: this does *not* do single-cycle multi-shifting,
53 # it *STAYS* in the align state until exponents match
54
55 # exponent of a greater than b: shift b down
56 m.d.comb += self.exp_eq.eq(0)
57 m.d.comb += self.out_a.eq(self.in_a)
58 m.d.comb += self.out_b.eq(self.in_b)
59 agtb = Signal(reset_less=True)
60 altb = Signal(reset_less=True)
61 m.d.comb += agtb.eq(self.in_a.e > self.in_b.e)
62 m.d.comb += altb.eq(self.in_a.e < self.in_b.e)
63 with m.If(agtb):
64 m.d.comb += self.out_b.shift_down(self.in_b)
65 # exponent of b greater than a: shift a down
66 with m.Elif(altb):
67 m.d.comb += self.out_a.shift_down(self.in_a)
68 # exponents equal: move to next stage.
69 with m.Else():
70 m.d.comb += self.exp_eq.eq(1)
71 return m
72
73
74 class FPAddAlignSingleMod(FPModBase):
75
76 def __init__(self, pspec):
77 super().__init__(pspec, "align")
78
79 def ispec(self):
80 return FPSCData(self.pspec, True)
81
82 def ospec(self):
83 return FPNumIn2Ops(self.pspec)
84
85 def elaborate(self, platform):
86 """ Aligns A against B or B against A, depending on which has the
87 greater exponent. This is done in a *single* cycle using
88 variable-width bit-shift
89
90 the shifter used here is quite expensive in terms of gates.
91 Mux A or B in (and out) into temporaries, as only one of them
92 needs to be aligned against the other
93 """
94 m = Module()
95
96 # temporary (muxed) input and output to be shifted
97 width = self.pspec.width
98 t_inp = FPNumBaseRecord(width)
99 t_out = FPNumBaseRecord(width)
100 espec = (len(self.i.a.e), True)
101 msr = MultiShiftRMerge(self.i.a.m_width, espec)
102 m.submodules.multishift_r = msr
103
104 ediff = Signal(espec, reset_less=True)
105 ediffr = Signal(espec, reset_less=True)
106 tdiff = Signal(espec, reset_less=True)
107 elz = Signal(reset_less=True)
108 egz = Signal(reset_less=True)
109
110 # connect multi-shifter to t_inp/out mantissa (and tdiff)
111 m.d.comb += msr.inp.eq(t_inp.m)
112 m.d.comb += msr.diff.eq(tdiff)
113 m.d.comb += t_out.m.eq(msr.m)
114 m.d.comb += t_out.e.eq(t_inp.e + tdiff)
115 m.d.comb += t_out.s.eq(t_inp.s)
116
117 m.d.comb += ediff.eq(self.i.a.e - self.i.b.e)
118 m.d.comb += ediffr.eq(self.i.b.e - self.i.a.e)
119 m.d.comb += elz.eq(self.i.a.e < self.i.b.e)
120 m.d.comb += egz.eq(self.i.a.e > self.i.b.e)
121
122 # default: A-exp == B-exp, A and B untouched (fall through)
123 m.d.comb += self.o.a.eq(self.i.a)
124 m.d.comb += self.o.b.eq(self.i.b)
125 # only one shifter (muxed)
126 #m.d.comb += t_out.shift_down_multi(tdiff, t_inp)
127 # exponent of a greater than b: shift b down
128 with m.If(~self.i.out_do_z):
129 with m.If(egz):
130 m.d.comb += [t_inp.eq(self.i.b),
131 tdiff.eq(ediff),
132 self.o.b.eq(t_out),
133 self.o.b.s.eq(self.i.b.s), # whoops forgot sign
134 ]
135 # exponent of b greater than a: shift a down
136 with m.Elif(elz):
137 m.d.comb += [t_inp.eq(self.i.a),
138 tdiff.eq(ediffr),
139 self.o.a.eq(t_out),
140 self.o.a.s.eq(self.i.a.s), # whoops forgot sign
141 ]
142
143 m.d.comb += self.o.ctx.eq(self.i.ctx)
144 m.d.comb += self.o.z.eq(self.i.z)
145 m.d.comb += self.o.out_do_z.eq(self.i.out_do_z)
146 m.d.comb += self.o.oz.eq(self.i.oz)
147
148 return m
149