Eliminate unused entries in shift matrix
[ieee754fpu.git] / src / ieee754 / part_shift / part_shift_dynamic.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 shifter. Unlike part_shift_scalar, both
9 operands can be partitioned
10
11 See:
12
13 * http://libre-riscv.org/3d_gpu/architecture/dynamic_simd/shift/
14 * http://bugs.libre-riscv.org/show_bug.cgi?id=173
15 """
16 from nmigen import Signal, Module, Elaboratable, Cat, Mux
17 from ieee754.part_mul_add.partpoints import PartitionPoints
18 import math
19
20
21 class PartitionedDynamicShift(Elaboratable):
22 def __init__(self, width, partition_points):
23 self.width = width
24 self.partition_points = PartitionPoints(partition_points)
25
26 self.a = Signal(width)
27 self.b = Signal(width)
28 self.output = Signal(width)
29
30 def elaborate(self, platform):
31 m = Module()
32 comb = m.d.comb
33 width = self.width
34 gates = Signal(self.partition_points.get_max_partition_count(width)-1)
35 comb += gates.eq(self.partition_points.as_sig())
36
37 matrix = []
38 keys = list(self.partition_points.keys()) + [self.width]
39 start = 0
40
41 # create a matrix of partial shift-results (similar to PartitionedMul
42 # matrices). These however have to be of length suitable to contain
43 # the full shifted "contribution". i.e. B from the LSB *could* contain
44 # a number great enough to shift the entirety of A LSB right up to
45 # the MSB of the output, however B from the *MSB* is *only* going
46 # to contribute to the *MSB* of the output.
47 for i in range(len(keys)):
48 row = []
49 start = 0
50 for j in range(len(keys)):
51 end = keys[j]
52 row.append(Signal(width - start,
53 name="matrix[%d][%d]" % (i, j)))
54 matrix.append(row)
55
56 a_intervals = []
57 b_intervals = []
58 out_intervals = []
59 intervals = []
60 start = 0
61 for i in range(len(keys)):
62 end = keys[i]
63 a_intervals.append(self.a[start:end])
64 b_intervals.append(self.b[start:end])
65 out_intervals.append(self.output[start:end])
66 intervals.append([start,end])
67 start = end
68
69 # actually calculate the shift-partials here
70 for i, b in enumerate(b_intervals):
71 start = 0
72 for j in range(i, len(a_intervals)):
73 a = a_intervals[j]
74 end = keys[i]
75 result_width = matrix[i][j].width
76 bwidth = math.ceil(math.log2(result_width + 1))
77 comb += matrix[i][j].eq(a << b[:bwidth])
78 start = end
79
80 # now create a switch statement which sums the relevant partial results
81 # in each output-partition
82
83 intermed = matrix[0][0]
84 comb += out_intervals[0].eq(intermed)
85 for i in range(1, len(out_intervals)):
86 index = gates[:i] # selects the 'i' least significant bits
87 # of gates
88 element = Signal(width, name="element%d" % i)
89 for index in range(1<<i):
90 print(index)
91 with m.Switch(gates[:i]):
92 with m.Case(index):
93 index = math.ceil(math.log2(index + 1))
94 comb += element.eq(matrix[index][i])
95 print(keys[i-1])
96 temp = Signal(width, name="intermed%d" % i)
97 print(intermed[keys[0]:])
98 intermed = Mux(gates[i-1], element, element | intermed[keys[0]:])
99 comb += temp.eq(intermed)
100 comb += out_intervals[i].eq(intermed)
101
102
103 return m
104