create MultiPriorityPicker which can mutually-exclusively select M outputs from
[nmutil.git] / src / nmutil / picker.py
index d47f785ed55077fccd7dc801ea103dbf42277bfe..844e7b0a4d80089082708e1427ff890d584b7c29 100644 (file)
@@ -1,10 +1,12 @@
 """ 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.
 """
 
-from nmigen import Module, Signal, Cat, Elaboratable
+from nmigen import Module, Signal, Cat, Elaboratable, Array, Const
+from nmigen.cli import verilog, rtlil
 
 class PriorityPicker(Elaboratable):
     """ implements a priority-picker.  input: N bits, output: N bits
@@ -40,3 +42,65 @@ class PriorityPicker(Elaboratable):
 
     def ports(self):
         return list(self)
+
+
+class MultiPriorityPicker(Elaboratable):
+    """ implements a multi-input priority picker
+        Mx inputs of N bits, Mx outputs of N bits, only one is set
+
+        Each picker masks out the one below it, such that the first
+        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.
+    """
+    def __init__(self, wid, levels):
+        self.levels = levels
+        self.wid = wid
+
+        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)
+            self.i.append(i)
+            self.o.append(o)
+        self.i = Array(self.i)
+        self.o = Array(self.o)
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+
+        prev_pp = None
+        p_mask = None
+        for j in range(self.levels):
+            o, i = self.o[j], self.i[j]
+            pp = PriorityPicker(self.wid)
+            setattr(m.submodules, "pp%d" % j, pp)
+            comb += o.eq(pp.o)
+            if prev_pp is None:
+                comb += pp.i.eq(i)
+                p_mask = Const(0, self.wid)
+            else:
+                mask = Signal(self.wid, name="m_%d" % j, reset_less=True)
+                comb += mask.eq(prev_pp.o | p_mask) # accumulate output bits
+                comb += pp.i.eq(i & ~mask)          # mask out input
+                p_mask = mask
+            prev_pp = pp
+
+        return m
+
+    def __iter__(self):
+        yield from self.i
+        yield from self.o
+
+    def ports(self):
+        return list(self)
+
+
+if __name__ == '__main__':
+    dut = MultiPriorityPicker(5, 4)
+    vl = rtlil.convert(dut, ports=dut.ports())
+    with open("test_multi_picker.il", "w") as f:
+        f.write(vl)