move code around to get set associative cache working
[soc.git] / src / TLB / ariane / tlb_content.py
1 from nmigen import Signal, Module, Cat, Const
2
3 from ptw import TLBUpdate, PTE
4
5 class TLBEntry:
6 def __init__(self, asid_width):
7 self.asid = Signal(asid_width)
8 # SV39 defines three levels of page tables
9 self.vpn0 = Signal(9)
10 self.vpn1 = Signal(9)
11 self.vpn2 = Signal(9)
12 self.is_2M = Signal()
13 self.is_1G = Signal()
14 self.valid = Signal()
15
16 def flatten(self):
17 return Cat(*self.ports())
18
19 def eq(self, x):
20 return self.flatten().eq(x.flatten())
21
22 def ports(self):
23 return [self.asid, self.vpn0, self.vpn1, self.vpn2,
24 self.is_2M, self.is_1G, self.valid]
25
26 class TLBContent:
27 def __init__(self, pte_width, asid_width):
28 self.asid_width = asid_width
29 self.pte_width = pte_width
30 self.flush_i = Signal() # Flush signal
31 # Update TLB
32 self.update_i = TLBUpdate(asid_width)
33 self.vpn2 = Signal(9)
34 self.vpn1 = Signal(9)
35 self.vpn0 = Signal(9)
36 self.replace_en_i = Signal() # replace the following entry,
37 # set by replacement strategy
38 # Lookup signals
39 self.lu_asid_i = Signal(asid_width)
40 self.lu_content_o = Signal(pte_width)
41 self.lu_is_2M_o = Signal()
42 self.lu_is_1G_o = Signal()
43 self.lu_hit_o = Signal()
44
45 def elaborate(self, platform):
46 m = Module()
47
48 tags = TLBEntry(self.asid_width)
49 content = Signal(self.pte_width)
50
51 m.d.comb += [self.lu_hit_o.eq(0),
52 self.lu_is_2M_o.eq(0),
53 self.lu_is_1G_o.eq(0)]
54
55 # temporaries for 1st level match
56 asid_ok = Signal(reset_less=True)
57 vpn2_ok = Signal(reset_less=True)
58 tags_ok = Signal(reset_less=True)
59 vpn2_hit = Signal(reset_less=True)
60 m.d.comb += [tags_ok.eq(tags.valid),
61 asid_ok.eq(tags.asid == self.lu_asid_i),
62 vpn2_ok.eq(tags.vpn2 == self.vpn2),
63 vpn2_hit.eq(tags_ok & asid_ok & vpn2_ok)]
64 # temporaries for 2nd level match
65 vpn1_ok = Signal(reset_less=True)
66 tags_2M = Signal(reset_less=True)
67 vpn0_ok = Signal(reset_less=True)
68 vpn0_or_2M = Signal(reset_less=True)
69 m.d.comb += [vpn1_ok.eq(self.vpn1 == tags.vpn1),
70 tags_2M.eq(tags.is_2M),
71 vpn0_ok.eq(self.vpn0 == tags.vpn0),
72 vpn0_or_2M.eq(tags_2M | vpn0_ok)]
73 # first level match, this may be a giga page,
74 # check the ASID flags as well
75 with m.If(vpn2_hit):
76 # second level
77 with m.If (tags.is_1G):
78 m.d.comb += [ self.lu_content_o.eq(content),
79 self.lu_is_1G_o.eq(1),
80 self.lu_hit_o.eq(1),
81 ]
82 # not a giga page hit so check further
83 with m.Elif(vpn1_ok):
84 # this could be a 2 mega page hit or a 4 kB hit
85 # output accordingly
86 with m.If(vpn0_or_2M):
87 m.d.comb += [ self.lu_content_o.eq(content),
88 self.lu_is_2M_o.eq(tags.is_2M),
89 self.lu_hit_o.eq(1),
90 ]
91 # ------------------
92 # Update or Flush
93 # ------------------
94
95 # temporaries
96 replace_valid = Signal(reset_less=True)
97 m.d.comb += replace_valid.eq(self.update_i.valid & self.replace_en_i)
98
99 # flush
100 with m.If (self.flush_i):
101 # invalidate (flush) conditions: all if zero or just this ASID
102 with m.If (self.lu_asid_i == Const(0, self.asid_width) |
103 (self.lu_asid_i == tags.asid)):
104 m.d.sync += tags.valid.eq(0)
105
106 # normal replacement
107 with m.Elif(replace_valid):
108 m.d.sync += [ # update tag array
109 tags.asid.eq(self.update_i.asid),
110 tags.vpn2.eq(self.update_i.vpn[18:27]),
111 tags.vpn1.eq(self.update_i.vpn[9:18]),
112 tags.vpn0.eq(self.update_i.vpn[0:9]),
113 tags.is_1G.eq(self.update_i.is_1G),
114 tags.is_2M.eq(self.update_i.is_2M),
115 tags.valid.eq(1),
116 # and content as well
117 content.eq(self.update_i.content.flatten())
118 ]
119 return m
120
121 def ports(self):
122 return [self.flush_i,
123 self.lu_asid_i,
124 self.lu_is_2M_o, self.lu_is_1G_o, self.lu_hit_o,
125 ] + self.update_i.content.ports() + self.update_i.ports()