had to add fixed_width parameter temporarily to confirm that the
[ieee754fpu.git] / src / ieee754 / part / partsig.py
index b899397f7feffb5a945f7f3a046a76a51e0d6a44..61b64e661e4ff1a3e3b2d8e3896c18be923947eb 100644 (file)
@@ -28,6 +28,8 @@ from ieee754.part_mux.part_mux import PMux
 from ieee754.part_ass.passign import PAssign
 from ieee754.part_cat.pcat import PCat
 from ieee754.part_repl.prepl import PRepl
+from ieee754.part.simd_scope import SimdScope
+from ieee754.part.layout_experiment import layout
 from operator import or_, xor, and_, not_
 
 from nmigen import (Signal, Const, Cat)
@@ -79,6 +81,7 @@ class PartType:  # TODO decide name
     def blanklanes(self):
         return 0
 
+
 # this one would be an elwidth version
 # see https://bugs.libre-soc.org/show_bug.cgi?id=713#c34
 # it requires an "adapter" which is the layout() function
@@ -86,42 +89,84 @@ class PartType:  # TODO decide name
 # function and this class then "understands" the relationship
 # between elwidth and the PartitionPoints that were created
 # by layout()
-
-
-class ElWidthPartType:  # TODO decide name
+class ElwidPartType:  # TODO decide name
     def __init__(self, psig):
         self.psig = psig
 
     def get_mask(self):
-        ppoints, pbits = layout()
-        return ppoints.values()  # i think
+        return list(self.psig._shape.partpoints.values())  # i think
 
     def get_switch(self):
-        return self.psig.elwidth
+        return self.psig.scope.elwid       # switch on elwid: match get_cases()
 
     def get_cases(self):
-        ppoints, pbits = layout()
-        return pbits
+        return self.psig._shape.bitp.keys() # all possible values of elwid
 
     @property
     def blanklanes(self):
-        return 0  # TODO
+        return self.psig.shape.blankmask
+
+
+class SimdShape(Shape):
+    """a SIMD variant of Shape. supports:
+    * fixed overall width with variable (maxed-out) element lengths
+    * fixed element widths with overall size auto-determined
+    * both fixed overall width and fixed element widths
+
+    naming is preserved to be compatible with Shape().
+    """
+    def __init__(self, scope, width=None, # this is actually widths_at_elwid
+                              signed=False,
+                              fixed_width=None): # fixed overall width
+        widths_at_elwid = width
+        print ("SimdShape width", width, "fixed_width", fixed_width)
+        # this check is done inside layout but do it again here anyway
+        assert fixed_width != None or widths_at_elwid != None, \
+            "both width (widths_at_elwid) and fixed_width cannot be None"
+        (pp, bitp, lpoints, bmask, fixed_width, lane_shapes, part_wid) = \
+            layout(scope.elwid,
+                   scope.vec_el_counts,
+                   widths_at_elwid,
+                   fixed_width)
+        self.partpoints = pp
+        self.bitp = bitp        # binary values for partpoints at each elwidth
+        self.lpoints = lpoints  # layout ranges
+        self.blankmask = bmask  # blanking mask (partitions always padding)
+        self.partwid = part_wid # smallest alignment start point for elements
+
+        # pass through the calculated width to Shape() so that when/if
+        # objects using this Shape are downcast, they know exactly how to
+        # get *all* bits and need know absolutely nothing about SIMD at all
+        Shape.__init__(self, fixed_width, signed)
 
 
 class SimdSignal(UserValue):
     # XXX ################################################### XXX
     # XXX Keep these functions in the same order as ast.Value XXX
     # XXX ################################################### XXX
-    def __init__(self, mask, *args, src_loc_at=0, **kwargs):
+    def __init__(self, mask, shape=None, *args,
+                       src_loc_at=0, fixed_width=None, **kwargs):
         super().__init__(src_loc_at=src_loc_at)
-        self.sig = Signal(*args, **kwargs)
-        width = len(self.sig)  # get signal width
+        print ("SimdSignal shape", shape)
         # create partition points
-        if isinstance(mask, PartitionPoints):
-            self.partpoints = mask
+        if isinstance(mask, SimdScope): # mask parameter is a SimdScope
+            self.scope = mask
+            self.ptype = ElwidPartType(self)
+            # adapt shape to a SimdShape
+            if not isinstance(shape, SimdShape):
+                shape = SimdShape(self.scope, shape, fixed_width=fixed_width)
+            self._shape = shape
+            self.sig = Signal(shape, *args, **kwargs)
+            # get partpoints from SimdShape
+            self.partpoints = shape.partpoints
         else:
-            self.partpoints = make_partition2(mask, width)
-        self.ptype = PartType(self)
+            self.sig = Signal(shape, *args, **kwargs)
+            width = len(self.sig)  # get signal width
+            if isinstance(mask, PartitionPoints):
+                self.partpoints = mask
+            else:
+                self.partpoints = make_partition2(mask, width)
+            self.ptype = PartType(self)
 
     def set_module(self, m):
         self.m = m
@@ -144,13 +189,22 @@ class SimdSignal(UserValue):
 
     # nmigen-redirected constructs (Mux, Cat, Switch, Assign)
 
-    # TODO, http://bugs.libre-riscv.org/show_bug.cgi?id=458
-    # def __Part__(self, offset, width, stride=1, *, src_loc_at=0):
+    # TODO, http://bugs.libre-riscv.org/show_bug.cgi?id=716
+    #def __Part__(self, offset, width, stride=1, *, src_loc_at=0):
+        raise NotImplementedError("TODO: implement as "
+                        "(self>>(offset*stride)[:width]")
+    # TODO, http://bugs.libre-riscv.org/show_bug.cgi?id=716
+    def __Slice__(self, start, stop, *, src_loc_at=0):
+        # NO.  Swizzled shall NOT be deployed, it violates
+        # Project Development Practices
+        raise NotImplementedError("TODO: need PartitionedSlice")
 
     def __Repl__(self, count, *, src_loc_at=0):
         return PRepl(self.m, self, count, self.ptype)
 
     def __Cat__(self, *args, src_loc_at=0):
+        print ("partsig cat", self, args)
+        # TODO: need SwizzledSimdValue-aware Cat
         args = [self] + list(args)
         for sig in args:
             assert isinstance(sig, SimdSignal), \
@@ -166,7 +220,17 @@ class SimdSignal(UserValue):
         return PMux(self.m, self.partpoints, self, val1, val2, self.ptype)
 
     def __Assign__(self, val, *, src_loc_at=0):
-        # print ("partsig ass", self, val)
+        print ("partsig assign", self, val)
+        # this is a truly awful hack, outlined here:
+        # https://bugs.libre-soc.org/show_bug.cgi?id=731#c13
+        # during the period between constructing Simd-aware sub-modules
+        # and the elaborate() being called on them there is a window of
+        # opportunity to indicate which of those submodules is LHS and
+        # which is RHS. manic laughter is permitted.  *gibber*.
+        if hasattr(self, "_hack_submodule"):
+            self._hack_submodule.set_lhs_mode(True)
+        if hasattr(val, "_hack_submodule"):
+            val._hack_submodule.set_lhs_mode(False)
         return PAssign(self.m, self, val, self.ptype)
 
     # TODO, http://bugs.libre-riscv.org/show_bug.cgi?id=458
@@ -399,6 +463,8 @@ class SimdSignal(UserValue):
     # def __getitem__(self, key):
 
     def __new_sign(self, signed):
+        # XXX NO - SimdShape not Shape
+        print ("XXX requires SimdShape not Shape")
         shape = Shape(len(self), signed=signed)
         result = SimdSignal.like(self, shape=shape)
         self.m.d.comb += result.sig.eq(self.sig)