update comments
[ieee754fpu.git] / src / add / multipipe.py
index f0c9aeacd34d97761da25fe27ba8794edf7b29e2..e24703f8fcbea1125c00c1a335ca2bb1290c32aa 100644 (file)
@@ -1,18 +1,28 @@
-""" Combinatorial Multi-input multiplexer block conforming to Pipeline API
+""" Combinatorial Multi-input and Multi-output multiplexer blocks
+    conforming to Pipeline API
+
+    Multi-input is complex because if any one input is ready, the output
+    can be ready, and the decision comes from a separate module.
+
+    Multi-output is simple (pretty much identical to UnbufferedPipeline),
+    and the selection is just a mux.  The only proviso (difference) being:
+    the outputs not being selected have to have their ready_o signals
+    DEASSERTED.
 """
 
 from math import log
 """
 
 from math import log
-from nmigen import Signal, Cat, Const, Mux, Module, Array
+from nmigen import Signal, Cat, Const, Mux, Module, Array, Elaboratable
 from nmigen.cli import verilog, rtlil
 from nmigen.lib.coding import PriorityEncoder
 from nmigen.hdl.rec import Record, Layout
 from nmigen.cli import verilog, rtlil
 from nmigen.lib.coding import PriorityEncoder
 from nmigen.hdl.rec import Record, Layout
+from stageapi import _spec
 
 from collections.abc import Sequence
 
 from example_buf_pipe import eq, NextControl, PrevControl, ExampleStage
 
 
 
 from collections.abc import Sequence
 
 from example_buf_pipe import eq, NextControl, PrevControl, ExampleStage
 
 
-class MultiInControlBase:
+class MultiInControlBase(Elaboratable):
     """ Common functions for Pipeline API
     """
     def __init__(self, in_multi=None, p_len=1):
     """ Common functions for Pipeline API
     """
     def __init__(self, in_multi=None, p_len=1):
@@ -23,8 +33,8 @@ class MultiInControlBase:
             * n: contains ready/valid to the next stage
 
             User must also:
             * n: contains ready/valid to the next stage
 
             User must also:
-            * add i_data members to PrevControl and
-            * add o_data member  to NextControl
+            * add data_i members to PrevControl and
+            * add data_o member  to NextControl
         """
         # set up input and output IO ACK (prev/next ready/valid)
         p = []
         """
         # set up input and output IO ACK (prev/next ready/valid)
         p = []
@@ -57,20 +67,25 @@ class MultiInControlBase:
     def set_input(self, i, idx=0):
         """ helper function to set the input data
         """
     def set_input(self, i, idx=0):
         """ helper function to set the input data
         """
-        return eq(self.p[idx].i_data, i)
+        return eq(self.p[idx].data_i, i)
 
 
-    def ports(self):
-        res = []
-        for i in range(len(self.p)):
-            res += [self.p[i].i_valid, self.p[i].o_ready,
-                    self.p[i].i_data]# XXX need flattening!]
-        res += [self.n.i_ready, self.n.o_valid,
-                self.n.o_data]   # XXX need flattening!]
-        return res
+    def elaborate(self, platform):
+        m = Module()
+        for i, p in enumerate(self.p):
+            setattr(m.submodules, "p%d" % i, p)
+        m.submodules.n = self.n
+        return m
 
 
+    def __iter__(self):
+        for p in self.p:
+            yield from p
+        yield from self.n
+
+    def ports(self):
+        return list(self)
 
 
 
 
-class MultiOutControlBase:
+class MultiOutControlBase(Elaboratable):
     """ Common functions for Pipeline API
     """
     def __init__(self, n_len=1, in_multi=None):
     """ Common functions for Pipeline API
     """
     def __init__(self, n_len=1, in_multi=None):
@@ -82,8 +97,8 @@ class MultiOutControlBase:
             * n: contains ready/valid to the next stages PLURAL
 
             User must also:
             * n: contains ready/valid to the next stages PLURAL
 
             User must also:
-            * add i_data member to PrevControl and
-            * add o_data members to NextControl
+            * add data_i member to PrevControl and
+            * add data_o members to NextControl
         """
 
         # set up input and output IO ACK (prev/next ready/valid)
         """
 
         # set up input and output IO ACK (prev/next ready/valid)
@@ -112,20 +127,25 @@ class MultiOutControlBase:
             return self.n[idx]._connect_out(nxt.n)
         return self.n[idx]._connect_out(nxt.n[nxt_idx])
 
             return self.n[idx]._connect_out(nxt.n)
         return self.n[idx]._connect_out(nxt.n[nxt_idx])
 
+    def elaborate(self, platform):
+        m = Module()
+        m.submodules.p = self.p
+        for i, n in enumerate(self.n):
+            setattr(m.submodules, "n%d" % i, n)
+        return m
+
     def set_input(self, i):
         """ helper function to set the input data
         """
     def set_input(self, i):
         """ helper function to set the input data
         """
-        return eq(self.p.i_data, i)
+        return eq(self.p.data_i, i)
 
 
-    def ports(self):
-        res = []
-        res += [self.p.i_valid, self.p.o_ready,
-                self.p.i_data] # XXX need flattening!
-        for i in range(len(self.n)):
-            res += [self.n[i].i_ready, self.n[i].o_valid,
-                    self.n[i].o_data]   # XXX need flattening!
-        return res
+    def __iter__(self):
+        yield from self.p
+        for n in self.n:
+            yield from n
 
 
+    def ports(self):
+        return list(self)
 
 
 class CombMultiOutPipeline(MultiOutControlBase):
 
 
 class CombMultiOutPipeline(MultiOutControlBase):
@@ -133,14 +153,8 @@ class CombMultiOutPipeline(MultiOutControlBase):
 
         Attributes:
         -----------
 
         Attributes:
         -----------
-        p.i_data : StageInput, shaped according to ispec
-            The pipeline input
-        p.o_data : StageOutput, shaped according to ospec
-            The pipeline output
-        r_data : input_shape according to ispec
-            A temporary (buffered) copy of a prior (valid) input.
-            This is HELD if the output is not ready.  It is updated
-            SYNCHRONOUSLY.
+        p.data_i : stage input data (non-array).  shaped according to ispec
+        n.data_o : stage output data array.       shaped according to ospec
     """
 
     def __init__(self, stage, n_len, n_mux):
     """
 
     def __init__(self, stage, n_len, n_mux):
@@ -149,37 +163,47 @@ class CombMultiOutPipeline(MultiOutControlBase):
         self.n_mux = n_mux
 
         # set up the input and output data
         self.n_mux = n_mux
 
         # set up the input and output data
-        self.p.i_data = stage.ispec() # input type
+        self.p.data_i = _spec(stage.ispec, 'data_i') # input type
         for i in range(n_len):
         for i in range(n_len):
-            self.n[i].o_data = stage.ospec() # output type
+            name = 'data_o_%d' % i
+            self.n[i].data_o = _spec(stage.ospec, name) # output type
+
+    def process(self, i):
+        if hasattr(self.stage, "process"):
+            return self.stage.process(i)
+        return i
 
     def elaborate(self, platform):
 
     def elaborate(self, platform):
-        m = Module()
+        m = MultiOutControlBase.elaborate(self, platform)
 
         if hasattr(self.n_mux, "elaborate"): # TODO: identify submodule?
             m.submodules += self.n_mux
 
         # need buffer register conforming to *input* spec
 
         if hasattr(self.n_mux, "elaborate"): # TODO: identify submodule?
             m.submodules += self.n_mux
 
         # need buffer register conforming to *input* spec
-        r_data = self.stage.ispec() # input type
+        r_data = _spec(self.stage.ispec, 'r_data') # input type
         if hasattr(self.stage, "setup"):
             self.stage.setup(m, r_data)
 
         if hasattr(self.stage, "setup"):
             self.stage.setup(m, r_data)
 
+        # multiplexer id taken from n_mux
         mid = self.n_mux.m_id
 
         mid = self.n_mux.m_id
 
-        data_valid = Signal() # is data valid or not
-        p_i_valid = Signal(reset_less=True)
-        m.d.comb += p_i_valid.eq(self.p.i_valid_logic())
+        # temporaries
+        p_valid_i = Signal(reset_less=True)
+        pv = Signal(reset_less=True)
+        m.d.comb += p_valid_i.eq(self.p.valid_i_test)
+        m.d.comb += pv.eq(self.p.valid_i & self.p.ready_o)
 
 
+        # all outputs to next stages first initialised to zero (invalid)
+        # the only output "active" is then selected by the muxid
         for i in range(len(self.n)):
         for i in range(len(self.n)):
-            m.d.comb += self.n[i].o_valid.eq(0)
-        data_valid = self.n[mid].o_valid
-        #m.d.comb += self.n[mid].o_valid.eq(data_valid)
-        m.d.comb += self.p.o_ready.eq(~data_valid | self.n[mid].i_ready)
-        m.d.comb += data_valid.eq(p_i_valid | \
-                                    (~self.n[mid].i_ready & data_valid))
-        with m.If(self.p.i_valid & self.p.o_ready):
-            m.d.comb += eq(r_data, self.p.i_data)
-        m.d.comb += eq(self.n[mid].o_data, self.stage.process(r_data))
+            m.d.comb += self.n[i].valid_o.eq(0)
+        data_valid = self.n[mid].valid_o
+        m.d.comb += self.p.ready_o.eq(~data_valid | self.n[mid].ready_i)
+        m.d.comb += data_valid.eq(p_valid_i | \
+                                    (~self.n[mid].ready_i & data_valid))
+        with m.If(pv):
+            m.d.comb += eq(r_data, self.p.data_i)
+        m.d.comb += eq(self.n[mid].data_o, self.process(r_data))
 
         return m
 
 
         return m
 
@@ -189,9 +213,9 @@ class CombMultiInPipeline(MultiInControlBase):
 
         Attributes:
         -----------
 
         Attributes:
         -----------
-        p.i_data : StageInput, shaped according to ispec
+        p.data_i : StageInput, shaped according to ispec
             The pipeline input
             The pipeline input
-        p.o_data : StageOutput, shaped according to ospec
+        p.data_o : StageOutput, shaped according to ospec
             The pipeline output
         r_data : input_shape according to ispec
             A temporary (buffered) copy of a prior (valid) input.
             The pipeline output
         r_data : input_shape according to ispec
             A temporary (buffered) copy of a prior (valid) input.
@@ -206,64 +230,85 @@ class CombMultiInPipeline(MultiInControlBase):
 
         # set up the input and output data
         for i in range(p_len):
 
         # set up the input and output data
         for i in range(p_len):
-            self.p[i].i_data = stage.ispec() # input type
-        self.n.o_data = stage.ospec()
+            name = 'data_i_%d' % i
+            self.p[i].data_i = _spec(stage.ispec, name) # input type
+        self.n.data_o = _spec(stage.ospec, 'data_o')
+
+    def process(self, i):
+        if hasattr(self.stage, "process"):
+            return self.stage.process(i)
+        return i
 
     def elaborate(self, platform):
 
     def elaborate(self, platform):
-        m = Module()
+        m = MultiInControlBase.elaborate(self, platform)
 
         m.submodules += self.p_mux
 
         # need an array of buffer registers conforming to *input* spec
         r_data = []
         data_valid = []
 
         m.submodules += self.p_mux
 
         # need an array of buffer registers conforming to *input* spec
         r_data = []
         data_valid = []
-        p_i_valid = []
-        n_i_readyn = []
+        p_valid_i = []
+        n_ready_in = []
         p_len = len(self.p)
         for i in range(p_len):
         p_len = len(self.p)
         for i in range(p_len):
-            r = self.stage.ispec() # input type
+            name = 'r_%d' % i
+            r = _spec(self.stage.ispec, name) # input type
             r_data.append(r)
             data_valid.append(Signal(name="data_valid", reset_less=True))
             r_data.append(r)
             data_valid.append(Signal(name="data_valid", reset_less=True))
-            p_i_valid.append(Signal(name="p_i_valid", reset_less=True))
-            n_i_readyn.append(Signal(name="n_i_readyn", reset_less=True))
+            p_valid_i.append(Signal(name="p_valid_i", reset_less=True))
+            n_ready_in.append(Signal(name="n_ready_in", reset_less=True))
             if hasattr(self.stage, "setup"):
                 self.stage.setup(m, r)
         if len(r_data) > 1:
             r_data = Array(r_data)
             if hasattr(self.stage, "setup"):
                 self.stage.setup(m, r)
         if len(r_data) > 1:
             r_data = Array(r_data)
-            p_i_valid = Array(p_i_valid)
-            n_i_readyn = Array(n_i_readyn)
+            p_valid_i = Array(p_valid_i)
+            n_ready_in = Array(n_ready_in)
             data_valid = Array(data_valid)
 
             data_valid = Array(data_valid)
 
+        nirn = Signal(reset_less=True)
+        m.d.comb += nirn.eq(~self.n.ready_i)
         mid = self.p_mux.m_id
         for i in range(p_len):
             m.d.comb += data_valid[i].eq(0)
         mid = self.p_mux.m_id
         for i in range(p_len):
             m.d.comb += data_valid[i].eq(0)
-            m.d.comb += n_i_readyn[i].eq(1)
-            m.d.comb += p_i_valid[i].eq(0)
-            m.d.comb += self.p[i].o_ready.eq(0)
-        m.d.comb += p_i_valid[mid].eq(self.p_mux.active)
-        m.d.comb += self.p[mid].o_ready.eq(~data_valid[mid] | self.n.i_ready)
-        m.d.comb += n_i_readyn[mid].eq(~self.n.i_ready & data_valid[mid])
+            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(0)
+        m.d.comb += p_valid_i[mid].eq(self.p_mux.active)
+        m.d.comb += self.p[mid].ready_o.eq(~data_valid[mid] | self.n.ready_i)
+        m.d.comb += n_ready_in[mid].eq(nirn & data_valid[mid])
         anyvalid = Signal(i, reset_less=True)
         av = []
         for i in range(p_len):
             av.append(data_valid[i])
         anyvalid = Cat(*av)
         anyvalid = Signal(i, reset_less=True)
         av = []
         for i in range(p_len):
             av.append(data_valid[i])
         anyvalid = Cat(*av)
-        m.d.comb += self.n.o_valid.eq(anyvalid.bool())
-        m.d.comb += data_valid[mid].eq(p_i_valid[mid] | \
-                                    (n_i_readyn[mid] & data_valid[mid]))
+        m.d.comb += self.n.valid_o.eq(anyvalid.bool())
+        m.d.comb += data_valid[mid].eq(p_valid_i[mid] | \
+                                    (n_ready_in[mid] & data_valid[mid]))
 
         for i in range(p_len):
             vr = Signal(reset_less=True)
 
         for i in range(p_len):
             vr = Signal(reset_less=True)
-            m.d.comb += vr.eq(self.p[i].i_valid & self.p[i].o_ready)
+            m.d.comb += vr.eq(self.p[i].valid_i & self.p[i].ready_o)
             with m.If(vr):
             with m.If(vr):
-                m.d.comb += eq(r_data[i], self.p[i].i_data)
+                m.d.comb += eq(r_data[i], self.p[i].data_i)
 
 
-        m.d.comb += eq(self.n.o_data, self.stage.process(r_data[mid]))
+        m.d.comb += eq(self.n.data_o, self.process(r_data[mid]))
 
         return m
 
 
 
         return m
 
 
-class InputPriorityArbiter:
+class CombMuxOutPipe(CombMultiOutPipeline):
+    def __init__(self, stage, n_len):
+        # HACK: stage is also the n-way multiplexer
+        CombMultiOutPipeline.__init__(self, stage, n_len=n_len, n_mux=stage)
+
+        # HACK: n-mux is also the stage... so set the muxid equal to input mid
+        stage.m_id = self.p.data_i.mid
+
+
+
+class InputPriorityArbiter(Elaboratable):
+    """ arbitration module for Input-Mux pipe, baed on PriorityEncoder
+    """
     def __init__(self, pipe, num_rows):
         self.pipe = pipe
         self.num_rows = num_rows
     def __init__(self, pipe, num_rows):
         self.pipe = pipe
         self.num_rows = num_rows
@@ -282,9 +327,9 @@ class InputPriorityArbiter:
         # connect priority encoder
         in_ready = []
         for i in range(self.num_rows):
         # connect priority encoder
         in_ready = []
         for i in range(self.num_rows):
-            p_i_valid = Signal(reset_less=True)
-            m.d.comb += p_i_valid.eq(self.pipe.p[i].i_valid_logic())
-            in_ready.append(p_i_valid)
+            p_valid_i = Signal(reset_less=True)
+            m.d.comb += p_valid_i.eq(self.pipe.p[i].valid_i_test)
+            in_ready.append(p_valid_i)
         m.d.comb += pe.i.eq(Cat(*in_ready)) # array of input "valids"
         m.d.comb += self.active.eq(~pe.n)   # encoder active (one input valid)
         m.d.comb += self.m_id.eq(pe.o)       # output one active input
         m.d.comb += pe.i.eq(Cat(*in_ready)) # array of input "valids"
         m.d.comb += self.active.eq(~pe.n)   # encoder active (one input valid)
         m.d.comb += self.m_id.eq(pe.o)       # output one active input
@@ -296,18 +341,18 @@ class InputPriorityArbiter:
 
 
 
 
 
 
-class ExamplePipeline(CombMultiInPipeline):
+class PriorityCombMuxInPipe(CombMultiInPipeline):
     """ an example of how to use the combinatorial pipeline.
     """
 
     """ an example of how to use the combinatorial pipeline.
     """
 
-    def __init__(self, p_len=2):
+    def __init__(self, stage, p_len=2):
         p_mux = InputPriorityArbiter(self, p_len)
         p_mux = InputPriorityArbiter(self, p_len)
-        CombMultiInPipeline.__init__(self, ExampleStage, p_len, p_mux)
+        CombMultiInPipeline.__init__(self, stage, p_len, p_mux)
 
 
 if __name__ == '__main__':
 
 
 
 if __name__ == '__main__':
 
-    dut = ExamplePipeline()
+    dut = PriorityCombMuxInPipe(ExampleStage)
     vl = rtlil.convert(dut, ports=dut.ports())
     with open("test_combpipe.il", "w") as f:
         f.write(vl)
     vl = rtlil.convert(dut, ports=dut.ports())
     with open("test_combpipe.il", "w") as f:
         f.write(vl)