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