--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# See Notices.txt for copyright information
+
+"""
+Copyright (C) 2020 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
+
+dynamically partitionable shifter. Unlike part_shift_scalar, both
+operands can be partitioned
+
+See:
+
+* http://libre-riscv.org/3d_gpu/architecture/dynamic_simd/shift/
+* http://bugs.libre-riscv.org/show_bug.cgi?id=173
+"""
+from nmigen import Signal, Module, Elaboratable, Cat, Mux
+from ieee754.part_mul_add.partpoints import PartitionPoints
+import math
+
+
+class PartitionedDynamicShift(Elaboratable):
+ def __init__(self, width, partition_points):
+ self.width = width
+ self.partition_points = PartitionPoints(partition_points)
+
+ self.a = Signal(width)
+ self.b = Signal(width)
+ self.output = Signal(width)
+
+ def elaborate(self, platform):
+ m = Module()
+ comb = m.d.comb
+ width = self.width
+ gates = self.partition_points.as_sig()
+
+ matrix = []
+ keys = list(self.partition_points.keys()) + [self.width]
+ start = 0
+
+ for i in range(len(keys)):
+ row = []
+ start = 0
+ for i in range(len(keys)):
+ end = keys[i]
+ row.append(Signal(width - start))
+ matrix.append(row)
+
+ a_intervals = []
+ b_intervals = []
+ out_intervals = []
+ intervals = []
+ start = 0
+ for i in range(len(keys)):
+ end = keys[i]
+ a_intervals.append(self.a[start:end])
+ b_intervals.append(self.b[start:end])
+ out_intervals.append(self.output[start:end])
+ intervals.append([start,end])
+ start = end
+
+ for i, b in enumerate(b_intervals):
+ start = 0
+ for j, a in enumerate(a_intervals):
+ end = keys[i]
+ comb += matrix[i][j].eq(a << b)
+ start = end
+
+ intermed = matrix[0][0]
+ comb += out_intervals[0].eq(intermed)
+ for i in range(1, len(out_intervals)):
+ index = gates[:i] # selects the 'i' least significant bits
+ # of gates
+ for index in range(1<<(i-1)):
+ with m.Switch(gates[:i]):
+ with m.Case(index):
+ element = matrix[index][i]
+ print(keys[i-1])
+ intermed = Mux(gates[i-1], element, element | intermed[:keys[i-1]])
+ comb += out_intervals[i].eq(intermed)
+
+
+ return m
+
# See Notices.txt for copyright information
"""
-Copyright (C) 2020 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
-dynamically-partitionable "comparison" class, directly equivalent
-to Signal.__eq__, __gt__ and __ge__, except SIMD-partitionable
+dynamically partitionable shifter. Only the operand to be shifted can
+be partitioned, the amount to shift by *must* be a scalar
See:
sp = Signal(width)
comb += sp[start:].eq(self.data[start:end] << self.shifter)
shiftparts.append(sp)
-
+
start = end # for next time round loop
for i, interval in enumerate(intervals):
--- /dev/null
+from nmigen import Module, Signal
+from nmigen.back.pysim import Simulator, Delay, Settle
+from nmigen.test.utils import FHDLTestCase
+from ieee754.part_mul_add.partpoints import PartitionPoints
+
+from ieee754.part_shift_scalar.part_shift_dynamic import \
+ PartitionedDynamicShift
+
+import unittest
+
+class DynamicShiftTestCase(FHDLTestCase):
+ def get_intervals(self, signal, points):
+ start = 0
+ interval = []
+ keys = list(points.keys()) + [signal.width]
+ for key in keys:
+ end = key
+ interval.append(signal[start:end])
+ start = end
+ return interval
+
+ def test_dynamic(self):
+ m = Module()
+ comb = m.d.comb
+ mwidth = 4
+ width = 32
+ step = int(width/mwidth)
+ gates = Signal(mwidth-1)
+ points = PartitionPoints()
+ for i in range(mwidth-1):
+ points[(i+1)*step] = gates[i]
+ a = Signal(width)
+ b = Signal(width)
+ output = Signal(width)
+ a_intervals = self.get_intervals(a, points)
+ b_intervals = self.get_intervals(b, points)
+ output_intervals = self.get_intervals(output, points)
+
+ m.submodules.dut = dut = PartitionedDynamicShift(width, points)
+ comb += [dut.a.eq(a),
+ dut.b.eq(b),
+ output.eq(dut.output)]
+
+ sim = Simulator(m)
+ def process():
+ yield a.eq(0x01010101)
+ yield b.eq(0x04030201)
+ for i in range(1<<(mwidth-1)):
+ yield gates.eq(0)
+ yield Delay(1e-6)
+ yield Settle()
+ yield gates.eq(1)
+ yield Delay(1e-6)
+ yield Settle()
+
+
+ sim.add_process(process)
+ with sim.write_vcd("test.vcd", "test.gtkw", traces=[a,b,output]):
+ sim.run()
+
+if __name__ == "__main__":
+ unittest.main()
+
+
+
+