self.r = Signal()
self.v = Signal()
+ def eq(self, x):
+ return [self.reserved.eq(x.reserved),
+ self.ppn.eq(x.ppn), self.rsw.eq(x.rsw),
+ self.d.eq(x.d), self.a.eq(x.a), self.g.eq(x.g),
+ self.u.eq(x.u), self.x.eq(x.x), self.w.eq(x.w),
+ self.r.eq(x.r), self.v.eq(x.v)]
+
+ def ports(self):
+ return [self.reserved, self.ppn, self.rsw, self.d, self.a, self.g,
+ self.u, self.x, self.w, self.r, self.v]
+
class TLBUpdate:
def __init__(self):
- valid = Signal() # valid flag
- is_2M = Signal()
- is_1G = Signal()
- vpn = Signal(27)
- asid = Signal(ASID_WIDTH)
- content = PTE()
+ self.valid = Signal() # valid flag
+ self.is_2M = Signal()
+ self.is_1G = Signal()
+ self.vpn = Signal(27)
+ self.asid = Signal(ASID_WIDTH)
+ self.content = PTE()
+
+ def ports(self):
+ return [self.valid, self.is_2M, self.is_1G, self.vpn, self.asid,
+ self.content.ports()]
# SV39 defines three levels of page tables
LVL1 = Const(0, 2)
data_rvalid.eq(req_port_i.data_rvalid)
]
+"""
+if __name__ == '__main__':
+ dut = PTE()
+ ports = [dut.p.i_valid, dut.n.i_ready,
+ dut.n.o_valid, dut.p.o_ready] + \
+ [dut.p.i_data] + [dut.n.o_data]
+ vl = rtlil.convert(dut, ports=ports)
+ with open("test_bufunbuf999.il", "w") as f:
+ f.write(vl)
+"""
# Description: Translation Lookaside Buffer, SV39
# fully set-associative
"""
-from math import log
+from math import log2
+from nmigen import Signal, Module, Cat, Const, Array
+from nmigen.cli import verilog, rtlil
+
# SV39 defines three levels of page tables
class TLBEntry:
from ptw import TLBUpdate, PTE
+
class TLB:
def __init__(self):
self.flush_i = Signal() # Flush signal
def elaborate(self, platform):
m = Module()
- tags = TLBEntry()
# SV39 defines three levels of page tables
-
- content = Array([TLB() for i in range(TLB_ENTRIES)])
+ tags = Array([TLBEntry() for i in range(TLB_ENTRIES)])
+ content = Array([PTE() for i in range(TLB_ENTRIES)])
vpn2 = Signal(9)
vpn1 = Signal(9)
#-------------
# Translation
#-------------
- m.d.comb += [ vpn0.eq(lu_vaddr_i[12:21]),
- vpn1.eq(lu_vaddr_i[21:30]),
- vpn2.eq(lu_vaddr_i[30:39]),
+ m.d.comb += [ vpn0.eq(self.lu_vaddr_i[12:21]),
+ vpn1.eq(self.lu_vaddr_i[21:30]),
+ vpn2.eq(self.lu_vaddr_i[30:39]),
]
for i in range(TLB_ENTRIES):
# first level match, this may be a giga page,
# check the ASID flags as well
with m.If(tags[i].valid & \
- tags[i].asid == lu_asid_i & \
- tags[i].vpn2 == vpn2):
+ (tags[i].asid == self.lu_asid_i) & \
+ (tags[i].vpn2 == vpn2)):
# second level
with m.If (tags[i].is_1G):
- m.d.sync += lu_content_o.eq(content[i])
- m.d.comb += [ lu_is_1G_o.eq(1),
- lu_hit_o.eq(1),
+ 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 == tags[i].vpn1):
# this could be a 2 mega page hit or a 4 kB hit
# output accordingly
- with m.If(tags[i].is_2M | vpn0 == tags[i].vpn0):
- m.d.sync += lu_content_o.eq(content[i])
- m.d.comb += [ lu_is_2M_o.eq(tags[i].is_2M),
- lu_hit_o.eq(1),
+ with m.If(tags[i].is_2M | (vpn0 == tags[i].vpn0)):
+ 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),
]
# ------------------
for i in range(TLB_ENTRIES):
- with m.If (flush_i):
+ with m.If (self.flush_i):
# invalidate (flush) conditions: all if zero or just this ASID
- with m.If (lu_asid_i == Const(0, ASID_WIDTH) |
- (lu_asid_i == tags[i].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(update_i.valid & replace_en[i]):
+ with m.Elif(self.update_i.valid & replace_en[i]):
m.d.sync += [ # update tag array
- tags[i].asid.eq(update_i.asid),
- tags[i].vpn2.eq(update_i.vpn [18:27]),
- tags[i].vpn1.eq(update_i.vpn [9:18]),
- tags[i].vpn0.eq(update_i.vpn[0:9]),
- tags[i].is_1G.eq(update_i.is_1G),
- tags[i].is_2M.eq(update_i.is_2M),
+ 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(update_i.content)
+ content[i].eq(self.update_i.content)
]
# -----------------------------------------------
LOG_TLB = int(log2(TLB_ENTRIES))
for i in range(TLB_ENTRIES):
# we got a hit so update the pointer as it was least recently used
- with m.If (lu_hit[i] & lu_access_i):
+ with m.If (lu_hit[i] & self.lu_access_i):
# Set the nodes to the values we would expect
for lvl in range(LOG_TLB):
idx_base = (1<<lvl)-1
assert (ASID_WIDTH >= 1), \
"ASID width must be at least 1"
+ return m
+
"""
# Just for checking
function int countSetBits(logic[TLB_ENTRIES-1:0] vector);
else $error("More then one TLB entry selected for next replace!");
"""
+ def ports(self):
+ return [self.flush_i, self.lu_access_i,
+ self.lu_asid_i, self.lu_vaddr_i,
+ self.lu_is_2M_o, self.lu_is_1G_o, self.lu_hit_o,
+ ] + self.lu_content_o.ports() + self.update_i.ports()
+
+if __name__ == '__main__':
+ tlb = TLB()
+ vl = rtlil.convert(tlb, ports=tlb.ports())
+ with open("test_tlb.il", "w") as f:
+ f.write(vl)
+