correct mmu.py syntax errors, output ilang as a test
[soc.git] / TLB / src / ariane / ptw.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: 24.4.2017
15 # Description: Hardware-PTW
16
17 /* verilator lint_off WIDTH */
18 import ariane_pkg::*;
19
20 see linux kernel source:
21
22 * "arch/riscv/include/asm/page.h"
23 * "arch/riscv/include/asm/mmu_context.h"
24 * "arch/riscv/Kconfig" (CONFIG_PAGE_OFFSET)
25
26 """
27
28 from nmigen import Const, Signal, Cat, Module
29 from nmigen.hdl.ast import ArrayProxy
30 from nmigen.cli import verilog, rtlil
31 from math import log2
32
33
34 DCACHE_SET_ASSOC = 8
35 CONFIG_L1D_SIZE = 32*1024
36 DCACHE_INDEX_WIDTH = int(log2(CONFIG_L1D_SIZE / DCACHE_SET_ASSOC))
37 DCACHE_TAG_WIDTH = 56 - DCACHE_INDEX_WIDTH
38
39 ASID_WIDTH = 8
40
41
42 class DCacheReqI:
43 def __init__(self):
44 self.address_index = Signal(DCACHE_INDEX_WIDTH)
45 self.address_tag = Signal(DCACHE_TAG_WIDTH)
46 self.data_wdata = Signal(64)
47 self.data_req = Signal()
48 self.data_we = Signal()
49 self.data_be = Signal(8)
50 self.data_size = Signal(2)
51 self.kill_req = Signal()
52 self.tag_valid = Signal()
53
54 def eq(self, inp):
55 res = []
56 for (o, i) in zip(self.ports(), inp.ports()):
57 res.append(o.eq(i))
58 return res
59
60 def ports(self):
61 return [self.address_index, self.address_tag,
62 self.data_wdata, self.data_req,
63 self.data_we, self.data_be, self.data_size,
64 self.kill_req, self.tag_valid,
65 ]
66
67 class DCacheReqO:
68 def __init__(self):
69 self.data_gnt = Signal()
70 self.data_rvalid = Signal()
71 self.data_rdata = Signal(64) # actually in PTE object format
72
73 def eq(self, inp):
74 res = []
75 for (o, i) in zip(self.ports(), inp.ports()):
76 res.append(o.eq(i))
77 return res
78
79 def ports(self):
80 return [self.data_gnt, self.data_rvalid, self.data_rdata]
81
82
83 class PTE: #(RecordObject):
84 def __init__(self):
85 self.reserved = Signal(10)
86 self.ppn = Signal(44)
87 self.rsw = Signal(2)
88 self.d = Signal()
89 self.a = Signal()
90 self.g = Signal()
91 self.u = Signal()
92 self.x = Signal()
93 self.w = Signal()
94 self.r = Signal()
95 self.v = Signal()
96
97 def flatten(self):
98 return Cat(*self.ports())
99
100 def eq(self, x):
101 if isinstance(x, ArrayProxy):
102 res = []
103 for o in self.ports():
104 i = getattr(x, o.name)
105 res.append(i)
106 x = Cat(*res)
107 else:
108 x = x.flatten()
109 return self.flatten().eq(x)
110
111 def ports(self):
112 return [self.reserved, self.ppn, self.rsw, self.d, self.a, self.g,
113 self.u, self.x, self.w, self.r, self.v]
114
115
116 class TLBUpdate:
117 def __init__(self):
118 self.valid = Signal() # valid flag
119 self.is_2M = Signal()
120 self.is_1G = Signal()
121 self.vpn = Signal(27)
122 self.asid = Signal(ASID_WIDTH)
123 self.content = PTE()
124
125 def flatten(self):
126 return Cat(*self.ports())
127
128 def eq(self, x):
129 return self.flatten().eq(x.flatten())
130
131 def ports(self):
132 return [self.valid, self.is_2M, self.is_1G, self.vpn, self.asid] + \
133 self.content.ports()
134
135
136 # SV39 defines three levels of page tables
137 LVL1 = Const(0, 2) # defined to 0 so that ptw_lvl default-resets to LVL1
138 LVL2 = Const(1, 2)
139 LVL3 = Const(2, 2)
140
141
142 class PTW:
143 def __init__(self):
144 self.flush_i = Signal() # flush everything, we need to do this because
145 # actually everything we do is speculative at this stage
146 # e.g.: there could be a CSR instruction that changes everything
147 self.ptw_active_o = Signal(reset=1) # active if not IDLE
148 self.walking_instr_o = Signal() # set when walking for TLB
149 self.ptw_error_o = Signal() # set when an error occurred
150 self.enable_translation_i = Signal() # CSRs indicate to enable SV39
151 self.en_ld_st_translation_i = Signal() # enable VM translation for ld/st
152
153 self.lsu_is_store_i = Signal() # translation triggered by store
154 # PTW memory interface
155 self.req_port_i = DCacheReqO()
156 self.req_port_o = DCacheReqI()
157
158 # to TLBs, update logic
159 self.itlb_update_o = TLBUpdate()
160 self.dtlb_update_o = TLBUpdate()
161
162 self.update_vaddr_o = Signal(39)
163
164 self.asid_i = Signal(ASID_WIDTH)
165 # from TLBs
166 # did we miss?
167 self.itlb_access_i = Signal()
168 self.itlb_hit_i = Signal()
169 self.itlb_vaddr_i = Signal(64)
170
171 self.dtlb_access_i = Signal()
172 self.dtlb_hit_i = Signal()
173 self.dtlb_vaddr_i = Signal(64)
174 # from CSR file
175 self.satp_ppn_i = Signal(44) # ppn from satp
176 self.mxr_i = Signal()
177 # Performance counters
178 self.itlb_miss_o = Signal()
179 self.dtlb_miss_o = Signal()
180
181 def ports(self):
182 return [self.ptw_active_o, self.walking_instr_o, self.ptw_error_o,
183 ]
184 return [
185 self.enable_translation_i, self.en_ld_st_translation_i,
186 self.lsu_is_store_i, self.req_port_i, self.req_port_o,
187 self.update_vaddr_o,
188 self.asid_i,
189 self.itlb_access_i, self.itlb_hit_i, self.itlb_vaddr_i,
190 self.dtlb_access_i, self.dtlb_hit_i, self.dtlb_vaddr_i,
191 self.satp_ppn_i, self.mxr_i,
192 self.itlb_miss_o, self.dtlb_miss_o
193 ] + self.itlb_update_o.ports() + self.dtlb_update_o.ports()
194
195 def elaborate(self, platform):
196 m = Module()
197
198 # input registers
199 data_rvalid = Signal()
200 data_rdata = Signal(64)
201
202 # NOTE: pte decodes the incoming bit-field (data_rdata). data_rdata
203 # is spec'd in 64-bit binary-format: better to spec as Record?
204 pte = PTE()
205 m.d.comb += pte.flatten().eq(data_rdata)
206
207 # SV39 defines three levels of page tables
208 ptw_lvl = Signal(2) # default=0=LVL1 on reset (see above)
209 ptw_lvl1 = Signal()
210 ptw_lvl2 = Signal()
211 ptw_lvl3 = Signal()
212 m.d.comb += [ptw_lvl1.eq(ptw_lvl == LVL1),
213 ptw_lvl2.eq(ptw_lvl == LVL2),
214 ptw_lvl3.eq(ptw_lvl == LVL3)]
215
216 # is this an instruction page table walk?
217 is_instr_ptw = Signal()
218 global_mapping = Signal()
219 # latched tag signal
220 tag_valid = Signal()
221 # register the ASID
222 tlb_update_asid = Signal(ASID_WIDTH)
223 # register VPN we need to walk, SV39 defines a 39 bit virtual addr
224 vaddr = Signal(64)
225 # 4 byte aligned physical pointer
226 ptw_pptr = Signal(56)
227
228 end = DCACHE_INDEX_WIDTH + DCACHE_TAG_WIDTH
229 m.d.sync += [
230 # Assignments
231 self.update_vaddr_o.eq(vaddr),
232
233 self.walking_instr_o.eq(is_instr_ptw),
234 # directly output the correct physical address
235 self.req_port_o.address_index.eq(ptw_pptr[0:DCACHE_INDEX_WIDTH]),
236 self.req_port_o.address_tag.eq(ptw_pptr[DCACHE_INDEX_WIDTH:end]),
237 # we are never going to kill this request
238 self.req_port_o.kill_req.eq(0), # XXX assign comb?
239 # we are never going to write with the HPTW
240 self.req_port_o.data_wdata.eq(Const(0, 64)), # XXX assign comb?
241 # -----------
242 # TLB Update
243 # -----------
244 self.itlb_update_o.vpn.eq(vaddr[12:39]),
245 self.dtlb_update_o.vpn.eq(vaddr[12:39]),
246 # update the correct page table level
247 self.itlb_update_o.is_2M.eq(ptw_lvl2),
248 self.itlb_update_o.is_1G.eq(ptw_lvl1),
249 self.dtlb_update_o.is_2M.eq(ptw_lvl2),
250 self.dtlb_update_o.is_1G.eq(ptw_lvl1),
251 # output the correct ASID
252 self.itlb_update_o.asid.eq(tlb_update_asid),
253 self.dtlb_update_o.asid.eq(tlb_update_asid),
254 # set the global mapping bit
255 self.itlb_update_o.content.eq(pte),
256 self.itlb_update_o.content.g.eq(global_mapping),
257 self.dtlb_update_o.content.eq(pte),
258 self.dtlb_update_o.content.g.eq(global_mapping),
259
260 self.req_port_o.tag_valid.eq(tag_valid),
261 ]
262
263 #-------------------
264 # Page table walker
265 #-------------------
266 # A virtual address va is translated into a physical address pa as
267 # follows:
268 # 1. Let a be sptbr.ppn × PAGESIZE, and let i = LEVELS-1. (For Sv39,
269 # PAGESIZE=2^12 and LEVELS=3.)
270 # 2. Let pte be the value of the PTE at address a+va.vpn[i]×PTESIZE.
271 # (For Sv32, PTESIZE=4.)
272 # 3. If pte.v = 0, or if pte.r = 0 and pte.w = 1, stop and raise an
273 # access exception.
274 # 4. Otherwise, the PTE is valid. If pte.r = 1 or pte.x = 1, go to
275 # step 5. Otherwise, this PTE is a pointer to the next level of
276 # the page table.
277 # Let i=i-1. If i < 0, stop and raise an access exception.
278 # Otherwise, let a = pte.ppn × PAGESIZE and go to step 2.
279 # 5. A leaf PTE has been found. Determine if the requested memory
280 # access is allowed by the pte.r, pte.w, and pte.x bits. If not,
281 # stop and raise an access exception. Otherwise, the translation is
282 # successful. Set pte.a to 1, and, if the memory access is a
283 # store, set pte.d to 1.
284 # The translated physical address is given as follows:
285 # - pa.pgoff = va.pgoff.
286 # - If i > 0, then this is a superpage translation and
287 # pa.ppn[i-1:0] = va.vpn[i-1:0].
288 # - pa.ppn[LEVELS-1:i] = pte.ppn[LEVELS-1:i].
289 # 6. If i > 0 and pa.ppn[i − 1 : 0] != 0, this is a misaligned
290 # superpage stop and raise a page-fault exception.
291
292 m.d.sync += tag_valid.eq(0)
293
294 # default assignments
295 m.d.comb += [
296 # PTW memory interface
297 self.req_port_o.data_req.eq(0),
298 self.req_port_o.data_be.eq(Const(0xFF, 8)),
299 self.req_port_o.data_size.eq(Const(0b11, 2)),
300 self.req_port_o.data_we.eq(0),
301 self.ptw_error_o.eq(0),
302 self.itlb_update_o.valid.eq(0),
303 self.dtlb_update_o.valid.eq(0),
304
305 self.itlb_miss_o.eq(0),
306 self.dtlb_miss_o.eq(0),
307 ]
308
309 # ------------
310 # State Machine
311 # ------------
312
313 with m.FSM() as fsm:
314
315 with m.State("IDLE"):
316 self.idle(m, is_instr_ptw, ptw_lvl, global_mapping,
317 ptw_pptr, vaddr, tlb_update_asid)
318
319 with m.State("WAIT_GRANT"):
320 self.grant(m, tag_valid, data_rvalid)
321
322 with m.State("PTE_LOOKUP"):
323 # we wait for the valid signal
324 with m.If(data_rvalid):
325 self.lookup(m, pte, ptw_lvl, ptw_lvl1, ptw_lvl2, ptw_lvl3,
326 data_rvalid, global_mapping,
327 is_instr_ptw, ptw_pptr)
328
329 # Propagate error to MMU/LSU
330 with m.State("PROPAGATE_ERROR"):
331 m.next = "IDLE"
332 m.d.comb += self.ptw_error_o.eq(1)
333
334 # wait for the rvalid before going back to IDLE
335 with m.State("WAIT_RVALID"):
336 with m.If(data_rvalid):
337 m.next = "IDLE"
338
339 m.d.sync += [data_rdata.eq(self.req_port_i.data_rdata),
340 data_rvalid.eq(self.req_port_i.data_rvalid)
341 ]
342
343 return m
344
345 def set_grant_state(self, m):
346 # should we have flushed before we got an rvalid,
347 # wait for it until going back to IDLE
348 with m.If(self.flush_i):
349 with m.If (self.req_port_i.data_gnt):
350 m.next = "WAIT_RVALID"
351 with m.Else():
352 m.next = "IDLE"
353 with m.Else():
354 m.next = "WAIT_GRANT"
355
356 def idle(self, m, is_instr_ptw, ptw_lvl, global_mapping,
357 ptw_pptr, vaddr, tlb_update_asid):
358 # by default we start with the top-most page table
359 m.d.sync += [is_instr_ptw.eq(0),
360 ptw_lvl.eq(LVL1),
361 global_mapping.eq(0),
362 self.ptw_active_o.eq(0), # deactive (IDLE)
363 ]
364 # work out itlb/dtlb miss
365 m.d.comb += self.itlb_miss_o.eq(self.enable_translation_i & \
366 self.itlb_access_i & \
367 ~self.itlb_hit_i & \
368 ~self.dtlb_access_i)
369 m.d.comb += self.dtlb_miss_o.eq(self.en_ld_st_translation_i & \
370 self.dtlb_access_i & \
371 ~self.dtlb_hit_i)
372 # we got an ITLB miss?
373 with m.If(self.itlb_miss_o):
374 pptr = Cat(Const(0, 3), self.itlb_vaddr_i[30:39],
375 self.satp_ppn_i)
376 m.d.sync += [ptw_pptr.eq(pptr),
377 is_instr_ptw.eq(1),
378 vaddr.eq(self.itlb_vaddr_i),
379 tlb_update_asid.eq(self.asid_i),
380 ]
381 self.set_grant_state(m)
382
383 # we got a DTLB miss?
384 with m.Elif(self.dtlb_miss_o):
385 pptr = Cat(Const(0, 3), self.dtlb_vaddr_i[30:39],
386 self.satp_ppn_i)
387 m.d.sync += [ptw_pptr.eq(pptr),
388 vaddr.eq(self.dtlb_vaddr_i),
389 tlb_update_asid.eq(self.asid_i),
390 ]
391 self.set_grant_state(m)
392
393 def grant(self, m, tag_valid, data_rvalid):
394 # we've got a data WAIT_GRANT so tell the
395 # cache that the tag is valid
396
397 # send a request out
398 m.d.comb += self.req_port_o.data_req.eq(1)
399 # wait for the WAIT_GRANT
400 with m.If(self.req_port_i.data_gnt):
401 # send the tag valid signal one cycle later
402 m.d.sync += tag_valid.eq(1)
403 # should we have flushed before we got an rvalid,
404 # wait for it until going back to IDLE
405 with m.If(self.flush_i):
406 with m.If (~data_rvalid):
407 m.next = "WAIT_RVALID"
408 with m.Else():
409 m.next = "IDLE"
410 with m.Else():
411 m.next = "PTE_LOOKUP"
412
413 def lookup(self, m, pte, ptw_lvl, ptw_lvl1, ptw_lvl2, ptw_lvl3,
414 data_rvalid, global_mapping,
415 is_instr_ptw, ptw_pptr):
416 # temporaries
417 pte_rx = Signal(reset_less=True)
418 pte_exe = Signal(reset_less=True)
419 pte_inv = Signal(reset_less=True)
420 pte_a = Signal(reset_less=True)
421 st_wd = Signal(reset_less=True)
422 m.d.comb += [pte_rx.eq(pte.r | pte.x),
423 pte_exe.eq(~pte.x | ~pte.a),
424 pte_inv.eq(~pte.v | (~pte.r & pte.w)),
425 pte_a.eq(pte.a & (pte.r | (pte.x & self.mxr_i))),
426 st_wd.eq(self.lsu_is_store_i & (~pte.w | ~pte.d))]
427
428 l1err = Signal(reset_less=True)
429 l2err = Signal(reset_less=True)
430 m.d.comb += [l2err.eq((ptw_lvl2) & pte.ppn[0:9] != Const(0, 9)),
431 l1err.eq((ptw_lvl1) & pte.ppn[0:18] != Const(0, 18)) ]
432
433 # check if the global mapping bit is set
434 with m.If (pte.g):
435 m.d.sync += global_mapping.eq(1)
436
437 m.next = "IDLE"
438
439 # -------------
440 # Invalid PTE
441 # -------------
442 # If pte.v = 0, or if pte.r = 0 and pte.w = 1,
443 # stop and raise a page-fault exception.
444 with m.If (pte_inv):
445 m.next = "PROPAGATE_ERROR"
446
447 # -----------
448 # Valid PTE
449 # -----------
450
451 # it is a valid PTE
452 # if pte.r = 1 or pte.x = 1 it is a valid PTE
453 with m.Elif (pte_rx):
454 # Valid translation found (either 1G, 2M or 4K)
455 with m.If(is_instr_ptw):
456 # ------------
457 # Update ITLB
458 # ------------
459 # If page not executable, we can directly raise error.
460 # This doesn't put a useless entry into the TLB.
461 # The same idea applies to the access flag since we let
462 # the access flag be managed by SW.
463 with m.If (pte_exe):
464 m.next = "IDLE"
465 with m.Else():
466 m.d.comb += self.itlb_update_o.valid.eq(1)
467
468 with m.Else():
469 # ------------
470 # Update DTLB
471 # ------------
472 # Check if the access flag has been set, otherwise
473 # throw page-fault and let software handle those bits.
474 # If page not readable (there are no write-only pages)
475 # directly raise an error. This doesn't put a useless
476 # entry into the TLB.
477 with m.If(pte_a):
478 m.d.comb += self.dtlb_update_o.valid.eq(1)
479 with m.Else():
480 m.next = "PROPAGATE_ERROR"
481 # Request is a store: perform additional checks
482 # If the request was a store and the page not
483 # write-able, raise an error
484 # the same applies if the dirty flag is not set
485 with m.If (st_wd):
486 m.d.comb += self.dtlb_update_o.valid.eq(0)
487 m.next = "PROPAGATE_ERROR"
488
489 # check if the ppn is correctly aligned: Case (6)
490 with m.If(l1err | l2err):
491 m.next = "PROPAGATE_ERROR"
492 m.d.comb += [self.dtlb_update_o.valid.eq(0),
493 self.itlb_update_o.valid.eq(0)]
494
495 # this is a pointer to the next TLB level
496 with m.Else():
497 # pointer to next level of page table
498 with m.If (ptw_lvl1):
499 # we are in the second level now
500 pptr = Cat(Const(0, 3), self.dtlb_vaddr_i[21:30], pte.ppn)
501 m.d.sync += [ptw_pptr.eq(pptr),
502 ptw_lvl.eq(LVL2)
503 ]
504 with m.If(ptw_lvl2):
505 # here we received a pointer to the third level
506 pptr = Cat(Const(0, 3), self.dtlb_vaddr_i[12:21], pte.ppn)
507 m.d.sync += [ptw_pptr.eq(pptr),
508 ptw_lvl.eq(LVL3)
509 ]
510 self.set_grant_state(m)
511
512 with m.If (ptw_lvl3):
513 # Should already be the last level
514 # page table => Error
515 m.d.sync += ptw_lvl.eq(LVL3)
516 m.next = "PROPAGATE_ERROR"
517
518
519 if __name__ == '__main__':
520 ptw = PTW()
521 vl = rtlil.convert(ptw, ports=ptw.ports())
522 with open("test_ptw.il", "w") as f:
523 f.write(vl)