From 36afa7971d3df7337d45decb05b0702f353df67b Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Sun, 14 Apr 2019 16:36:13 +0100 Subject: [PATCH] split into separate module instead of array --- TLB/src/ariane/ptw.py | 17 +++- TLB/src/ariane/tlb.py | 224 ++++++++++++++++++++++++++++-------------- 2 files changed, 164 insertions(+), 77 deletions(-) diff --git a/TLB/src/ariane/ptw.py b/TLB/src/ariane/ptw.py index 832e9bc1..ebb34179 100644 --- a/TLB/src/ariane/ptw.py +++ b/TLB/src/ariane/ptw.py @@ -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() diff --git a/TLB/src/ariane/tlb.py b/TLB/src/ariane/tlb.py index bdd0b9aa..3a66eb96 100644 --- a/TLB/src/ariane/tlb.py +++ b/TLB/src/ariane/tlb.py @@ -18,21 +18,132 @@ 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 -- 2.30.2