split into separate module instead of array
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sun, 14 Apr 2019 15:36:13 +0000 (16:36 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sun, 14 Apr 2019 15:36:13 +0000 (16:36 +0100)
TLB/src/ariane/ptw.py
TLB/src/ariane/tlb.py

index 832e9bc10d481aac2fd18e2839b7307510c94688..ebb34179c5be5ecd33d642533ef7cb145681d8e6 100644 (file)
@@ -19,6 +19,7 @@ import ariane_pkg::*;
 """
 
 from nmigen import Const, Signal, Cat
+from nmigen.hdl.ast import ArrayProxy
 from math import log
 
 DCACHE_SET_ASSOC = 8
@@ -65,7 +66,15 @@ class PTE: #(RecordObject):
         return Cat(*self.ports())
 
     def eq(self, x):
-        return self.flatten().eq(x.flatten())
+        if isinstance(x, ArrayProxy):
+            res = []
+            for o in self.ports():
+                i = getattr(x, o.name)
+                res.append(i)
+            x = Cat(*res)
+        else:
+            x = x.flatten()
+        return self.flatten().eq(x)
 
     def ports(self):
         return [self.reserved, self.ppn, self.rsw, self.d, self.a, self.g,
@@ -81,6 +90,12 @@ class TLBUpdate:
         self.asid = Signal(ASID_WIDTH)
         self.content = PTE()
 
+    def flatten(self):
+        return Cat(*self.ports())
+
+    def eq(self, x):
+        return self.flatten().eq(x.flatten())
+
     def ports(self):
         return [self.valid, self.is_2M, self.is_1G, self.vpn, self.asid] + \
                 self.content.ports()
index bdd0b9aac37e4b75e7a475d0fcf2a0b0abe71341..3a66eb963926a4c2db6859e78e82975d90950914 100644 (file)
 from math import log2
 from nmigen import Signal, Module, Cat, Const, Array
 from nmigen.cli import verilog, rtlil
+from nmigen.lib.coding import Encoder
 
 from ptw import TLBUpdate, PTE, ASID_WIDTH
 
-# SV39 defines three levels of page tables
+TLB_ENTRIES = 4
+
+
 class TLBEntry:
     def __init__(self):
         self.asid = Signal(ASID_WIDTH)
-        self.vpn2 = Signal(9)
-        self.vpn1 = Signal(9)
+        # SV39 defines three levels of page tables
         self.vpn0 = Signal(9)
+        self.vpn1 = Signal(9)
+        self.vpn2 = Signal(9)
         self.is_2M = Signal()
         self.is_1G = Signal()
         self.valid = Signal()
 
-TLB_ENTRIES = 4
+    def flatten(self):
+        return Cat(*self.ports())
+
+    def eq(self, x):
+        return self.flatten().eq(x.flatten())
+
+    def ports(self):
+        return [self.asid, self.vpn0, self.vpn1, self.vpn2,
+                self.is_2M, self.is_1G, self.valid]
+
+
+class TLBContent:
+    def __init__(self):
+        self.flush_i = Signal()  # Flush signal
+        # Update TLB
+        self.update_i = TLBUpdate()
+        self.vpn2 = Signal(9)
+        self.vpn1 = Signal(9)
+        self.vpn0 = Signal(9)
+        self.lu_hit = Signal()     # to replacement logic
+        self.replace_en = Signal() # replace the following entry,
+                                   # set by replacement strategy
+        # Lookup signals
+        self.lu_asid_i = Signal(ASID_WIDTH)
+        self.lu_content_o = PTE()
+        self.lu_is_2M_o = Signal()
+        self.lu_is_1G_o = Signal()
+        self.lu_hit_o = Signal()
+
+    def elaborate(self, platform):
+        m = Module()
+
+        tags = TLBEntry()
+        content = PTE()
+
+        m.d.comb += self.lu_hit.eq(0)
+        # temporaries for 1st level match
+        asid_ok = Signal()
+        vpn2_ok = Signal()
+        tags_ok = Signal()
+        vpn2_hit = Signal()
+        m.d.comb += [tags_ok.eq(tags.valid),
+                     asid_ok.eq(tags.asid == self.lu_asid_i),
+                     vpn2_ok.eq(tags.vpn2 == self.vpn2),
+                     vpn2_hit.eq(tags_ok & asid_ok & vpn2_ok)]
+        # temporaries for 2nd level match
+        vpn1_ok = Signal()
+        tags_2M = Signal()
+        vpn0_ok = Signal()
+        vpn0_or_2M = Signal()
+        m.d.comb += [vpn1_ok.eq(self.vpn1 == tags.vpn1),
+                     tags_2M.eq(tags.is_2M),
+                     vpn0_ok.eq(self.vpn0 == tags.vpn0),
+                     vpn0_or_2M.eq(tags_2M | vpn0_ok)]
+        # first level match, this may be a giga page,
+        # check the ASID flags as well
+        with m.If(vpn2_hit):
+            # second level
+            with m.If (tags.is_1G):
+                m.d.sync += self.lu_content_o.eq(content)
+                m.d.comb += [ self.lu_is_1G_o.eq(1),
+                              self.lu_hit_o.eq(1),
+                              self.lu_hit.eq(1),
+                            ]
+            # not a giga page hit so check further
+            with m.Elif(vpn1_ok):
+                # this could be a 2 mega page hit or a 4 kB hit
+                # output accordingly
+                with m.If(vpn0_or_2M):
+                    m.d.sync += self.lu_content_o.eq(content)
+                    m.d.comb += [ self.lu_is_2M_o.eq(tags.is_2M),
+                                  self.lu_hit_o.eq(1),
+                                  self.lu_hit.eq(1),
+                                ]
+
+        # ------------------
+        # Update and Flush
+        # ------------------
+
+        replace_valid = Signal()
+        m.d.comb += replace_valid.eq(self.update_i.valid & self.replace_en)
+        with m.If (self.flush_i):
+            # invalidate (flush) conditions: all if zero or just this ASID
+            with m.If (self.lu_asid_i == Const(0, ASID_WIDTH) |
+                      (self.lu_asid_i == tags.asid)):
+                m.d.sync += tags.valid.eq(0)
+
+        # normal replacement
+        with m.Elif(replace_valid):
+            m.d.sync += [ # update tag array
+                          tags.asid.eq(self.update_i.asid),
+                          tags.vpn2.eq(self.update_i.vpn[18:27]),
+                          tags.vpn1.eq(self.update_i.vpn[9:18]),
+                          tags.vpn0.eq(self.update_i.vpn[0:9]),
+                          tags.is_1G.eq(self.update_i.is_1G),
+                          tags.is_2M.eq(self.update_i.is_2M),
+                          tags.valid.eq(1),
+                          # and content as well
+                          content.eq(self.update_i.content)
+                        ]
+
+        return m
+
+    def ports(self):
+        return [self.flush_i,
+                 self.lu_asid_i,
+                 self.lu_is_2M_o, self.lu_is_1G_o, self.lu_hit_o,
+                ] + self.update_i.content.ports() + self.update_i.ports()
 
 
 class TLB:
@@ -52,16 +163,10 @@ class TLB:
     def elaborate(self, platform):
         m = Module()
 
-        # SV39 defines three levels of page tables
-        tags = Array([TLBEntry() for i in range(TLB_ENTRIES)])
-        content = Array([PTE() for i in range(TLB_ENTRIES)])
-
         vpn2 = Signal(9)
         vpn1 = Signal(9)
         vpn0 = Signal(9)
-        lu_hit = Signal(TLB_ENTRIES)     # to replacement logic
-        replace_en = Signal(TLB_ENTRIES) # replace the following entry,
-                                         # set by replacement strategy
+
         #-------------
         # Translation
         #-------------
@@ -70,73 +175,40 @@ class TLB:
                       vpn2.eq(self.lu_vaddr_i[30:39]),
                     ]
 
+        # SV39 defines three levels of page tables
+        tc = []
         for i in range(TLB_ENTRIES):
-            m.d.comb += lu_hit[i].eq(0)
-            # temporaries for 1st level match
-            asid_ok = Signal()
-            vpn2_ok = Signal()
-            tags_ok = Signal()
-            vpn2_hit = Signal()
-            m.d.comb += [tags_ok.eq(tags[i].valid),
-                         asid_ok.eq(tags[i].asid == self.lu_asid_i),
-                         vpn2_ok.eq(tags[i].vpn2 == vpn2),
-                         vpn2_hit.eq(tags_ok & asid_ok & vpn2_ok)]
-            # temporaries for 2nd level match
-            vpn1_ok = Signal()
-            tags_2M = Signal()
-            vpn0_ok = Signal()
-            vpn0_or_2M = Signal()
-            m.d.comb += [vpn1_ok.eq(vpn1 == tags[i].vpn1),
-                         tags_2M.eq(tags[i].is_2M),
-                         vpn0_ok.eq(vpn0 == tags[i].vpn0),
-                         vpn0_or_2M.eq(tags_2M | vpn0_ok)]
-            # first level match, this may be a giga page,
-            # check the ASID flags as well
-            with m.If(vpn2_hit):
-                # second level
-                with m.If (tags[i].is_1G):
-                    m.d.sync += self.lu_content_o.eq(content[i])
-                    m.d.comb += [ self.lu_is_1G_o.eq(1),
-                                  self.lu_hit_o.eq(1),
-                                  lu_hit[i].eq(1),
-                                ]
-                # not a giga page hit so check further
-                with m.Elif(vpn1_ok):
-                    # this could be a 2 mega page hit or a 4 kB hit
-                    # output accordingly
-                    with m.If(vpn0_or_2M):
-                        m.d.sync += self.lu_content_o.eq(content[i])
-                        m.d.comb += [ self.lu_is_2M_o.eq(tags[i].is_2M),
-                                      self.lu_hit_o.eq(1),
-                                      lu_hit[i].eq(1),
-                                    ]
+            tlc = TLBContent()
+            setattr(m.submodules, "tc%d" % i, tlc)
+            tc.append(tlc)
+            m.d.comb += [tlc.vpn0.eq(vpn0),
+                         tlc.vpn1.eq(vpn1),
+                         tlc.vpn2.eq(vpn2),
+                         tlc.flush_i.eq(self.flush_i),
+                         tlc.update_i.eq(self.update_i),
+                         tlc.lu_asid_i.eq(self.lu_asid_i)
+                        ]
 
-        # ------------------
-        # Update and Flush
-        # ------------------
+        tc = Array(tc)
 
+        # use Encoder to select hit index
+        hitsel = Encoder(TLB_ENTRIES)
+        m.submodules += hitsel
+
+        hits = []
         for i in range(TLB_ENTRIES):
-            replace_valid = Signal()
-            m.d.comb += replace_valid.eq(self.update_i.valid & replace_en[i])
-            with m.If (self.flush_i):
-                # invalidate (flush) conditions: all if zero or just this ASID
-                with m.If (self.lu_asid_i == Const(0, ASID_WIDTH) |
-                          (self.lu_asid_i == tags[i].asid)):
-                    m.d.sync += tags[i].valid.eq(0)
-
-            # normal replacement
-            with m.Elif(replace_valid):
-                m.d.sync += [ # update tag array
-                              tags[i].asid.eq(self.update_i.asid),
-                              tags[i].vpn2.eq(self.update_i.vpn[18:27]),
-                              tags[i].vpn1.eq(self.update_i.vpn[9:18]),
-                              tags[i].vpn0.eq(self.update_i.vpn[0:9]),
-                              tags[i].is_1G.eq(self.update_i.is_1G),
-                              tags[i].is_2M.eq(self.update_i.is_2M),
-                              tags[i].valid.eq(1),
-                              # and content as well
-                              content[i].eq(self.update_i.content)
-                            ]
+            hits.append(tc[i].lu_hit)
+        m.d.comb += hitsel.i.eq(Cat(*hits))
+        idx = hitsel.o
+
+        active = Signal()
+        m.d.comb += active.eq(~hitsel.n)
+        with m.If(active):
+            m.d.comb += [ self.lu_is_1G_o.eq(tc[idx].lu_is_1G_o),
+                          self.lu_is_2M_o.eq(tc[idx].lu_is_2M_o),
+                          self.lu_hit_o.eq(1),
+                          self.lu_content_o.eq(tc[idx].lu_content_o)
+                        ]
 
         # -----------------------------------------------
         # PLRU - Pseudo Least Recently Used Replacement
@@ -172,7 +244,7 @@ class TLB:
         for i in range(TLB_ENTRIES):
             # we got a hit so update the pointer as it was least recently used
             hit = Signal()
-            m.d.comb += hit.eq(lu_hit[i] & self.lu_access_i)
+            m.d.comb += hit.eq(tc[i].lu_hit & self.lu_access_i)
             with m.If(hit):
                 # Set the nodes to the values we would expect
                 for lvl in range(LOG_TLB):
@@ -215,7 +287,7 @@ class TLB:
             print ("plru", i, en)
             # boolean logic manipluation:
             # plur0 & plru1 & plur2 == ~(~plru0 | ~plru1 | ~plru2)
-            m.d.sync += replace_en[i].eq(~Cat(*en).bool())
+            m.d.sync += tc[i].replace_en.eq(~Cat(*en).bool())
 
         #--------------
         # Sanity checks