move unused directory out of src, to indicate "ignore completely"
[soc.git] / unused_please_ignore_completely / TLB / ariane / mmu.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: Florian Zaruba, ETH Zurich
13 # Date: 19/04/2017
14 # Description: Memory Management Unit for Ariane, contains TLB and
15 # address translation unit. SV48 as defined in
16 # Volume II: RISC-V Privileged Architectures V1.10 Page 63
17
18 import ariane_pkg::*;
19 """
20
21 from nmigen import Const, Signal, Cat, Module, Mux
22 from nmigen.cli import verilog, rtlil
23
24 from ptw import DCacheReqI, DCacheReqO, TLBUpdate, PTE, PTW
25 from tlb import TLB
26 from exceptcause import (INSTR_ACCESS_FAULT, INSTR_PAGE_FAULT,
27 LOAD_PAGE_FAULT, STORE_PAGE_FAULT)
28
29 PRIV_LVL_M = Const(0b11, 2)
30 PRIV_LVL_S = Const(0b01, 2)
31 PRIV_LVL_U = Const(0b00, 2)
32
33
34 class RVException:
35 def __init__(self):
36 self.cause = Signal(64) # cause of exception
37 self.tval = Signal(64) # more info of causing exception
38 # (e.g.: instruction causing it),
39 # address of LD/ST fault
40 self.valid = Signal()
41
42 def eq(self, inp):
43 res = []
44 for (o, i) in zip(self.ports(), inp.ports()):
45 res.append(o.eq(i))
46 return res
47
48 def __iter__(self):
49 yield self.cause
50 yield self.tval
51 yield self.valid
52
53 def ports(self):
54 return list(self)
55
56
57 class ICacheReqI:
58 def __init__(self):
59 self.fetch_valid = Signal() # address translation valid
60 self.fetch_paddr = Signal(64) # physical address in
61 self.fetch_exception = RVException() # exception occurred during fetch
62
63 def __iter__(self):
64 yield self.fetch_valid
65 yield self.fetch_paddr
66 yield from self.fetch_exception
67
68 def ports(self):
69 return list(self)
70
71
72 class ICacheReqO:
73 def __init__(self):
74 self.fetch_req = Signal() # address translation request
75 self.fetch_vaddr = Signal(64) # virtual address out
76
77 def __iter__(self):
78 yield self.fetch_req
79 yield self.fetch_vaddr
80
81 def ports(self):
82 return list(self)
83
84
85 class MMU:
86 def __init__(self, instr_tlb_entries = 4,
87 data_tlb_entries = 4,
88 asid_width = 1):
89 self.instr_tlb_entries = instr_tlb_entries
90 self.data_tlb_entries = data_tlb_entries
91 self.asid_width = asid_width
92
93 self.flush_i = Signal()
94 self.enable_translation_i = Signal()
95 self.en_ld_st_translation_i = Signal() # enable VM translation for LD/ST
96 # IF interface
97 self.icache_areq_i = ICacheReqO()
98 self.icache_areq_o = ICacheReqI()
99 # LSU interface
100 # this is a more minimalistic interface because the actual addressing
101 # logic is handled in the LSU as we distinguish load and stores,
102 # what we do here is simple address translation
103 self.misaligned_ex_i = RVException()
104 self.lsu_req_i = Signal() # request address translation
105 self.lsu_vaddr_i = Signal(64) # virtual address in
106 self.lsu_is_store_i = Signal() # the translation is requested by a store
107 # if we need to walk the page table we can't grant in the same cycle
108
109 # Cycle 0
110 self.lsu_dtlb_hit_o = Signal() # sent in the same cycle as the request
111 # if translation hits in the DTLB
112 # Cycle 1
113 self.lsu_valid_o = Signal() # translation is valid
114 self.lsu_paddr_o = Signal(64) # translated address
115 self.lsu_exception_o = RVException() # addr translate threw exception
116
117 # General control signals
118 self.priv_lvl_i = Signal(2)
119 self.ld_st_priv_lvl_i = Signal(2)
120 self.sum_i = Signal()
121 self.mxr_i = Signal()
122 # input logic flag_mprv_i,
123 self.satp_ppn_i = Signal(44)
124 self.asid_i = Signal(self.asid_width)
125 self.flush_tlb_i = Signal()
126 # Performance counters
127 self.itlb_miss_o = Signal()
128 self.dtlb_miss_o = Signal()
129 # PTW memory interface
130 self.req_port_i = DCacheReqO()
131 self.req_port_o = DCacheReqI()
132
133 def elaborate(self, platform):
134 m = Module()
135
136 iaccess_err = Signal() # insufficient priv to access instr page
137 daccess_err = Signal() # insufficient priv to access data page
138 ptw_active = Signal() # PTW is currently walking a page table
139 walking_instr = Signal() # PTW is walking because of an ITLB miss
140 ptw_error = Signal() # PTW threw an exception
141
142 update_vaddr = Signal(48) # guessed
143 uaddr64 = Cat(update_vaddr, Const(0, 25)) # extend to 64bit with zeros
144 update_ptw_itlb = TLBUpdate(self.asid_width)
145 update_ptw_dtlb = TLBUpdate(self.asid_width)
146
147 itlb_lu_access = Signal()
148 itlb_content = PTE()
149 itlb_is_2M = Signal()
150 itlb_is_1G = Signal()
151 itlb_is_512G = Signal()
152 itlb_lu_hit = Signal()
153
154 dtlb_lu_access = Signal()
155 dtlb_content = PTE()
156 dtlb_is_2M = Signal()
157 dtlb_is_1G = Signal()
158 dtlb_is_512G = Signal()
159 dtlb_lu_hit = Signal()
160
161 # Assignments
162 m.d.comb += [itlb_lu_access.eq(self.icache_areq_i.fetch_req),
163 dtlb_lu_access.eq(self.lsu_req_i)
164 ]
165
166 # ITLB
167 m.submodules.i_tlb = i_tlb = TLB(self.instr_tlb_entries,
168 self.asid_width)
169 m.d.comb += [i_tlb.flush_i.eq(self.flush_tlb_i),
170 i_tlb.update_i.eq(update_ptw_itlb),
171 i_tlb.lu_access_i.eq(itlb_lu_access),
172 i_tlb.lu_asid_i.eq(self.asid_i),
173 i_tlb.lu_vaddr_i.eq(self.icache_areq_i.fetch_vaddr),
174 itlb_content.eq(i_tlb.lu_content_o),
175 itlb_is_2M.eq(i_tlb.lu_is_2M_o),
176 itlb_is_1G.eq(i_tlb.lu_is_1G_o),
177 itlb_is_512G.eq(i_tlb.lu_is_512G_o),
178 itlb_lu_hit.eq(i_tlb.lu_hit_o),
179 ]
180
181 # DTLB
182 m.submodules.d_tlb = d_tlb = TLB(self.data_tlb_entries,
183 self.asid_width)
184 m.d.comb += [d_tlb.flush_i.eq(self.flush_tlb_i),
185 d_tlb.update_i.eq(update_ptw_dtlb),
186 d_tlb.lu_access_i.eq(dtlb_lu_access),
187 d_tlb.lu_asid_i.eq(self.asid_i),
188 d_tlb.lu_vaddr_i.eq(self.lsu_vaddr_i),
189 dtlb_content.eq(d_tlb.lu_content_o),
190 dtlb_is_2M.eq(d_tlb.lu_is_2M_o),
191 dtlb_is_1G.eq(d_tlb.lu_is_1G_o),
192 dtlb_is_512G.eq(d_tlb.lu_is_512G_o),
193 dtlb_lu_hit.eq(d_tlb.lu_hit_o),
194 ]
195
196 # PTW
197 m.submodules.ptw = ptw = PTW(self.asid_width)
198 m.d.comb += [ptw_active.eq(ptw.ptw_active_o),
199 walking_instr.eq(ptw.walking_instr_o),
200 ptw_error.eq(ptw.ptw_error_o),
201 ptw.enable_translation_i.eq(self.enable_translation_i),
202
203 update_vaddr.eq(ptw.update_vaddr_o),
204 update_ptw_itlb.eq(ptw.itlb_update_o),
205 update_ptw_dtlb.eq(ptw.dtlb_update_o),
206
207 ptw.itlb_access_i.eq(itlb_lu_access),
208 ptw.itlb_hit_i.eq(itlb_lu_hit),
209 ptw.itlb_vaddr_i.eq(self.icache_areq_i.fetch_vaddr),
210
211 ptw.dtlb_access_i.eq(dtlb_lu_access),
212 ptw.dtlb_hit_i.eq(dtlb_lu_hit),
213 ptw.dtlb_vaddr_i.eq(self.lsu_vaddr_i),
214
215 ptw.req_port_i.eq(self.req_port_i),
216 self.req_port_o.eq(ptw.req_port_o),
217 ]
218
219 # ila_1 i_ila_1 (
220 # .clk(clk_i), # input wire clk
221 # .probe0({req_port_o.address_tag, req_port_o.address_index}),
222 # .probe1(req_port_o.data_req), # input wire [63:0] probe1
223 # .probe2(req_port_i.data_gnt), # input wire [0:0] probe2
224 # .probe3(req_port_i.data_rdata), # input wire [0:0] probe3
225 # .probe4(req_port_i.data_rvalid), # input wire [0:0] probe4
226 # .probe5(ptw_error), # input wire [1:0] probe5
227 # .probe6(update_vaddr), # input wire [0:0] probe6
228 # .probe7(update_ptw_itlb.valid), # input wire [0:0] probe7
229 # .probe8(update_ptw_dtlb.valid), # input wire [0:0] probe8
230 # .probe9(dtlb_lu_access), # input wire [0:0] probe9
231 # .probe10(lsu_vaddr_i), # input wire [0:0] probe10
232 # .probe11(dtlb_lu_hit), # input wire [0:0] probe11
233 # .probe12(itlb_lu_access), # input wire [0:0] probe12
234 # .probe13(icache_areq_i.fetch_vaddr), # input wire [0:0] probe13
235 # .probe14(itlb_lu_hit) # input wire [0:0] probe13
236 # );
237
238 #-----------------------
239 # Instruction Interface
240 #-----------------------
241 # The instruction interface is a simple request response interface
242
243 # MMU disabled: just pass through
244 m.d.comb += [self.icache_areq_o.fetch_valid.eq(
245 self.icache_areq_i.fetch_req),
246 # play through in case we disabled address translation
247 self.icache_areq_o.fetch_paddr.eq(
248 self.icache_areq_i.fetch_vaddr)
249 ]
250 # two potential exception sources:
251 # 1. HPTW threw an exception -> signal with a page fault exception
252 # 2. We got an access error because of insufficient permissions ->
253 # throw an access exception
254 m.d.comb += self.icache_areq_o.fetch_exception.valid.eq(0)
255 # Check whether we are allowed to access this memory region
256 # from a fetch perspective
257
258 # PLATEN TODO: use PermissionValidator instead [we like modules]
259 m.d.comb += iaccess_err.eq(self.icache_areq_i.fetch_req & \
260 (((self.priv_lvl_i == PRIV_LVL_U) & \
261 ~itlb_content.u) | \
262 ((self.priv_lvl_i == PRIV_LVL_S) & \
263 itlb_content.u)))
264
265 # MMU enabled: address from TLB, request delayed until hit.
266 # Error when TLB hit and no access right or TLB hit and
267 # translated address not valid (e.g. AXI decode error),
268 # or when PTW performs walk due to ITLB miss and raises
269 # an error.
270 with m.If (self.enable_translation_i):
271 # we work with SV48, so if VM is enabled, check that
272 # all bits [47:38] are equal
273 with m.If (self.icache_areq_i.fetch_req & \
274 ~(((~self.icache_areq_i.fetch_vaddr[47:64]) == 0) | \
275 (self.icache_areq_i.fetch_vaddr[47:64]) == 0)):
276 fe = self.icache_areq_o.fetch_exception
277 m.d.comb += [fe.cause.eq(INSTR_ACCESS_FAULT),
278 fe.tval.eq(self.icache_areq_i.fetch_vaddr),
279 fe.valid.eq(1)
280 ]
281
282 m.d.comb += self.icache_areq_o.fetch_valid.eq(0)
283
284 # 4K page
285 paddr = Signal.like(self.icache_areq_o.fetch_paddr)
286 paddr4k = Cat(self.icache_areq_i.fetch_vaddr[0:12],
287 itlb_content.ppn)
288 m.d.comb += paddr.eq(paddr4k)
289 # Mega page
290 with m.If(itlb_is_2M):
291 m.d.comb += paddr[12:21].eq(
292 self.icache_areq_i.fetch_vaddr[12:21])
293 # Giga page
294 with m.If(itlb_is_1G):
295 m.d.comb += paddr[12:30].eq(
296 self.icache_areq_i.fetch_vaddr[12:30])
297 m.d.comb += self.icache_areq_o.fetch_paddr.eq(paddr)
298 # Tera page
299 with m.If(itlb_is_512G):
300 m.d.comb += paddr[12:39].eq(
301 self.icache_areq_i.fetch_vaddr[12:39])
302 m.d.comb += self.icache_areq_o.fetch_paddr.eq(paddr)
303
304 # ---------
305 # ITLB Hit
306 # --------
307 # if we hit the ITLB output the request signal immediately
308 with m.If(itlb_lu_hit):
309 m.d.comb += self.icache_areq_o.fetch_valid.eq(
310 self.icache_areq_i.fetch_req)
311 # we got an access error
312 with m.If (iaccess_err):
313 # throw a page fault
314 fe = self.icache_areq_o.fetch_exception
315 m.d.comb += [fe.cause.eq(INSTR_ACCESS_FAULT),
316 fe.tval.eq(self.icache_areq_i.fetch_vaddr),
317 fe.valid.eq(1)
318 ]
319 # ---------
320 # ITLB Miss
321 # ---------
322 # watch out for exceptions happening during walking the page table
323 with m.Elif(ptw_active & walking_instr):
324 m.d.comb += self.icache_areq_o.fetch_valid.eq(ptw_error)
325 fe = self.icache_areq_o.fetch_exception
326 m.d.comb += [fe.cause.eq(INSTR_PAGE_FAULT),
327 fe.tval.eq(uaddr64),
328 fe.valid.eq(1)
329 ]
330
331 #-----------------------
332 # Data Interface
333 #-----------------------
334
335 lsu_vaddr = Signal(64)
336 dtlb_pte = PTE()
337 misaligned_ex = RVException()
338 lsu_req = Signal()
339 lsu_is_store = Signal()
340 dtlb_hit = Signal()
341 #dtlb_is_2M = Signal()
342 #dtlb_is_1G = Signal()
343 #dtlb_is_512 = Signal()
344
345 # check if we need to do translation or if we are always
346 # ready (e.g.: we are not translating anything)
347 m.d.comb += self.lsu_dtlb_hit_o.eq(Mux(self.en_ld_st_translation_i,
348 dtlb_lu_hit, 1))
349
350 # The data interface is simpler and only consists of a
351 # request/response interface
352 m.d.comb += [
353 # save request and DTLB response
354 lsu_vaddr.eq(self.lsu_vaddr_i),
355 lsu_req.eq(self.lsu_req_i),
356 misaligned_ex.eq(self.misaligned_ex_i),
357 dtlb_pte.eq(dtlb_content),
358 dtlb_hit.eq(dtlb_lu_hit),
359 lsu_is_store.eq(self.lsu_is_store_i),
360 #dtlb_is_2M.eq(dtlb_is_2M),
361 #dtlb_is_1G.eq(dtlb_is_1G),
362 ##dtlb_is_512.eq(self.dtlb_is_512G) #????
363 ]
364 m.d.sync += [
365 self.lsu_paddr_o.eq(lsu_vaddr),
366 self.lsu_valid_o.eq(lsu_req),
367 self.lsu_exception_o.eq(misaligned_ex),
368 ]
369
370 sverr = Signal()
371 usrerr = Signal()
372
373 m.d.comb += [
374 # mute misaligned exceptions if there is no request
375 # otherwise they will throw accidental exceptions
376 misaligned_ex.valid.eq(self.misaligned_ex_i.valid & self.lsu_req_i),
377
378 # SUM is not set and we are trying to access a user
379 # page in supervisor mode
380 sverr.eq(self.ld_st_priv_lvl_i == PRIV_LVL_S & ~self.sum_i & \
381 dtlb_pte.u),
382 # this is not a user page but we are in user mode and
383 # trying to access it
384 usrerr.eq(self.ld_st_priv_lvl_i == PRIV_LVL_U & ~dtlb_pte.u),
385
386 # Check if the User flag is set, then we may only
387 # access it in supervisor mode if SUM is enabled
388 daccess_err.eq(sverr | usrerr),
389 ]
390
391 # translation is enabled and no misaligned exception occurred
392 with m.If(self.en_ld_st_translation_i & ~misaligned_ex.valid):
393 m.d.comb += lsu_req.eq(0)
394 # 4K page
395 paddr = Signal.like(lsu_vaddr)
396 paddr4k = Cat(lsu_vaddr[0:12], itlb_content.ppn)
397 m.d.comb += paddr.eq(paddr4k)
398 # Mega page
399 with m.If(dtlb_is_2M):
400 m.d.comb += paddr[12:21].eq(lsu_vaddr[12:21])
401 # Giga page
402 with m.If(dtlb_is_1G):
403 m.d.comb += paddr[12:30].eq(lsu_vaddr[12:30])
404 m.d.sync += self.lsu_paddr_o.eq(paddr)
405 # TODO platen tera_page
406
407 # ---------
408 # DTLB Hit
409 # --------
410 with m.If(dtlb_hit & lsu_req):
411 m.d.comb += lsu_req.eq(1)
412 # this is a store
413 with m.If (lsu_is_store):
414 # check if the page is write-able and
415 # we are not violating privileges
416 # also check if the dirty flag is set
417 with m.If(~dtlb_pte.w | daccess_err | ~dtlb_pte.d):
418 le = self.lsu_exception_o
419 m.d.sync += [le.cause.eq(STORE_PAGE_FAULT),
420 le.tval.eq(lsu_vaddr),
421 le.valid.eq(1)
422 ]
423
424 # this is a load, check for sufficient access
425 # privileges - throw a page fault if necessary
426 with m.Elif(daccess_err):
427 le = self.lsu_exception_o
428 m.d.sync += [le.cause.eq(LOAD_PAGE_FAULT),
429 le.tval.eq(lsu_vaddr),
430 le.valid.eq(1)
431 ]
432 # ---------
433 # DTLB Miss
434 # ---------
435 # watch out for exceptions
436 with m.Elif (ptw_active & ~walking_instr):
437 # page table walker threw an exception
438 with m.If (ptw_error):
439 # an error makes the translation valid
440 m.d.comb += lsu_req.eq(1)
441 # the page table walker can only throw page faults
442 with m.If (lsu_is_store):
443 le = self.lsu_exception_o
444 m.d.sync += [le.cause.eq(STORE_PAGE_FAULT),
445 le.tval.eq(uaddr64),
446 le.valid.eq(1)
447 ]
448 with m.Else():
449 m.d.sync += [le.cause.eq(LOAD_PAGE_FAULT),
450 le.tval.eq(uaddr64),
451 le.valid.eq(1)
452 ]
453
454 return m
455
456 def ports(self):
457 return [self.flush_i, self.enable_translation_i,
458 self.en_ld_st_translation_i,
459 self.lsu_req_i,
460 self.lsu_vaddr_i, self.lsu_is_store_i, self.lsu_dtlb_hit_o,
461 self.lsu_valid_o, self.lsu_paddr_o,
462 self.priv_lvl_i, self.ld_st_priv_lvl_i, self.sum_i, self.mxr_i,
463 self.satp_ppn_i, self.asid_i, self.flush_tlb_i,
464 self.itlb_miss_o, self.dtlb_miss_o] + \
465 self.icache_areq_i.ports() + self.icache_areq_o.ports() + \
466 self.req_port_i.ports() + self.req_port_o.ports() + \
467 self.misaligned_ex_i.ports() + self.lsu_exception_o.ports()
468
469 if __name__ == '__main__':
470 mmu = MMU()
471 vl = rtlil.convert(mmu, ports=mmu.ports())
472 with open("test_mmu.il", "w") as f:
473 f.write(vl)
474