move gt_combiner out of experiments/
[ieee754fpu.git] / src / ieee754 / part_cmp / eq_gt_ge.py
1 # SPDX-License-Identifier: LGPL-2.1-or-later
2 # See Notices.txt for copyright information
3
4 """
5 Copyright (C) 2020 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
6
7 dynamically-partitionable "comparison" class, directly equivalent
8 to Signal.__eq__ except SIMD-partitionable
9
10 See:
11
12 * http://libre-riscv.org/3d_gpu/architecture/dynamic_simd/eq
13 * http://bugs.libre-riscv.org/show_bug.cgi?id=132
14 """
15
16 from nmigen import Signal, Module, Elaboratable, Cat, C, Mux, Repl
17 from nmigen.back.pysim import Simulator, Delay, Settle
18 from nmigen.cli import main
19
20 from ieee754.part_mul_add.partpoints import PartitionPoints
21 from ieee754.part_cmp.gt_combiner import GTCombiner
22
23
24 class PartitionedEqGtGe(Elaboratable):
25 EQ = C(0b00, 2)
26 GT = C(0b01, 2)
27 GE = C(0b10, 2)
28
29 # Expansion of the partitioned equals module to handle Greater
30 # Than and Greater than or Equal to. The function being evaluated
31 # is selected by the opcode signal, where:
32 # opcode 0x00 - EQ
33 # opcode 0x01 - GT
34 # opcode 0x02 - GE
35 def __init__(self, width, partition_points):
36 """Create a ``PartitionedEq`` operator
37 """
38 self.width = width
39 self.a = Signal(width, reset_less=True)
40 self.b = Signal(width, reset_less=True)
41 self.opcode = Signal(2)
42 self.partition_points = PartitionPoints(partition_points)
43 self.mwidth = len(self.partition_points)+1
44 self.output = Signal(self.mwidth, reset_less=True)
45 if not self.partition_points.fits_in_width(width):
46 raise ValueError("partition_points doesn't fit in width")
47
48 def elaborate(self, platform):
49 m = Module()
50 comb = m.d.comb
51 m.submodules.gtc = gtc = GTCombiner(self.mwidth)
52
53 # make a series of "eqs" and "gts", splitting a and b into
54 # partition chunks
55 eqs = Signal(self.mwidth, reset_less=True)
56 eql = []
57 gts = Signal(self.mwidth, reset_less=True)
58 gtl = []
59
60 keys = list(self.partition_points.keys()) + [self.width]
61 start = 0
62 for i in range(len(keys)):
63 end = keys[i]
64 eql.append(self.a[start:end] == self.b[start:end])
65 gtl.append(self.a[start:end] > self.b[start:end])
66 start = end # for next time round loop
67 comb += eqs.eq(Cat(*eql))
68 comb += gts.eq(Cat(*gtl))
69
70 # control the constant injected into the partition
71 # next to a closed gate
72 aux_input = Signal()
73 # enable or disable the gt input for the gt partition combiner
74 gt_en = Signal()
75
76 with m.Switch(self.opcode):
77 with m.Case(0b00): # equals
78 comb += aux_input.eq(1)
79 comb += gt_en.eq(0)
80 with m.Case(0b01): # greater than
81 comb += aux_input.eq(0)
82 comb += gt_en.eq(1)
83 with m.Case(0b10): # greater than or equal to
84 comb += aux_input.eq(1)
85 comb += gt_en.eq(1)
86
87 comb += gtc.gates.eq(self.partition_points.as_sig())
88 comb += gtc.eqs.eq(eqs)
89 comb += gtc.gts.eq(gts)
90 comb += gtc.aux_input.eq(aux_input)
91 comb += gtc.gt_en.eq(gt_en)
92 comb += self.output.eq(gtc.outputs)
93
94 return m
95
96 def ports(self):
97 return [self.a, self.b, self.opcode,
98 self.partition_points.as_sig(),
99 self.output]
100
101 if __name__ == "__main__":
102 from ieee754.part_mul_add.partpoints import make_partition
103 m = Module()
104 mask = Signal(4)
105 m.submodules.egg = egg = PartitionedEqGtGe(16, make_partition(mask, 16))
106
107 sim = Simulator(m)
108
109 def process():
110 yield mask.eq(0b010)
111 yield egg.a.eq(0xf000)
112 yield egg.b.eq(0)
113 yield Delay(1e-6)
114 out = yield egg.output
115 print ("out", bin(out))
116 yield mask.eq(0b111)
117 yield egg.a.eq(0x0000)
118 yield egg.b.eq(0)
119 yield Delay(1e-6)
120 yield mask.eq(0b010)
121 yield egg.a.eq(0x0000)
122 yield egg.b.eq(0)
123 yield Delay(1e-6)
124 out = yield egg.output
125 print ("out", bin(out))
126
127 sim.add_process(process)
128 with sim.write_vcd("eq_gt_ge.vcd", "eq_gt_ge.gtkw", traces=egg.ports()):
129 sim.run()