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