speed up ==, hash, <, >, <=, and >= for plain_data
[nmutil.git] / src / nmutil / plru.py
index a677b946773770a2ce9afe21c65cb27780d8aefb..265af7fbd995a9fba1b32d0c678abfc9be5e15ea 100644 (file)
@@ -1,13 +1,18 @@
 # based on ariane plru, from tlb.sv
 
-from nmigen import Signal, Module, Cat, Const, Repl
+# old PLRU API, once all users have migrated to new API in plru2.py, then
+# plru2.py will be renamed to plru.py, replacing this file.
+
+from nmigen import Signal, Module, Cat, Const, Repl, Array
 from nmigen.hdl.ir import Elaboratable
 from nmigen.cli import rtlil
 from nmigen.utils import log2_int
+from nmigen.lib.coding import Decoder
+from warnings import warn
 
 
 class PLRU(Elaboratable):
-    """ PLRU - Pseudo Least Recently Used Replacement
+    r""" PLRU - Pseudo Least Recently Used Replacement
 
         PLRU-tree indexing:
         lvl0        0
@@ -21,17 +26,24 @@ class PLRU(Elaboratable):
     """
 
     def __init__(self, BITS):
+        warn("nmutil.plru.PLRU is deprecated due to having a broken API, use "
+             "nmutil.plru2.PLRU instead", DeprecationWarning, stacklevel=2)
         self.BITS = BITS
-        self.acc_en = Signal(BITS)
-        self.acc_i = Signal()
+        self.acc_i = Signal(BITS)
+        self.acc_en = Signal()
         self.lru_o = Signal(BITS)
 
+        self._plru_tree = Signal(self.TLBSZ)
+        """ exposed only for testing """
+
+    @property
+    def TLBSZ(self):
+        return 2 * (self.BITS - 1)
+
     def elaborate(self, platform=None):
         m = Module()
 
         # Tree (bit per entry)
-        TLBSZ = 2*(self.BITS-1)
-        plru_tree = Signal(TLBSZ)
 
         # Just predefine which nodes will be set/cleared
         # E.g. for a TLB with 8 entries, the for-loop is semantically
@@ -50,7 +62,7 @@ class PLRU(Elaboratable):
 
         LOG_TLB = log2_int(self.BITS, False)
         hit = Signal(self.BITS, reset_less=True)
-        m.d.comb += hit.eq(Repl(self.acc_i, self.BITS) & self.acc_en)
+        m.d.comb += hit.eq(Repl(self.acc_en, self.BITS) & self.acc_i)
 
         for i in range(self.BITS):
             # we got a hit so update the pointer as it was least recently used
@@ -62,9 +74,9 @@ class PLRU(Elaboratable):
                     shift = LOG_TLB - lvl
                     new_idx = Const(~((i >> (shift-1)) & 1), 1)
                     plru_idx = idx_base + (i >> shift)
-                    #print("plru", i, lvl, hex(idx_base),
+                    # print("plru", i, lvl, hex(idx_base),
                     #      plru_idx, shift, new_idx)
-                    m.d.sync += plru_tree[plru_idx].eq(new_idx)
+                    m.d.sync += self._plru_tree[plru_idx].eq(new_idx)
 
         # Decode tree to write enable signals
         # Next for-loop basically creates the following logic for e.g.
@@ -90,9 +102,9 @@ class PLRU(Elaboratable):
                 new_idx = (i >> (shift-1)) & 1
                 plru_idx = idx_base + (i >> shift)
                 plru = Signal(reset_less=True,
-                              name="plru-%d-%d-%d-%d" % \
-                                    (i, lvl, plru_idx, new_idx))
-                m.d.comb += plru.eq(plru_tree[plru_idx])
+                              name="plru-%d-%d-%d-%d" %
+                              (i, lvl, plru_idx, new_idx))
+                m.d.comb += plru.eq(self._plru_tree[plru_idx])
                 if new_idx:
                     en.append(~plru)  # yes inverted (using bool() below)
                 else:
@@ -109,8 +121,59 @@ class PLRU(Elaboratable):
         return [self.acc_en, self.lru_o, self.acc_i]
 
 
+class PLRUs(Elaboratable):
+    def __init__(self, n_plrus, n_bits):
+        warn("nmutil.plru.PLRUs is deprecated due to having a broken API, use "
+             "nmutil.plru2.PLRUs instead", DeprecationWarning, stacklevel=2)
+        self.n_plrus = n_plrus
+        self.n_bits = n_bits
+        self.valid = Signal()
+        self.way = Signal(n_bits)
+        self.index = Signal(n_plrus.bit_length())
+        self.isel = Signal(n_plrus.bit_length())
+        self.o_index = Signal(n_bits)
+
+    def elaborate(self, platform):
+        """Generate TLB PLRUs
+        """
+        m = Module()
+        comb = m.d.comb
+
+        if self.n_plrus == 0:
+            return m
+
+        # Binary-to-Unary one-hot, enabled by valid
+        m.submodules.te = te = Decoder(self.n_plrus)
+        comb += te.n.eq(~self.valid)
+        comb += te.i.eq(self.index)
+
+        out = Array(Signal(self.n_bits, name="plru_out%d" % x)
+                    for x in range(self.n_plrus))
+
+        for i in range(self.n_plrus):
+            # PLRU interface
+            m.submodules["plru_%d" % i] = plru = PLRU(self.n_bits)
+
+            comb += plru.acc_en.eq(te.o[i])
+            comb += plru.acc_i.eq(self.way)
+            comb += out[i].eq(plru.lru_o)
+
+        # select output based on index
+        comb += self.o_index.eq(out[self.isel])
+
+        return m
+
+    def ports(self):
+        return [self.valid, self.way, self.index, self.isel, self.o_index]
+
+
 if __name__ == '__main__':
-    dut = PLRU(8)
+    dut = PLRU(3)
     vl = rtlil.convert(dut, ports=dut.ports())
     with open("test_plru.il", "w") as f:
         f.write(vl)
+
+    dut = PLRUs(4, 2)
+    vl = rtlil.convert(dut, ports=dut.ports())
+    with open("test_plrus.il", "w") as f:
+        f.write(vl)