add indices output to MultiPriorityPicker
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 17 Mar 2020 21:36:50 +0000 (21:36 +0000)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 17 Mar 2020 21:36:50 +0000 (21:36 +0000)
src/nmutil/picker.py

index 844e7b0a4d80089082708e1427ff890d584b7c29..0e50fd24f1131cb40ff306030f0dcfb107d0c22e 100644 (file)
@@ -1,12 +1,27 @@
 """ Priority Picker: optimised back-to-back PriorityEncoder and Decoder
     and MultiPriorityPicker: cascading mutually-exclusive pickers
 
 """ Priority Picker: optimised back-to-back PriorityEncoder and Decoder
     and MultiPriorityPicker: cascading mutually-exclusive pickers
 
-    The input is N bits, the output is N bits wide and only one is
-    enabled.
+    PriorityPicker: the input is N bits, the output is N bits wide and
+    only one is enabled.
+
+    MultiPriorityPicker: likewise except that there are M pickers and
+    each output is guaranteed mutually exclusive.  Optionally:
+    an "index" (and enable line) is also outputted.
+
+    MultiPriorityPicker is designed for port-selection, when there are
+    multiple "things" (of width N) contending for access to M "ports".
+    When the M=0 "thing" requests a port, it gets allocated port 0
+    (always).  However if the M=0 "thing" does *not* request a port,
+    this gives the M=1 "thing" the opportunity to gain access to port 0.
+
+    Given that N may potentially be much greater than M (16 bits wide
+    where M may be e.g. only 4) we can't just ok, "ok so M=N therefore
+    M=0 gets access to port 0, M=1 gets access to port 1" etc.
 """
 
 """
 
-from nmigen import Module, Signal, Cat, Elaboratable, Array, Const
+from nmigen import Module, Signal, Cat, Elaboratable, Array, Const, Mux
 from nmigen.cli import verilog, rtlil
 from nmigen.cli import verilog, rtlil
+import math
 
 class PriorityPicker(Elaboratable):
     """ implements a priority-picker.  input: N bits, output: N bits
 
 class PriorityPicker(Elaboratable):
     """ implements a priority-picker.  input: N bits, output: N bits
@@ -16,6 +31,7 @@ class PriorityPicker(Elaboratable):
         # inputs
         self.i = Signal(wid, reset_less=True)
         self.o = Signal(wid, reset_less=True)
         # inputs
         self.i = Signal(wid, reset_less=True)
         self.o = Signal(wid, reset_less=True)
+        self.en_o = Signal(reset_less=True) # true if any output is true
 
     def elaborate(self, platform):
         m = Module()
 
     def elaborate(self, platform):
         m = Module()
@@ -33,6 +49,7 @@ class PriorityPicker(Elaboratable):
 
         # we like Cat(*xxx).  turn lists into concatenated bits
         m.d.comb += self.o.eq(Cat(*res))
 
         # we like Cat(*xxx).  turn lists into concatenated bits
         m.d.comb += self.o.eq(Cat(*res))
+        m.d.comb += self.en_o.eq(self.o.bool()) # true if 1 input is true
 
         return m
 
 
         return m
 
@@ -52,14 +69,17 @@ class MultiPriorityPicker(Elaboratable):
         gets top priority, the second cannot have the same bit that
         the first has set, and so on.  To do this, a "mask" accumulates
         the output from the chain, masking the input to the next chain.
         gets top priority, the second cannot have the same bit that
         the first has set, and so on.  To do this, a "mask" accumulates
         the output from the chain, masking the input to the next chain.
+
+        Also outputted (optional): an index for each picked "thing".
     """
     """
-    def __init__(self, wid, levels):
+    def __init__(self, wid, levels, indices=False):
         self.levels = levels
         self.wid = wid
         self.levels = levels
         self.wid = wid
+        self.indices = indices
 
 
+        # create array of input and output (unary)
         self.i = [] # store the array of picker inputs
         self.o = [] # store the array of picker outputs
         self.i = [] # store the array of picker inputs
         self.o = [] # store the array of picker outputs
-
         for j in range(self.levels):
             i = Signal(self.wid, name="i_%d" % j, reset_less=True)
             o = Signal(self.wid, name="o_%d" % j, reset_less=True)
         for j in range(self.levels):
             i = Signal(self.wid, name="i_%d" % j, reset_less=True)
             o = Signal(self.wid, name="o_%d" % j, reset_less=True)
@@ -68,15 +88,29 @@ class MultiPriorityPicker(Elaboratable):
         self.i = Array(self.i)
         self.o = Array(self.o)
 
         self.i = Array(self.i)
         self.o = Array(self.o)
 
+        if not self.indices:
+            return
+
+        # add an array of "enables" and indices
+        self.en_o = Signal(self.levels, name="en_o", reset_less=True)
+        lidx = math.ceil(math.log2(self.levels+1))
+        idx_o = [] # store the array of indices
+        for j in range(self.levels):
+            i = Signal(lidx, name="idxo_%d" % j, reset_less=True)
+            idx_o.append(i)
+        self.idx_o = Array(idx_o)
+
     def elaborate(self, platform):
         m = Module()
         comb = m.d.comb
 
         prev_pp = None
         p_mask = None
     def elaborate(self, platform):
         m = Module()
         comb = m.d.comb
 
         prev_pp = None
         p_mask = None
+        pp_l = []
         for j in range(self.levels):
             o, i = self.o[j], self.i[j]
             pp = PriorityPicker(self.wid)
         for j in range(self.levels):
             o, i = self.o[j], self.i[j]
             pp = PriorityPicker(self.wid)
+            pp_l.append(pp)
             setattr(m.submodules, "pp%d" % j, pp)
             comb += o.eq(pp.o)
             if prev_pp is None:
             setattr(m.submodules, "pp%d" % j, pp)
             comb += o.eq(pp.o)
             if prev_pp is None:
@@ -89,6 +123,27 @@ class MultiPriorityPicker(Elaboratable):
                 p_mask = mask
             prev_pp = pp
 
                 p_mask = mask
             prev_pp = pp
 
+        if not self.indices:
+            return m
+
+        # for each picker enabled, pass that out and set a cascading index
+        lidx = math.ceil(math.log2(self.levels+1))
+        en_l = []
+        prev_count = None
+        for j in range(self.levels):
+            en_o = pp_l[j].en_o
+            en_l.append(en_o)
+            count1 = Signal(lidx, name="count_%d" % j, reset_less=True)
+            if prev_count is None:
+                comb += self.idx_o[j].eq(Mux(en_o, 1, 0))
+            else:
+                comb += count1.eq(prev_count + Const(1, lidx))
+                comb += self.idx_o[j].eq(Mux(en_o, count1, prev_count))
+            prev_count = self.idx_o[j]
+
+        # concat accumulated enable bits
+        comb += self.en_o.eq(Cat(*en_o))
+
         return m
 
     def __iter__(self):
         return m
 
     def __iter__(self):
@@ -100,7 +155,7 @@ class MultiPriorityPicker(Elaboratable):
 
 
 if __name__ == '__main__':
 
 
 if __name__ == '__main__':
-    dut = MultiPriorityPicker(5, 4)
+    dut = MultiPriorityPicker(5, 4, True)
     vl = rtlil.convert(dut, ports=dut.ports())
     with open("test_multi_picker.il", "w") as f:
         f.write(vl)
     vl = rtlil.convert(dut, ports=dut.ports())
     with open("test_multi_picker.il", "w") as f:
         f.write(vl)