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:
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
#-------------
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
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):
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