add is_512G to the data structure
[soc.git] / src / TLB / ariane / tlb.py
1 """
2 # Copyright 2018 ETH Zurich and University of Bologna.
3 # Copyright and related rights are licensed under the Solderpad Hardware
4 # License, Version 0.51 (the "License"); you may not use this file except in
5 # compliance with the License. You may obtain a copy of the License at
6 # http:#solderpad.org/licenses/SHL-0.51. Unless required by applicable law
7 # or agreed to in writing, software, hardware and materials distributed under
8 # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
9 # CONDITIONS OF ANY KIND, either express or implied. See the License for the
10 # specific language governing permissions and limitations under the License.
11 #
12 # Author: David Schaffenrath, TU Graz
13 # Author: Florian Zaruba, ETH Zurich
14 # Date: 21.4.2017
15 # Description: Translation Lookaside Buffer, SV48
16 # fully set-associative
17
18 Implementation in c++:
19 https://raw.githubusercontent.com/Tony-Hu/TreePLRU/master/TreePLRU.cpp
20
21 Text description:
22 https://people.cs.clemson.edu/~mark/464/p_lru.txt
23
24 Online simulator:
25 http://www.ntu.edu.sg/home/smitha/ParaCache/Paracache/vm.html
26 """
27 from math import log2
28 from nmigen import Signal, Module, Cat, Const, Array, Elaboratable
29 from nmigen.cli import verilog, rtlil
30 from nmigen.lib.coding import Encoder
31
32 from TLB.ariane.ptw import TLBUpdate, PTE, ASID_WIDTH
33 from TLB.ariane.plru import PLRU
34 from TLB.ariane.tlb_content import TLBContent
35
36 TLB_ENTRIES = 8
37
38 class TLB(Elaboratable):
39 def __init__(self, tlb_entries=8, asid_width=8):
40 self.tlb_entries = tlb_entries
41 self.asid_width = asid_width
42
43 self.flush_i = Signal() # Flush signal
44 # Lookup signals
45 self.lu_access_i = Signal()
46 self.lu_asid_i = Signal(self.asid_width)
47 self.lu_vaddr_i = Signal(64)
48 self.lu_content_o = PTE()
49 self.lu_is_2M_o = Signal()
50 self.lu_is_1G_o = Signal()
51 self.lu_is_512G_o = Signal()
52 self.lu_hit_o = Signal()
53 # Update TLB
54 self.pte_width = len(self.lu_content_o.flatten())
55 self.update_i = TLBUpdate(asid_width)
56
57 def elaborate(self, platform):
58 m = Module()
59
60 vpn3 = Signal(9) #FIXME unused signal
61 vpn2 = Signal(9)
62 vpn1 = Signal(9)
63 vpn0 = Signal(9)
64
65 #-------------
66 # Translation
67 #-------------
68
69 # SV48 defines four levels of page tables
70 m.d.comb += [ vpn0.eq(self.lu_vaddr_i[12:21]),
71 vpn1.eq(self.lu_vaddr_i[21:30]),
72 vpn2.eq(self.lu_vaddr_i[30:39]),
73 vpn3.eq(self.lu_vaddr_i[39:48]), ### FIXME
74 ]
75
76 tc = []
77 for i in range(self.tlb_entries):
78 tlc = TLBContent(self.pte_width, self.asid_width)
79 setattr(m.submodules, "tc%d" % i, tlc)
80 tc.append(tlc)
81 # connect inputs
82 tlc.update_i = self.update_i # saves a lot of graphviz links
83 m.d.comb += [tlc.vpn0.eq(vpn0),
84 tlc.vpn1.eq(vpn1),
85 tlc.vpn2.eq(vpn2),
86 # TODO 4th
87 tlc.flush_i.eq(self.flush_i),
88 #tlc.update_i.eq(self.update_i),
89 tlc.lu_asid_i.eq(self.lu_asid_i)]
90 tc = Array(tc)
91
92 #--------------
93 # Select hit
94 #--------------
95
96 # use Encoder to select hit index
97 # XXX TODO: assert that there's only one valid entry (one lu_hit)
98 hitsel = Encoder(self.tlb_entries)
99 m.submodules.hitsel = hitsel
100
101 hits = []
102 for i in range(self.tlb_entries):
103 hits.append(tc[i].lu_hit_o)
104 m.d.comb += hitsel.i.eq(Cat(*hits)) # (goes into plru as well)
105 idx = hitsel.o
106
107 active = Signal(reset_less=True)
108 m.d.comb += active.eq(~hitsel.n)
109 with m.If(active):
110 # active hit, send selected as output
111 m.d.comb += [ self.lu_is_512G_o.eq(tc[idx].lu_is_512G_o),
112 self.lu_is_1G_o.eq(tc[idx].lu_is_1G_o),
113 self.lu_is_2M_o.eq(tc[idx].lu_is_2M_o),
114 self.lu_hit_o.eq(1),
115 self.lu_content_o.flatten().eq(tc[idx].lu_content_o),
116 ]
117
118 #--------------
119 # PLRU.
120 #--------------
121
122 p = PLRU(self.tlb_entries)
123 plru_tree = Signal(p.TLBSZ)
124 m.submodules.plru = p
125
126 # connect PLRU inputs/outputs
127 # XXX TODO: assert that there's only one valid entry (one replace_en)
128 en = []
129 for i in range(self.tlb_entries):
130 en.append(tc[i].replace_en_i)
131 m.d.comb += [Cat(*en).eq(p.replace_en_o), # output from PLRU into tags
132 p.lu_hit.eq(hitsel.i),
133 p.lu_access_i.eq(self.lu_access_i),
134 p.plru_tree.eq(plru_tree)]
135 m.d.sync += plru_tree.eq(p.plru_tree_o)
136
137 #--------------
138 # Sanity checks
139 #--------------
140
141 assert (self.tlb_entries % 2 == 0) and (self.tlb_entries > 1), \
142 "TLB size must be a multiple of 2 and greater than 1"
143 assert (self.asid_width >= 1), \
144 "ASID width must be at least 1"
145
146 return m
147
148 """
149 # Just for checking
150 function int countSetBits(logic[self.tlb_entries-1:0] vector);
151 automatic int count = 0;
152 foreach (vector[idx]) begin
153 count += vector[idx];
154 end
155 return count;
156 endfunction
157
158 assert property (@(posedge clk_i)(countSetBits(lu_hit) <= 1))
159 else $error("More then one hit in TLB!"); $stop(); end
160 assert property (@(posedge clk_i)(countSetBits(replace_en) <= 1))
161 else $error("More then one TLB entry selected for next replace!");
162 """
163
164 def ports(self):
165 return [self.flush_i, self.lu_access_i,
166 self.lu_asid_i, self.lu_vaddr_i,
167 self.lu_is_2M_o, self.lu_1G_o, self.lu_is_512G_o, self.lu_hit_o
168 ] + self.lu_content_o.ports() + self.update_i.ports()
169
170 if __name__ == '__main__':
171 tlb = TLB()
172 vl = rtlil.convert(tlb, ports=tlb.ports())
173 with open("test_tlb.il", "w") as f:
174 f.write(vl)
175