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