concatenate mask bits on fan-in, split on fan-out
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sat, 3 Aug 2019 09:28:37 +0000 (10:28 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sat, 3 Aug 2019 09:28:37 +0000 (10:28 +0100)
src/nmutil/iocontrol.py
src/nmutil/multipipe.py
src/nmutil/singlepipe.py
src/nmutil/test/test_inout_unary_mux_cancel_pipe.py

index 8254795ea133798630d87480b73a012f7fca0171..04e4123839a03b8737f92d4a9cd8b2a0cc2dd1e5 100644 (file)
@@ -171,11 +171,12 @@ class PrevControl(Elaboratable):
         * data_i : an input - MUST be added by the USER of this class
     """
 
-    def __init__(self, i_width=1, stage_ctl=False, maskwid=0):
+    def __init__(self, i_width=1, stage_ctl=False, maskwid=0, offs=0):
         self.stage_ctl = stage_ctl
         self.maskwid = maskwid
         if maskwid:
             self.mask_i = Signal(maskwid)                # prev   >>in  self
+            self.stop_i = Signal(maskwid)                # prev   >>in  self
         self.valid_i = Signal(i_width, name="p_valid_i") # prev   >>in  self
         self._ready_o = Signal(name="p_ready_o")         # prev   <<out self
         self.data_i = None # XXX MUST BE ADDED BY USER
@@ -191,7 +192,8 @@ class PrevControl(Elaboratable):
             return self.s_ready_o # set dynamically by stage
         return self._ready_o      # return this when not under dynamic control
 
-    def _connect_in(self, prev, direct=False, fn=None, do_data=True):
+    def _connect_in(self, prev, direct=False, fn=None,
+                    do_data=True, do_stop=True):
         """ internal helper function to connect stage to an input source.
             do not use to connect stage-to-stage!
         """
@@ -200,6 +202,8 @@ class PrevControl(Elaboratable):
                prev.ready_o.eq(self.ready_o)]
         if self.maskwid:
             res.append(self.mask_i.eq(prev.mask_i))
+            if do_stop:
+                res.append(self.stop_i.eq(prev.stop_i))
         if do_data is False:
             return res
         data_i = fn(prev.data_i) if fn is not None else prev.data_i
@@ -241,6 +245,7 @@ class PrevControl(Elaboratable):
         yield self.ready_o
         if self.maskwid:
             yield self.mask_i
+            yield self.stop_i
         if hasattr(self.data_i, "ports"):
             yield from self.data_i.ports()
         elif isinstance(self.data_i, Sequence):
@@ -263,6 +268,7 @@ class NextControl(Elaboratable):
         self.maskwid = maskwid
         if maskwid:
             self.mask_o = Signal(maskwid)       # self out>>  next
+            self.stop_o = Signal(maskwid)       # self out>>  next
         self.valid_o = Signal(name="n_valid_o") # self out>>  next
         self.ready_i = Signal(name="n_ready_i") # self <<in   next
         self.data_o = None # XXX MUST BE ADDED BY USER
@@ -276,7 +282,7 @@ class NextControl(Elaboratable):
             return self.ready_i & self.d_valid
         return self.ready_i
 
-    def connect_to_next(self, nxt, do_data=True):
+    def connect_to_next(self, nxt, do_data=True, do_stop=True):
         """ helper function to connect to the next stage data/valid/ready.
             data/valid is passed *TO* nxt, and ready comes *IN* from nxt.
             use this when connecting stage-to-stage
@@ -288,11 +294,14 @@ class NextControl(Elaboratable):
                self.ready_i.eq(nxt.ready_o)]
         if self.maskwid:
             res.append(nxt.mask_i.eq(self.mask_o))
+            if do_stop:
+                res.append(nxt.stop_i.eq(self.stop_o))
         if do_data:
             res.append(nmoperator.eq(nxt.data_i, self.data_o))
         return res
 
-    def _connect_out(self, nxt, direct=False, fn=None, do_data=True):
+    def _connect_out(self, nxt, direct=False, fn=None,
+                     do_data=True, do_stop=True):
         """ internal helper function to connect stage to an output source.
             do not use to connect stage-to-stage!
         """
@@ -301,6 +310,8 @@ class NextControl(Elaboratable):
                self.ready_i.eq(ready_i)]
         if self.maskwid:
             res.append(nxt.mask_o.eq(self.mask_o))
+            if do_stop:
+                res.append(nxt.stop_o.eq(self.stop_o))
         if not do_data:
             return res
         data_o = fn(nxt.data_o) if fn is not None else nxt.data_o
@@ -316,6 +327,7 @@ class NextControl(Elaboratable):
         yield self.valid_o
         if self.maskwid:
             yield self.mask_o
+            yield self.stop_o
         if hasattr(self.data_o, "ports"):
             yield from self.data_o.ports()
         elif isinstance(self.data_o, Sequence):
index 6c858cd82af2980ea6ac5e8d11ed356111d70ee3..51acfba972983095eba257a5ff2ce7c26c25bb88 100644 (file)
@@ -26,7 +26,7 @@ from .iocontrol import NextControl, PrevControl
 class MultiInControlBase(Elaboratable):
     """ Common functions for Pipeline API
     """
-    def __init__(self, in_multi=None, p_len=1, maskmode=False):
+    def __init__(self, in_multi=None, p_len=1, masklen=0):
         """ Multi-input Control class.  Conforms to same API as ControlBase...
             mostly.  has additional indices to the *multiple* input stages
 
@@ -38,13 +38,12 @@ class MultiInControlBase(Elaboratable):
             * add data_o member  to NextControl
         """
         # set up input and output IO ACK (prev/next ready/valid)
-        maskwid = p_len if maskmode else 0
-        print ("multi_in", maskwid, maskmode, p_len)
+        print ("multi_in", masklen, p_len)
         p = []
         for i in range(p_len):
-            p.append(PrevControl(in_multi, maskwid=maskwid))
+            p.append(PrevControl(in_multi, maskwid=masklen))
         self.p = Array(p)
-        self.n = NextControl(maskwid=maskwid)
+        self.n = NextControl(maskwid=masklen*p_len)
 
     def connect_to_next(self, nxt, p_idx=0):
         """ helper function to connect to the next stage data/valid/ready.
@@ -91,7 +90,7 @@ class MultiInControlBase(Elaboratable):
 class MultiOutControlBase(Elaboratable):
     """ Common functions for Pipeline API
     """
-    def __init__(self, n_len=1, in_multi=None, maskmode=False):
+    def __init__(self, n_len=1, in_multi=None, masklen=0):
         """ Multi-output Control class.  Conforms to same API as ControlBase...
             mostly.  has additional indices to the multiple *output* stages
             [MultiInControlBase has multiple *input* stages]
@@ -105,11 +104,10 @@ class MultiOutControlBase(Elaboratable):
         """
 
         # set up input and output IO ACK (prev/next ready/valid)
-        maskwid = n_len if maskmode else 0
-        self.p = PrevControl(in_multi, maskwid=maskwid)
+        self.p = PrevControl(in_multi, maskwid=masklen*n_len)
         n = []
         for i in range(n_len):
-            n.append(NextControl(maskwid=maskwid))
+            n.append(NextControl(maskwid=masklen))
         self.n = Array(n)
 
     def connect_to_next(self, nxt, n_idx=0):
@@ -161,10 +159,10 @@ class CombMultiOutPipeline(MultiOutControlBase):
         n.data_o : stage output data array.       shaped according to ospec
     """
 
-    def __init__(self, stage, n_len, n_mux, maskmode=False):
-        MultiOutControlBase.__init__(self, n_len=n_len, maskmode=maskmode)
+    def __init__(self, stage, n_len, n_mux, masklen=0):
+        MultiOutControlBase.__init__(self, n_len=n_len, masklen=masklen)
         self.stage = stage
-        self.maskmode = maskmode
+        self.masklen = masklen
         self.n_mux = n_mux
 
         # set up the input and output data
@@ -208,9 +206,13 @@ class CombMultiOutPipeline(MultiOutControlBase):
         m.d.comb += self.p.ready_o.eq(~data_valid | self.n[muxid].ready_i)
         m.d.comb += data_valid.eq(p_valid_i | \
                                     (~self.n[muxid].ready_i & data_valid))
+        if self.masklen:
+            ml = [] # accumulate output masks
+            for i in range(len(self.n)):
+                ml.append(self.n[i].mask_o)
+            with m.If(pv):
+                m.d.comb += Cat(*ml).eq(self.p.mask_i)
         with m.If(pv):
-            if self.maskmode:
-                m.d.comb += self.n[muxid].mask_o.eq(self.p.mask_i)
             m.d.comb += eq(r_data, self.p.data_i)
         m.d.comb += eq(self.n[muxid].data_o, self.process(r_data))
 
@@ -232,10 +234,10 @@ class CombMultiInPipeline(MultiInControlBase):
             SYNCHRONOUSLY.
     """
 
-    def __init__(self, stage, p_len, p_mux, maskmode=False):
-        MultiInControlBase.__init__(self, p_len=p_len, maskmode=maskmode)
+    def __init__(self, stage, p_len, p_mux, masklen=0):
+        MultiInControlBase.__init__(self, p_len=p_len, masklen=masklen)
         self.stage = stage
-        self.maskmode = maskmode
+        self.masklen = masklen
         self.p_mux = p_mux
 
         # set up the input and output data
@@ -295,13 +297,19 @@ class CombMultiInPipeline(MultiInControlBase):
         m.d.comb += data_valid[mid].eq(p_valid_i[mid] | \
                                     (n_ready_in[mid] & data_valid[mid]))
 
+        ml = [] # accumulate output masks
         for i in range(p_len):
             vr = Signal(reset_less=True)
             m.d.comb += vr.eq(self.p[i].valid_i & self.p[i].ready_o)
             with m.If(vr):
                 m.d.comb += eq(r_data[i], self.p[i].data_i)
-                if self.maskmode:
-                    m.d.comb += self.n.mask_o.eq(self.p[i].mask_i)
+            if self.masklen:
+                mlen = len(self.p[i].mask_i)
+                s = mlen*i
+                e = mlen*(i+1)
+                ml.append(Mux(vr, self.p[i].mask_i, Const(0, mlen)))
+        if self.masklen:
+            m.d.comb += self.n.mask_o.eq(Cat(*ml))
 
         m.d.comb += eq(self.n.data_o, self.process(r_data[mid]))
 
@@ -309,10 +317,10 @@ class CombMultiInPipeline(MultiInControlBase):
 
 
 class CombMuxOutPipe(CombMultiOutPipeline):
-    def __init__(self, stage, n_len, maskmode=False):
+    def __init__(self, stage, n_len, masklen=0):
         # HACK: stage is also the n-way multiplexer
         CombMultiOutPipeline.__init__(self, stage, n_len=n_len,
-                                            n_mux=stage, maskmode=maskmode)
+                                            n_mux=stage, masklen=masklen)
 
         # HACK: n-mux is also the stage... so set the muxid equal to input muxid
         print ("combmuxout", self.p.data_i.muxid)
@@ -359,10 +367,10 @@ class PriorityCombMuxInPipe(CombMultiInPipeline):
     """ an example of how to use the combinatorial pipeline.
     """
 
-    def __init__(self, stage, p_len=2, maskmode=False):
+    def __init__(self, stage, p_len=2, masklen=0):
         p_mux = InputPriorityArbiter(self, p_len)
         CombMultiInPipeline.__init__(self, stage, p_len, p_mux,
-                                     maskmode=maskmode)
+                                     masklen=masklen)
 
 
 if __name__ == '__main__':
index 42cd158cc42afa2b8ae1d4e284a6ce05a2ee05da..17df104c3314d582e3dae2b1835d408a930bbb45 100644 (file)
@@ -432,7 +432,7 @@ class MaskCancellable(ControlBase):
         # is NOT "normal" for the Stage API.
         p_valid_i = Signal(reset_less=True)
         #print ("self.p.data_i", self.p.data_i)
-        m.d.comb += p_valid_i.eq(((self.p.mask_i & ~self.cancelmask).bool()))
+        m.d.comb += p_valid_i.eq(((self.p.mask_i & ~self.p.stop_i).bool()))
 
         # if idmask nonzero, mask gets passed on (and register set).
         # register is left as-is if idmask is zero, but out-mask is set to zero
@@ -447,6 +447,9 @@ class MaskCancellable(ControlBase):
         #m.d.comb += self.p._ready_o.eq(self.n.ready_i_test)
         m.d.comb += self.p._ready_o.eq(Const(1))
 
+        # always pass on stop (as combinatorial: single signal)
+        m.d.comb += self.n.stop_o.eq(self.p.stop_i)
+
         return self.m
 
 
index bde4fe986a4c5b1ac4da6315032370b451cbb157..6b40332f4556ad5fcab2d67d2cfe1de3d94b795d 100644 (file)
@@ -46,8 +46,7 @@ class PassThroughStage:
 
 
 class PassThroughPipe(MaskCancellable):
-    def __init__(self, maskwid, cancelmask):
-        self.cancelmask = cancelmask
+    def __init__(self, maskwid):
         MaskCancellable.__init__(self, PassThroughStage(), maskwid)
 
 
@@ -72,7 +71,7 @@ class InputTest:
             yield rs.data_i.data.eq(op2)
             yield rs.data_i.idx.eq(i)
             yield rs.data_i.muxid.eq(muxid)
-            yield rs.mask_i.eq(1<<muxid)
+            yield rs.mask_i.eq(1)
             yield
             o_p_ready = yield rs.ready_o
             while not o_p_ready:
@@ -137,12 +136,11 @@ class InputTest:
 
 
 class TestPriorityMuxPipe(PriorityCombMuxInPipe):
-    def __init__(self, num_rows, cancelmask):
+    def __init__(self, num_rows):
         self.num_rows = num_rows
-        self.cancelmask = cancelmask
         stage = PassThroughStage()
         PriorityCombMuxInPipe.__init__(self, stage,
-                                       p_len=self.num_rows, maskmode=True)
+                                       p_len=self.num_rows, masklen=1)
 
 
 class OutputTest:
@@ -183,22 +181,20 @@ class OutputTest:
 
 
 class TestMuxOutPipe(CombMuxOutPipe):
-    def __init__(self, num_rows, cancelmask):
+    def __init__(self, num_rows):
         self.num_rows = num_rows
-        self.cancelmask = cancelmask
         stage = PassThroughStage()
         CombMuxOutPipe.__init__(self, stage, n_len=self.num_rows,
-                                maskmode=True)
+                                masklen=1)
 
 
 class TestInOutPipe(Elaboratable):
     def __init__(self, num_rows=4):
         self.num_rows = nr = num_rows
-        self.cancelmask = cm = Signal(nr)         # cancellation mask
-        self.inpipe = TestPriorityMuxPipe(nr, cm) # fan-in (combinatorial)
-        self.pipe1 = PassThroughPipe(nr, cm)      # stage 1 (clock-sync)
-        self.pipe2 = PassThroughPipe(nr, cm)      # stage 2 (clock-sync)
-        self.outpipe = TestMuxOutPipe(nr, cm)     # fan-out (combinatorial)
+        self.inpipe = TestPriorityMuxPipe(nr) # fan-in (combinatorial)
+        self.pipe1 = PassThroughPipe(nr)      # stage 1 (clock-sync)
+        self.pipe2 = PassThroughPipe(nr)      # stage 2 (clock-sync)
+        self.outpipe = TestMuxOutPipe(nr)     # fan-out (combinatorial)
 
         self.p = self.inpipe.p  # kinda annoying,
         self.n = self.outpipe.n # use pipe in/out as this class in/out