switch to exact version of cython
[ieee754fpu.git] / src / ieee754 / part_mux / part_mux.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/mux
13 * http://bugs.libre-riscv.org/show_bug.cgi?id=132
14 """
15
16 from nmigen import Signal, Module, Elaboratable, Mux
17 from ieee754.part_mul_add.partpoints import PartitionPoints
18 from ieee754.part_mul_add.partpoints import make_partition2
19
20
21 modcount = 0 # global for now
22 def PMux(m, mask, sel, a, b, ctx):
23 global modcount
24 modcount += 1
25 width = len(a.sig) # get width
26 part_pts = make_partition2(mask, width) # create partition points
27 pm = PartitionedMux(width, part_pts, ctx)
28 m.d.comb += pm.a.eq(a.sig)
29 m.d.comb += pm.b.eq(b.sig)
30 m.d.comb += pm.sel.eq(sel)
31 setattr(m.submodules, "pmux%d" % modcount, pm)
32 return pm.output
33
34
35 class PartitionedMux(Elaboratable):
36 """PartitionedMux: Partitioned "Mux"
37
38 takes a partition point set, subdivides a and b into blocks
39 and "selects" them. the assumption is that "sel" has had
40 its LSB propagated up throughout the entire partition, and
41 consequently the incoming selector (sel) can completely
42 ignore what the *actual* partition bits are.
43 """
44 def __init__(self, width, partition_points, ctx):
45 self.width = width
46 self.ctx = ctx
47 self.partition_points = PartitionPoints(partition_points)
48 self.mwidth = len(self.partition_points)+1
49 self.a = Signal(width, reset_less=True)
50 self.b = Signal(width, reset_less=True)
51 self.sel = Signal(self.mwidth, reset_less=True)
52 self.output = Signal(width, reset_less=True)
53 assert self.partition_points.fits_in_width(width), \
54 "partition_points doesn't fit in width"
55
56 def elaborate(self, platform):
57 m = Module()
58 comb = m.d.comb
59
60 # loop across all partition ranges.
61 # drop the selection directly into the output.
62 keys = list(self.partition_points.keys()) + [self.width]
63 stt = 0
64 for i in range(len(keys)):
65 end = keys[i]
66 mux = self.output[stt:end]
67 comb += mux.eq(Mux(self.sel[i], self.a[stt:end], self.b[stt:end]))
68 stt = end # for next time round loop
69
70 return m
71
72 def ports(self):
73 return [self.a.sig, self.b.sig, self.sel, self.output]
74