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