example_buf_pipe.py
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Mon, 25 Mar 2019 10:35:23 +0000 (10:35 +0000)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Mon, 25 Mar 2019 10:35:23 +0000 (10:35 +0000)
p.o_ready needs to be set as a group, regardless of input mux

src/add/example_buf_pipe.py
src/add/test_buf_pipe.py

index 7b14b0a720dfbf7bc47b2ada41f7f84590032db4..c521197586932888660872977982d4bd5b8c4f72 100644 (file)
@@ -368,6 +368,7 @@ class BufferedPipeline(PipelineBase):
 
         # need an array of buffer registers conforming to *output* spec
         r_data = []
+        p_len = len(self.p)
         for i in range(len(self.p)):
             r = self.stage.ospec() # output type
             r_data.append(r)
@@ -405,21 +406,30 @@ class BufferedPipeline(PipelineBase):
                 # Flush the [already processed] buffer to the output port.
                 m.d.sync += [self.n[ni].o_valid.eq(1),      # declare reg empty
                              eq(self.n[ni].o_data, r_data[ni]), # flush buffer
-                             self.p[pi].o_ready.eq(1),      # clear stall 
                             ]
-                # ignore input, since p.o_ready is also false.
+                for i in range(p_len):
+                    m.d.sync += self.p[i].o_ready.eq(1) # clear stall
+                # ignore input, since p.o_ready is false (in current clock)
 
         # (n.i_ready) is false here: next stage is ready
         with m.Elif(o_n_validn): # next stage being told "ready"
             m.d.sync += [self.n[ni].o_valid.eq(p_i_valid),
-                         self.p[pi].o_ready.eq(1), # Keep the buffer empty
+                         self.p[pi].o_ready.eq(1),
                          eq(self.n[ni].o_data, result), # set output data
                         ]
+            for i in range(p_len):
+                m.d.sync += self.p[i].o_ready.eq(1) # Keep the buffer empty
 
         # (n.i_ready) false and (n.o_valid) true:
         with m.Elif(i_p_valid_o_p_ready):
             # If next stage *is* ready, and not stalled yet, accept input
-            m.d.sync += self.p[pi].o_ready.eq(~(p_i_valid & self.n[ni].o_valid))
+            for i in range(p_len):
+                piv = Signal(reset_less=True)
+                pnv = Signal(reset_less=True)
+                m.d.comb += [p_i_valid.eq(self.p[i].i_valid_logic()),
+                             pnv.eq(~(p_i_valid & self.n[ni].o_valid))
+                ]
+                m.d.sync += self.p[i].o_ready.eq(pnv)
 
         return m
 
@@ -530,8 +540,10 @@ class UnbufferedPipeline(PipelineBase):
             SYNCHRONOUSLY.
     """
 
-    def __init__(self, stage, p_len=1, n_len=1):
+    def __init__(self, stage, n_len=1, p_len=1, p_mux=None, n_mux=None):
         PipelineBase.__init__(self, stage, p_len, n_len)
+        self.p_mux = p_mux
+        self.n_mux = n_mux
         self._data_valid = Signal()
 
         # set up the input and output data
@@ -543,9 +555,13 @@ class UnbufferedPipeline(PipelineBase):
     def elaborate(self, platform):
         m = Module()
 
+        if self.p_mux:
+            m.submodules += self.p_mux
+
         # need an array of buffer registers conforming to *input* spec
         r_data = []
-        for i in range(len(self.p)):
+        p_len = len(self.p)
+        for i in range(p_len):
             r = self.stage.ispec() # input type
             r_data.append(r)
             if hasattr(self.stage, "setup"):
@@ -553,14 +569,21 @@ class UnbufferedPipeline(PipelineBase):
         if len(r_data) > 1:
             r_data = Array(r_data)
 
-        pi = 0 # TODO: use p_mux to decide which to select
         ni = 0 # TODO: use n_nux to decide which to select
 
-        p_i_valid = Signal(reset_less=True)
+        if self.p_mux:
+            pi = self.p_mux.mid
+            p_i_valid = self.p_mux.valid
+        else:
+            pi = 0
+            p_i_valid = Signal(reset_less=True)
+            m.d.comb += p_i_valid.eq(self.p[pi].i_valid_logic())
+
         m.d.comb += p_i_valid.eq(self.p[pi].i_valid_logic())
         m.d.comb += self.n[ni].o_valid.eq(self._data_valid)
-        m.d.comb += self.p[pi].o_ready.eq(~self._data_valid | \
-                                           self.n[ni].i_ready)
+        for i in range(p_len):
+            m.d.comb += self.p[i].o_ready.eq(~self._data_valid | \
+                                              self.n[ni].i_ready)
         m.d.sync += self._data_valid.eq(p_i_valid | \
                                     (~self.n[ni].i_ready & self._data_valid))
         with m.If(self.p[pi].i_valid & self.p[pi].o_ready):
index 0811b3a62f5bb66f5c279bc7cf25ec9f7da7c071..441e5d9441da62a8fc9efb9d6d375318b51f5360 100644 (file)
@@ -546,6 +546,86 @@ def data_2op():
             data.append(TestInputAdd(randint(0, 1<<16-1), randint(0, 1<<16-1)))
         return data
 
+class InputPriorityArbiter:
+    def __init__(self, pipe, num_rows):
+        self.pipe = pipe
+        self.num_rows = num_rows
+        self.mmax = int(log(self.num_rows) / log(2))
+        self.mid = Signal(self.mmax, reset_less=True) # multiplex id
+        self.active = Signal(reset_less=True)
+
+    def elaborate(self, platform):
+        m = Module()
+
+        assert len(self.pipe.p) == self.num_rows, \
+                "must declare input to be same size"
+        pe = PriorityEncoder(self.num_rows)
+        m.submodules.selector = pe
+
+        # 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[i].i_valid_logic())
+            in_ready.append(p_i_valid)
+        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.mid.eq(pe.o)       # output one active input
+
+        return m
+
+    def ports(self):
+        return [self.mid, self.active]
+
+
+class PriorityUnbufferedPipeline(UnbufferedPipeline):
+    def __init__(self, stage, p_len=4):
+        p_mux = InputPriorityArbiter(self, p_len)
+        UnbufferedPipeline.__init__(stage, p_len=p_len, p_mux=p_mux)
+
+    def elaborate(self, platform):
+        m = Module()
+
+        pe = PriorityEncoder(self.num_rows)
+        m.submodules.selector = pe
+        m.submodules.out_op = self.out_op
+        m.submodules += self.rs
+
+        # connect priority encoder
+        in_ready = []
+        for i in range(self.num_rows):
+            in_ready.append(self.rs[i].ready)
+        m.d.comb += pe.i.eq(Cat(*in_ready))
+
+        active = Signal(reset_less=True)
+        out_en = Signal(reset_less=True)
+        m.d.comb += active.eq(~pe.n) # encoder active
+        m.d.comb += out_en.eq(active & self.out_op.trigger)
+
+        # encoder active: ack relevant input, record MID, pass output
+        with m.If(out_en):
+            rs = self.rs[pe.o]
+            m.d.sync += self.mid.eq(pe.o)
+            m.d.sync += rs.ack.eq(0)
+            m.d.sync += self.out_op.stb.eq(0)
+            for j in range(self.num_ops):
+                m.d.sync += self.out_op.v[j].eq(rs.out_op[j])
+        with m.Else():
+            m.d.sync += self.out_op.stb.eq(1)
+            # acks all default to zero
+            for i in range(self.num_rows):
+                m.d.sync += self.rs[i].ack.eq(1)
+
+        return m
+
+    def ports(self):
+        res = []
+        for i in range(self.num_rows):
+            inop = self.rs[i]
+            res += inop.in_op + [inop.stb]
+        return self.out_op.ports() + res + [self.mid]
+
+
 
 num_tests = 100