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