2eb6a4994ad23ae8010ad3769910a4b3be003459
[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, a in enumerate(a_intervals):
73 end = keys[i]
74 comb += matrix[i][j].eq(a << b)
75 start = end
76
77 # now create a switch statement which sums the relevant partial results
78 # in each output-partition
79
80 intermed = matrix[0][0]
81 comb += out_intervals[0].eq(intermed)
82 for i in range(1, len(out_intervals)):
83 index = gates[:i] # selects the 'i' least significant bits
84 # of gates
85 element = Signal(width, name="element%d" % i)
86 for index in range(1<<i):
87 print(index)
88 with m.Switch(gates[:i]):
89 with m.Case(index):
90 index = math.ceil(math.log2(index + 1))
91 comb += element.eq(matrix[index][i])
92 print(keys[i-1])
93 temp = Signal(width, name="intermed%d" % i)
94 print(intermed[keys[0]:])
95 intermed = Mux(gates[i-1], element, element | intermed[keys[0]:])
96 comb += temp.eq(intermed)
97 comb += out_intervals[i].eq(intermed)
98
99
100 return m
101