add grant links, and record of funding under #538
[nmutil.git] / src / nmutil / ripple.py
1 """
2 This work is funded through NLnet under Grant 2019-02-012
3
4 License: LGPLv3+
5
6 """
7
8 # need to ripple the starting LSB of each partition up through the
9 # rest of the partition. a Mux on the partition gate therefore selects
10 # either the current "thing" being propagated, or, if the gate is set open,
11 # will select the current bit from the input.
12 #
13 # this is actually a useful function, it's one of "set before first" or
14 # "set after first" from vector predication processing.
15
16 from nmigen import Signal, Module, Elaboratable, Mux, Cat
17 from nmigen.cli import main
18
19
20 class RippleLSB(Elaboratable):
21 """RippleLSB
22
23 based on a partition mask, the LSB is "rippled" (duplicated)
24 up to the beginning of the next partition.
25 """
26 def __init__(self, width):
27 self.width = width
28 self.results_in = Signal(width, reset_less=True)
29 self.gates = Signal(width-1, reset_less=True)
30 self.output = Signal(width, reset_less=True)
31
32 def elaborate(self, platform):
33 m = Module()
34 comb = m.d.comb
35 width = self.width
36
37 current_result = self.results_in[0]
38 comb += self.output[0].eq(current_result)
39
40 for i in range(width-1):
41 cur = Mux(self.gates[i], self.results_in[i+1], self.output[i])
42 comb += self.output[i+1].eq(cur)
43
44 return m
45
46
47 class MoveMSBDown(Elaboratable):
48 """MoveMSBDown
49
50 based on a partition mask, moves the MSB down to the LSB position.
51 only the MSB is relevant, other bits are ignored. works by first
52 rippling the MSB across the entire partition (TODO: split that out
53 into its own useful module), then ANDs the (new) LSB with the
54 partition mask to isolate it.
55 """
56 def __init__(self, width):
57 self.width = width
58 self.results_in = Signal(width, reset_less=True)
59 self.gates = Signal(width-1, reset_less=True)
60 self.output = Signal(width, reset_less=True)
61
62 def elaborate(self, platform):
63 m = Module()
64 comb = m.d.comb
65 width = self.width
66 intermed = Signal(width, reset_less=True)
67
68 # first propagate MSB down until the nearest partition gate
69 comb += intermed[-1].eq(self.results_in[-1]) # start at MSB
70 for i in range(width-2, -1, -1):
71 cur = Mux(self.gates[i], self.results_in[i], intermed[i+1])
72 comb += intermed[i].eq(cur)
73
74 # now only select those bits where the mask starts
75 out = [intermed[0]] # LSB of first part always set
76 for i in range(width-1): # length of partition gates
77 out.append(self.gates[i] & intermed[i+1])
78 comb += self.output.eq(Cat(*out))
79
80 return m
81
82
83 if __name__ == "__main__":
84 # python3 ieee754/part_cmp/ripple.py generate -t il > ripple.il
85 # then check with yosys "read_ilang ripple.il; show top"
86 alu = MoveMSBDown(width=4)
87 main(alu, ports=[alu.results_in, alu.gates, alu.output])
88