switch to exact version of cython
[ieee754fpu.git] / src / ieee754 / fpcommon / msbhigh.py
1 """ module for adjusting a mantissa and exponent so that the MSB is always 1
2 """
3
4 from nmigen import Module, Signal, Mux, Elaboratable
5 from nmigen.lib.coding import PriorityEncoder
6
7
8 class FPMSBHigh(Elaboratable):
9 """ makes the top mantissa bit hi (i.e. shifts until it is)
10
11 NOTE: this does NOT do any kind of checks. do not pass in
12 zero (empty) stuff, and it's best to check if the MSB is
13 already 1 before calling it. although it'll probably work
14 in both cases...
15
16 * exponent is signed
17 * mantissa is unsigned.
18 * loprop: propagates the low bit (LSB) on the shift
19 * limclz: use this to limit the amount of shifting.
20
21 examples:
22 exp = -30, mantissa = 0b00011 - output: -33, 0b11000
23 exp = 2, mantissa = 0b01111 - output: 1, 0b11110
24 """
25 def __init__(self, m_width, e_width, limclz=False, loprop=False):
26 self.m_width = m_width
27 self.e_width = e_width
28 self.loprop = loprop
29 self.limclz = limclz and Signal((e_width, True), reset_less=True)
30
31 self.m_in = Signal(m_width, reset_less=True)
32 self.e_in = Signal((e_width, True), reset_less=True)
33 self.m_out = Signal(m_width, reset_less=True)
34 self.e_out = Signal((e_width, True), reset_less=True)
35
36 def elaborate(self, platform):
37 m = Module()
38
39 mwid = self.m_width
40 pe = PriorityEncoder(mwid)
41 m.submodules.pe = pe
42
43 # *sigh* not entirely obvious: count leading zeros (clz)
44 # with a PriorityEncoder. to find from the MSB
45 # we reverse the order of the bits. it would be better if PE
46 # took a "reverse" argument.
47
48 clz = Signal((len(self.e_out), True), reset_less=True)
49 # GRRR utterly irritating https://github.com/nmigen/nmigen/issues/302
50 uclz = Signal(len(self.e_out), reset_less=True)
51 temp = Signal(mwid, reset_less=True)
52 if self.loprop:
53 temp_r = Signal(mwid, reset_less=True)
54 with m.If(self.m_in[0]):
55 # propagate low bit: do an ASL basically, except
56 # i can't work out how to do it in nmigen sigh
57 m.d.comb += temp_r.eq((self.m_in[0] << clz) - 1)
58
59 # limclz sets a limit (set by the exponent) on how far M can be shifted
60 # this can be used to ensure that near-zero numbers don't then have
61 # to be shifted *back* (e < -126 in the case of FP32 for example)
62 if self.limclz is not False:
63 limclz = Mux(self.limclz > pe.o, pe.o, self.limclz)
64 else:
65 limclz = pe.o
66
67 m.d.comb += [
68 pe.i.eq(self.m_in[::-1]), # inverted
69 clz.eq(limclz), # count zeros from MSB down
70 uclz.eq(limclz), # *sigh*...
71 temp.eq((self.m_in << uclz)), # shift mantissa UP
72 self.e_out.eq(self.e_in - clz), # DECREASE exponent
73 ]
74 if self.loprop:
75 m.d.comb += self.m_out.eq(temp | temp_r)
76 else:
77 m.d.comb += self.m_out.eq(temp),
78
79 return m