add dynamic comb/sync mode to MaskCancellable
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Thu, 15 Aug 2019 16:51:20 +0000 (17:51 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Thu, 15 Aug 2019 16:51:20 +0000 (17:51 +0100)
src/nmutil/multipipe.py
src/nmutil/singlepipe.py
src/nmutil/test/test_buf_pipe.py

index b6eb0cd7944a3b63d067e365584c6268e6547737..b4995326d32c45cb3bac82877e9a83fb0d9dd17e 100644 (file)
@@ -320,7 +320,7 @@ class CombMultiInPipeline(MultiInControlBase):
             m.d.comb += data_valid[i].eq(0)
             m.d.comb += n_ready_in[i].eq(1)
             m.d.comb += p_valid_i[i].eq(0)
-            m.d.comb += self.p[i].ready_o.eq(~data_valid[i] | self.n.ready_i)
+            #m.d.comb += self.p[i].ready_o.eq(~data_valid[i] | self.n.ready_i)
             m.d.comb += self.p[i].ready_o.eq(0)
         p = self.p[mid]
         maskedout = Signal(reset_less=True)
index ba4acfbe3d4195f63c467a9b679753fdb9bc0bd7..3a04fbf9bcaf6947905df9d7e36d345229392ee7 100644 (file)
@@ -469,58 +469,78 @@ class MaskCancellable(ControlBase):
                               |             |
                               +--process->--^
     """
-    def __init__(self, stage, maskwid, in_multi=None, stage_ctl=False):
+    def __init__(self, stage, maskwid, in_multi=None, stage_ctl=False,
+                       dynamic=False):
         ControlBase.__init__(self, stage, in_multi, stage_ctl, maskwid)
+        self.dynamic = dynamic
+        if dynamic:
+            self.latchmode = Signal()
+        else:
+            self.latchmode = Const(1)
 
     def elaborate(self, platform):
         self.m = m = ControlBase.elaborate(self, platform)
 
-        r_busy = Signal()
-        result = _spec(self.stage.ospec, "r_tmp")
-
-        # establish if the data should be passed on.  cancellation is
-        # a global signal.
-        p_valid_i = Signal(reset_less=True)
-        #print ("self.p.data_i", self.p.data_i)
-        maskedout = Signal(len(self.p.mask_i), reset_less=True)
-        m.d.comb += maskedout.eq(self.p.mask_i & ~self.p.stop_i)
-
-        # establish some combinatorial temporaries
-        n_ready_i = Signal(reset_less=True, name="n_i_rdy_data")
-        p_valid_i_p_ready_o = Signal(reset_less=True)
-        m.d.comb += [p_valid_i.eq(self.p.valid_i_test & maskedout.bool()),
-                     n_ready_i.eq(self.n.ready_i_test),
-                     p_valid_i_p_ready_o.eq(p_valid_i & self.p.ready_o),
-        ]
-
-        # store result of processing in combinatorial temporary
-        m.d.comb += nmoperator.eq(result, self.data_r)
-
-        # 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
-        # note however: only the *uncancelled* mask bits get passed on
-        m.d.sync += self.n.mask_o.eq(Mux(p_valid_i, maskedout, 0))
+        r_mask = Signal(len(self.p.mask_i), reset_less=True)
+
+        with m.If(self.latchmode):
+            r_busy = Signal()
+            result = _spec(self.stage.ospec, "r_tmp")
+
+            # establish if the data should be passed on.  cancellation is
+            # a global signal.
+            p_valid_i = Signal(reset_less=True)
+            #print ("self.p.data_i", self.p.data_i)
+            maskedout = Signal(len(self.p.mask_i), reset_less=True)
+            m.d.comb += maskedout.eq(self.p.mask_i & ~self.p.stop_i)
+
+            # establish some combinatorial temporaries
+            n_ready_i = Signal(reset_less=True, name="n_i_rdy_data")
+            p_valid_i_p_ready_o = Signal(reset_less=True)
+            m.d.comb += [p_valid_i.eq(self.p.valid_i_test & maskedout.bool()),
+                         n_ready_i.eq(self.n.ready_i_test),
+                         p_valid_i_p_ready_o.eq(p_valid_i & self.p.ready_o),
+            ]
+
+            # 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
+            # note however: only the *uncancelled* mask bits get passed on
+            m.d.sync += r_mask.eq(Mux(p_valid_i, maskedout, 0))
+            m.d.comb += self.n.mask_o.eq(r_mask)
+
+            # always pass on stop (as combinatorial: single signal)
+            m.d.comb += self.n.stop_o.eq(self.p.stop_i)
 
-        # always pass on stop (as combinatorial: single signal)
-        m.d.comb += self.n.stop_o.eq(self.p.stop_i)
-
-        # previous valid and ready
-        with m.If(p_valid_i_p_ready_o):
-            data_o = self._postprocess(result) # XXX TBD, does nothing right now
-            m.d.sync += [r_busy.eq(1),      # output valid
-                         nmoperator.eq(self.n.data_o, data_o), # update output
-                        ]
-        # previous invalid or not ready, however next is accepting
-        with m.Elif(n_ready_i):
             data_o = self._postprocess(result) # XXX TBD, does nothing right now
-            m.d.sync += [nmoperator.eq(self.n.data_o, data_o)]
-            # TODO: could still send data here (if there was any)
-            #m.d.sync += self.n.valid_o.eq(0) # ...so set output invalid
-            m.d.sync += r_busy.eq(0) # ...so set output invalid
+            # previous valid and ready
+            with m.If(p_valid_i_p_ready_o):
+                # store result of processing in combinatorial temporary
+                m.d.sync += nmoperator.eq(result, self.data_r)
+                m.d.sync += r_busy.eq(1)      # output valid
+            # previous invalid or not ready, however next is accepting
+            with m.Elif(n_ready_i):
+                # TODO: could still send data here (if there was any)
+                #m.d.sync += self.n.valid_o.eq(0) # ...so set output invalid
+                m.d.sync += r_busy.eq(0) # ...so set output invalid
+                m.d.sync += nmoperator.eq(result, self.data_r)
+
+            m.d.comb += [nmoperator.eq(self.n.data_o, data_o)]
+
+            m.d.comb += self.n.valid_o.eq(r_busy)
+            # if next is ready, so is previous
+            m.d.comb += self.p._ready_o.eq(n_ready_i)
+
+        with m.Else():
+            # pass everything straight through.  p connected to n: data,
+            # valid, mask, everything.
+            data_o = self._postprocess(self.data_r)
+            m.d.comb += self.n.valid_o.eq(self.p.valid_i_test)
+            m.d.comb += self.p._ready_o.eq(self.n.ready_i_test)
+            m.d.comb += self.n.stop_o.eq(self.p.stop_i)
+            m.d.comb += self.n.mask_o.eq(self.p.mask_i)
+            m.d.comb += nmoperator.eq(self.n.data_o, data_o)
 
-        m.d.comb += self.n.valid_o.eq(r_busy)
-        # if next is ready, so is previous
-        m.d.comb += self.p._ready_o.eq(n_ready_i)
 
         return self.m
 
index dcd2b45818432d91aa1e114e5d69e5549a188b33..d489b6526abad4419d994c596e4ac03f0a1d8cc8 100644 (file)
@@ -1130,12 +1130,39 @@ class MaskCancellablePipe(MaskCancellable):
 
     """ connects two stages together as a *single* combinatorial stage.
     """
-    def __init__(self):
-        self.cancelmask = Signal(16)
+    def __init__(self, dynamic=False):
         stage1 = ExampleMaskCancellable()
         stage2 = ExampleMaskCancellable()
         combined = StageChain([stage1, stage2])
-        MaskCancellable.__init__(self, combined, 16)
+        MaskCancellable.__init__(self, combined, 16, dynamic=dynamic)
+
+
+class MaskCancellablePipe1(MaskCancellable):
+
+    """ connects a stage to a cancellable pipe with "dynamic" mode on.
+    """
+    def __init__(self, dynamic=True):
+        stage = ExampleMaskCancellable()
+        MaskCancellable.__init__(self, stage, 16, dynamic=dynamic)
+
+
+class MaskCancellableDynamic(ControlBase):
+
+    def __init__(self):
+        ControlBase.__init__(self, None, maskwid=16)
+
+    def elaborate(self, platform):
+        m = ControlBase.elaborate(self, platform)
+
+        pipe1 = MaskCancellablePipe1()
+        pipe2 = MaskCancellablePipe1()
+
+        m.submodules.pipe1 = pipe1
+        m.submodules.pipe2 = pipe2
+
+        m.d.comb += self.connect([pipe1, pipe2])
+
+        return m
 
 
 def data_chain0():
@@ -1175,6 +1202,20 @@ def test0():
     run_simulation(dut, [test.send, test.rcv],
                         vcd_name="test_maskchain0.vcd")
 
+def test0_1():
+    print ("test 0.1")
+    dut = MaskCancellableDynamic()
+    ports = [dut.p.valid_i, dut.n.ready_i,
+             dut.n.valid_o, dut.p.ready_o] #+ \
+             #dut.p.data_i.ports() + dut.n.data_o.ports()
+    vl = rtlil.convert(dut, ports=ports)
+    with open("test_maskchain0_dynamic.il", "w") as f:
+        f.write(vl)
+    data = data_chain0()
+    test = TestMask(dut, resultfn_0, 16, data=data)
+    run_simulation(dut, [test.send, test.rcv],
+                        vcd_name="test_maskchain0_dynamic.vcd")
+
 def notworking1():
     print ("test 1")
     dut = ExampleBufPipe()
@@ -1494,3 +1535,4 @@ def test999():
 
 if __name__ == '__main__':
     test0()
+    test0_1()