Use singal passed into plru rather than relying on internal signal
[soc.git] / TLB / src / 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, SV39
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 from math import log2
25 from nmigen import Signal, Module, Cat, Const, Array
26 from nmigen.cli import verilog, rtlil
27 from nmigen.lib.coding import Encoder
28
29 from ptw import TLBUpdate, PTE, ASID_WIDTH
30 from plru import PLRU
31 from tlb_content import TLBContent
32
33 TLB_ENTRIES = 8
34
35 class TLB:
36 def __init__(self, tlb_entries=8, asid_width=8):
37 self.tlb_entries = tlb_entries
38 self.asid_width = asid_width
39
40 self.flush_i = Signal() # Flush signal
41 # Lookup signals
42 self.lu_access_i = Signal()
43 self.lu_asid_i = Signal(self.asid_width)
44 self.lu_vaddr_i = Signal(64)
45 self.lu_content_o = PTE()
46 self.lu_is_2M_o = Signal()
47 self.lu_is_1G_o = Signal()
48 self.lu_hit_o = Signal()
49 # Update TLB
50 self.pte_width = len(self.lu_content_o.flatten())
51 self.update_i = TLBUpdate(asid_width)
52
53 def elaborate(self, platform):
54 m = Module()
55
56 vpn2 = Signal(9)
57 vpn1 = Signal(9)
58 vpn0 = Signal(9)
59
60 #-------------
61 # Translation
62 #-------------
63
64 # SV39 defines three levels of page tables
65 m.d.comb += [ vpn0.eq(self.lu_vaddr_i[12:21]),
66 vpn1.eq(self.lu_vaddr_i[21:30]),
67 vpn2.eq(self.lu_vaddr_i[30:39]),
68 ]
69
70 tc = []
71 for i in range(self.tlb_entries):
72 tlc = TLBContent(self.pte_width, self.asid_width)
73 setattr(m.submodules, "tc%d" % i, tlc)
74 tc.append(tlc)
75 # connect inputs
76 tlc.update_i = self.update_i # saves a lot of graphviz links
77 m.d.comb += [tlc.vpn0.eq(vpn0),
78 tlc.vpn1.eq(vpn1),
79 tlc.vpn2.eq(vpn2),
80 tlc.flush_i.eq(self.flush_i),
81 #tlc.update_i.eq(self.update_i),
82 tlc.lu_asid_i.eq(self.lu_asid_i)]
83 tc = Array(tc)
84
85 #--------------
86 # Select hit
87 #--------------
88
89 # use Encoder to select hit index
90 # XXX TODO: assert that there's only one valid entry (one lu_hit)
91 hitsel = Encoder(self.tlb_entries)
92 m.submodules.hitsel = hitsel
93
94 hits = []
95 for i in range(self.tlb_entries):
96 hits.append(tc[i].lu_hit_o)
97 m.d.comb += hitsel.i.eq(Cat(*hits)) # (goes into plru as well)
98 idx = hitsel.o
99
100 active = Signal(reset_less=True)
101 m.d.comb += active.eq(~hitsel.n)
102 with m.If(active):
103 # active hit, send selected as output
104 m.d.comb += [ self.lu_is_1G_o.eq(tc[idx].lu_is_1G_o),
105 self.lu_is_2M_o.eq(tc[idx].lu_is_2M_o),
106 self.lu_hit_o.eq(1),
107 self.lu_content_o.flatten().eq(tc[idx].lu_content_o),
108 ]
109
110 #--------------
111 # PLRU.
112 #--------------
113
114 p = PLRU(self.tlb_entries)
115 plru_tree = Signal(p.TLBSZ)
116 m.submodules.plru = p
117
118 # connect PLRU inputs/outputs
119 # XXX TODO: assert that there's only one valid entry (one replace_en)
120 en = []
121 for i in range(self.tlb_entries):
122 en.append(tc[i].replace_en_i)
123 m.d.comb += [Cat(*en).eq(p.replace_en_o), # output from PLRU into tags
124 p.lu_hit.eq(hitsel.i),
125 p.lu_access_i.eq(self.lu_access_i),
126 p.plru_tree.eq(plru_tree)]
127 m.d.sync += plru_tree.eq(p.plru_tree_o)
128
129 #--------------
130 # Sanity checks
131 #--------------
132
133 assert (self.tlb_entries % 2 == 0) and (self.tlb_entries > 1), \
134 "TLB size must be a multiple of 2 and greater than 1"
135 assert (self.asid_width >= 1), \
136 "ASID width must be at least 1"
137
138 return m
139
140 """
141 # Just for checking
142 function int countSetBits(logic[self.tlb_entries-1:0] vector);
143 automatic int count = 0;
144 foreach (vector[idx]) begin
145 count += vector[idx];
146 end
147 return count;
148 endfunction
149
150 assert property (@(posedge clk_i)(countSetBits(lu_hit) <= 1))
151 else $error("More then one hit in TLB!"); $stop(); end
152 assert property (@(posedge clk_i)(countSetBits(replace_en) <= 1))
153 else $error("More then one TLB entry selected for next replace!");
154 """
155
156 def ports(self):
157 return [self.flush_i, self.lu_access_i,
158 self.lu_asid_i, self.lu_vaddr_i,
159 self.lu_is_2M_o, self.lu_is_1G_o, self.lu_hit_o,
160 ] + self.lu_content_o.ports() + self.update_i.ports()
161
162 if __name__ == '__main__':
163 tlb = TLB()
164 vl = rtlil.convert(tlb, ports=tlb.ports())
165 with open("test_tlb.il", "w") as f:
166 f.write(vl)
167