add bug cross-reference to #113 for FCVT unit tests
[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 temp = Signal(mwid, reset_less=True)
50 if self.loprop:
51 temp_r = Signal(mwid, reset_less=True)
52 with m.If(self.m_in[0]):
53 # propagate low bit: do an ASL basically, except
54 # i can't work out how to do it in nmigen sigh
55 m.d.comb += temp_r.eq((self.m_in[0] << clz) - 1)
56
57 # limclz sets a limit (set by the exponent) on how far M can be shifted
58 # this can be used to ensure that near-zero numbers don't then have
59 # to be shifted *back* (e < -126 in the case of FP32 for example)
60 if self.limclz is not False:
61 limclz = Mux(self.limclz > pe.o, pe.o, self.limclz)
62 else:
63 limclz = pe.o
64
65 m.d.comb += [
66 pe.i.eq(self.m_in[::-1]), # inverted
67 clz.eq(limclz), # count zeros from MSB down
68 temp.eq((self.m_in << clz)), # shift mantissa UP
69 self.e_out.eq(self.e_in - clz), # DECREASE exponent
70 ]
71 if self.loprop:
72 m.d.comb += self.m_out.eq(temp | temp_r)
73 else:
74 m.d.comb += self.m_out.eq(temp),
75
76 return m