a245ddbd7d1af973ee09d6560279759affb04ee8
[soc.git] / src / soc / experiment / mmu.py
1 # MMU
2 #
3 # License for original copyright mmu.vhdl by microwatt authors: CC4
4 # License for copyrighted modifications made in mmu.py: LGPLv3+
5 #
6 # This derivative work although includes CC4 licensed material is
7 # covered by the LGPLv3+
8
9 """MMU
10
11 based on Anton Blanchard microwatt mmu.vhdl
12
13 """
14 from enum import Enum, unique
15 from nmigen import (C, Module, Signal, Elaboratable, Mux, Cat, Repl, Signal)
16 from nmigen.cli import main
17 from nmigen.cli import rtlil
18 from nmutil.iocontrol import RecordObject
19 from nmutil.byterev import byte_reverse
20 from nmutil.mask import Mask, masked
21 from nmutil.util import Display
22
23 # NOTE: to use cxxsim, export NMIGEN_SIM_MODE=cxxsim from the shell
24 # Also, check out the cxxsim nmigen branch, and latest yosys from git
25 from nmutil.sim_tmp_alternative import Simulator, Settle
26
27 from nmutil.util import wrap
28
29 from soc.experiment.mem_types import (LoadStore1ToMMUType,
30 MMUToLoadStore1Type,
31 MMUToDCacheType,
32 DCacheToMMUType,
33 MMUToICacheType)
34
35 # Radix Tree Page Directory Entry Record, TODO put this somewhere sensible
36 # v3.0C Book III p1015-1016 section 6.7.10.1
37 class RTPDE(RecordObject):
38 def __init__(self, name=None):
39 super().__init__(name=name)
40 self.nls = Signal(5) # Nextded Access Auth bits 59:63 LSB0 0:4
41 self.rs1 = Signal(3) # Reserved bits 56:58 LSB0 5:7
42 self.nlb = Signal(52) # Next Level Base bit 4:55 LSB0 8:59
43 self.rs2 = Signal(2) # Reserved bit 2:3 LSB0 60:61
44 self.leaf = Signal(1) # leaf bit 1 LSB0 62
45 self.valid = Signal(1) # valid bit 0 LSB0 63
46
47
48 # Radix Tree Page Table Entry Record, TODO put this somewhere sensible
49 # v3.0C Book III p1016 section 6.7.10.2
50 class RTPTE(RecordObject):
51 def __init__(self, name=None):
52 super().__init__(name=name)
53 self.eaa = Signal(4) # Encoded Access Auth bits 60:63 LSB0 0:3
54 self.att = Signal(2) # Attributes bits 58:59 LSB0 4:5
55 self.rs1 = Signal(1) # Reserved bit 57 LSB0 6
56 self.c = Signal(1) # Change bit 56 LSB0 7
57 self.r = Signal(1) # Reference bit 55 LSB0 8
58 self.sw = Signal(3) # SW bits 1:3 bits 52:54 LSB0 9:11
59 self.rpn = Signal(45) # Real Page Number bits 7:51 LSB0 12:56
60 self.rs2 = Signal(4) # Reserved bit 3:6 LSB0 57-60
61 self.sw0 = Signal(1) # SW bit 0 bit 2 LSB0 61
62 self.leaf = Signal(1) # leaf bit 1 LSB0 62
63 self.valid = Signal(1) # valid bit 0 LSB0 63
64
65 # and these... which of course are turned round to LSB0 order.
66 # TODO: sigh. use botchify and put them in openpower.consts
67 EAA_PRIV = 3 # bit 0 (in MSB0) set ==> problem-state banned (priv=1 only)
68 EAA_RD = 2 # bit 1 (in MSB0) set ==> loads are permitted
69 EAA_WR = 1 # bit 2 (in MSB0) set ==> load and stores permitted
70 EAA_EXE = 0 # bit 3 (in MSB0) set ==> execute permitted
71
72 # for debugging
73 display_invalid = True
74
75 @unique
76 class State(Enum):
77 IDLE = 0 # zero is default on reset for r.state
78 DO_TLBIE = 1
79 TLB_WAIT = 2
80 PROC_TBL_READ = 3
81 PROC_TBL_WAIT = 4
82 SEGMENT_CHECK = 5
83 RADIX_LOOKUP = 6
84 RADIX_READ_WAIT = 7
85 RADIX_LOAD_TLB = 8
86 RADIX_FINISH = 9
87
88
89 # Process Table Record - near-identical to Page Table Record (same format)
90 # v3.0C Book III Section 6.7.6.2 p1004
91 class PRTBL(RecordObject):
92 def __init__(self, name=None):
93 super().__init__(name=name)
94 self.rpds = Signal(5) # Root Page Directory Size 59:63 LSB0 0:4
95 self.rts2 = Signal(3) # Radix Tree Size part 2 56:58 LSB0 5:7
96 self.rpdb = Signal(52) # Root Page Directory Base 4:55 LSB0 8:59
97 self.rsv2 = Signal(1) # reserved 3 LSB0 60
98 self.rts1 = Signal(2) # Radix Tree Size part 1 1:2 LSB0 61:62
99 self.rsv1 = Signal(1) # reserved 0 LSB0 63
100
101
102 class RegStage(RecordObject):
103 def __init__(self, name=None):
104 super().__init__(name=name)
105 # latched request from loadstore1
106 self.valid = Signal()
107 self.iside = Signal()
108 self.store = Signal()
109 self.priv = Signal()
110 self.addr = Signal(64)
111 self.inval_all = Signal()
112
113 # config SPRs
114 self.prtbl = Signal(64)
115 self.pid = Signal(32)
116
117 # internal state
118 self.state = Signal(State) # resets to IDLE
119 self.done = Signal()
120 self.err = Signal()
121
122 # there are 4 quadrants (0-3): here we only support 2 (pt0 and pt3)
123 # these are bits 62-63 of any given address.
124 # except in segment_check, bit 62 is ignored
125 # Quadrant Select can be seen in v3.0C 6.7.10 p1015 book III figure 36
126 # and is further described in 6.7.11.3 p1019
127 self.pgtbl0 = Signal(64)
128 self.pt0_valid = Signal()
129 self.pgtbl3 = Signal(64)
130 self.pt3_valid = Signal()
131
132 self.shift = Signal(6)
133 self.mask_size = Signal(5)
134 self.pgbase = Signal(56)
135 self.pde = Signal(64)
136 self.invalid = Signal()
137 self.badtree = Signal()
138 self.segerror = Signal()
139 self.perm_err = Signal()
140 self.rc_error = Signal()
141
142
143 # Page Table Record - note that HR bit is treated as part of rts below
144 # (near-identical to Process Table Record - same format)
145 # v3.0C Book III Section 6.7.6.1 p1003
146 class PGTBL(RecordObject):
147 def __init__(self, name=None):
148 super().__init__(name=name)
149 self.rpds = Signal(5) # Root Page Directory Size 59:63 LSB0 0:4
150 self.rts2 = Signal(3) # Radix Tree Size part 2 56:58 LSB0 5:7
151 self.rpdb = Signal(52) # Root Page Directory Base 4:55 LSB0 8:59
152 self.s = Signal(1) # Host Secure 3 LSB0 60
153 self.rts1 = Signal(2) # Radix Tree Size part 1 1:2 LSB0 61:62
154 self.hr = Signal(1) # Host Radix 0 LSB0 63
155
156
157 class MMU(Elaboratable):
158 """Radix MMU
159
160 Supports 4-level trees as in arch 3.0B, but not the
161 two-step translation for guests under a hypervisor
162 (i.e. there is no gRA -> hRA translation).
163 """
164 def __init__(self):
165 self.l_in = LoadStore1ToMMUType("l_in")
166 self.l_out = MMUToLoadStore1Type("l_out")
167 self.d_out = MMUToDCacheType("d_out")
168 self.d_in = DCacheToMMUType("d_in")
169 self.i_out = MMUToICacheType("i_out")
170
171 def radix_tree_idle(self, m, l_in, r, v):
172 """radix_tree_idle - the main decision-point. valid actions include:
173 * LDST incoming TLBIE request (invalidate TLB entry)
174 * LDST incoming RADIX walk request
175 * set either PRTBL or PID SPRs (which then fires a TLB invalidate)
176 """
177 comb = m.d.comb
178 sync = m.d.sync
179
180 pt_valid = Signal()
181 pgtbl = PGTBL("pgtbl")
182 rts = Signal(6)
183 mbits = Signal(6, name="mbits_idle")
184
185 with m.If(l_in.addr[63]): # quadrant 3
186 comb += pgtbl.eq(r.pgtbl3)
187 comb += pt_valid.eq(r.pt3_valid)
188 with m.Else():
189 comb += pgtbl.eq(r.pgtbl0)
190 comb += pt_valid.eq(r.pt0_valid)
191
192 # rts == radix tree size, number of address bits
193 # being translated. takes bits 5:7 and 61:62
194 comb += rts.eq(Cat(pgtbl.rts2, pgtbl.rts1, C(0)))
195
196 # mbits == number of address bits to index top
197 # level of tree. takes bits 0:4
198 comb += mbits.eq(pgtbl.rpds)
199
200 # set v.shift to rts so that we can use finalmask
201 # for the segment check.
202 # note: rpdb (52 bits long) is truncated to 48 bits
203 comb += v.shift.eq(rts)
204 comb += v.mask_size.eq(mbits[0:5])
205
206 # create the page base from root page directory base (48 bits with 8 0s)
207 comb += v.pgbase.eq(Cat(C(0, 8), pgtbl.rpdb[:48])) # bits 8:55
208
209 # request either TLB invalidate
210 # or start a RADIX walk
211
212 with m.If(l_in.valid):
213 comb += v.addr.eq(l_in.addr)
214 comb += v.iside.eq(l_in.iside)
215 comb += v.store.eq(~(l_in.load | l_in.iside))
216 comb += v.priv.eq(l_in.priv)
217
218 sync += Display("state %d l_in.valid addr %x iside %d store %d "
219 "rpdb %x rts %d mbits %d pt_valid %d",
220 v.state, v.addr, v.iside, v.store,
221 pgtbl.rpdb, rts, mbits, pt_valid)
222
223 with m.If(l_in.tlbie):
224 # Invalidate all iTLB/dTLB entries for
225 # tlbie with RB[IS] != 0 or RB[AP] != 0,
226 # or for slbia
227 comb += v.inval_all.eq(l_in.slbia
228 | l_in.addr[11]
229 | l_in.addr[10]
230 | l_in.addr[7]
231 | l_in.addr[6]
232 | l_in.addr[5]
233 )
234 # The RIC field of the tlbie instruction
235 # comes across on the sprn bus as bits 2--3.
236 # RIC=2 flushes process table caches.
237 with m.If(l_in.sprn[3]):
238 comb += v.pt0_valid.eq(0)
239 comb += v.pt3_valid.eq(0)
240 comb += v.state.eq(State.DO_TLBIE)
241 with m.Else():
242 comb += v.valid.eq(1)
243 with m.If(~pt_valid):
244 # need to fetch process table entry
245 # set v.shift so we can use finalmask
246 # for generating the process table
247 # entry address
248 prtbl = PRTBL("prtbl")
249 comb += prtbl.eq(r.prtbl)
250 comb += v.shift.eq(prtbl.rpds)
251 comb += v.state.eq(State.PROC_TBL_READ)
252
253 with m.Elif(mbits == 0):
254 # Use RPDS = 0 to disable radix tree walks
255 comb += v.state.eq(State.RADIX_FINISH)
256 comb += v.invalid.eq(1)
257 if(display_invalid):
258 sync += Display("MMUBUG: Use RPDS = 0 to disable"
259 " radix tree walks")
260 with m.Else():
261 comb += v.state.eq(State.SEGMENT_CHECK)
262
263 # set either PID or PRTBL SPRs
264 # (then invalidate TLBs)
265
266 with m.If(l_in.mtspr):
267 # Move to PID needs to invalidate L1 TLBs
268 # and cached pgtbl0 value.
269 # Move to PRTBL does that plus invalidating the cached
270 # pgtbl3 value as well.
271 with m.If(~l_in.sprn[9]):
272 comb += v.pid.eq(l_in.rs[0:32])
273 with m.Else():
274 comb += v.prtbl.eq(l_in.rs)
275 comb += v.pt3_valid.eq(0)
276
277 comb += v.pt0_valid.eq(0)
278 comb += v.inval_all.eq(1)
279 comb += v.state.eq(State.DO_TLBIE)
280
281 def proc_tbl_wait(self, m, v, r, data):
282 comb = m.d.comb
283 sync = m.d.sync
284 rts = Signal(6)
285 mbits = Signal(6, name="mbits_tbl_wait")
286 prtbl = PRTBL("prtblw")
287 comb += prtbl.eq(data)
288
289 with m.If(r.addr[63]): # top bit of quadrant selects pt3
290 comb += v.pgtbl3.eq(prtbl)
291 comb += v.pt3_valid.eq(1)
292 with m.Else():
293 comb += v.pgtbl0.eq(prtbl)
294 comb += v.pt0_valid.eq(1)
295
296 # rts == radix tree size, # address bits being translated
297 comb += rts.eq(Cat(prtbl.rts2, prtbl.rts1, C(0)))
298
299 # mbits == # address bits to index top level of tree
300 comb += mbits.eq(prtbl.rpds[0:5])
301
302 # set v.shift to rts so that we can use finalmask for the segment check
303 comb += v.shift.eq(rts)
304 comb += v.mask_size.eq(mbits[0:5])
305
306 # create the page base from root page directory base (48 bits with 8 0s)
307 comb += v.pgbase.eq(Cat(C(0, 8), prtbl.rpdb[:48])) # bits 8:55
308
309 with m.If(mbits):
310 comb += v.state.eq(State.SEGMENT_CHECK)
311 sync += Display("PROC TBL %d data %x rts1 %x rts2 %x rts %d "
312 "rpdb %x mbits %d pgbase %x "
313 " pt0_valid %d, pt3_valid %d",
314 v.state, data, prtbl.rts1, prtbl.rts2, rts,
315 prtbl.rpdb, mbits, v.pgbase,
316 v.pt0_valid, v.pt3_valid)
317 with m.Else():
318 comb += v.state.eq(State.RADIX_FINISH)
319 comb += v.invalid.eq(1)
320 if (display_invalid): m.d.sync += Display("MMU: mbits is invalid")
321
322 def radix_read_wait(self, m, v, r, d_in, data):
323 comb = m.d.comb
324 sync = m.d.sync
325
326 rpte = RTPTE(name="radix_rpte") # page-table (leaf) entry
327 rpde = RTPDE(name="radix_rpde") # page-directory (non-leaf) entry
328
329 perm_ok = Signal()
330 rc_ok = Signal()
331 mbits = Signal(6, name="mbits_read_wait")
332 valid = rpte.valid
333 eaa = rpte.eaa
334 leaf = rpte.leaf
335 badtree = Signal()
336
337 sync += Display("RDW %016x done %d "
338 "perm %d rc %d mbits %d shf %d "
339 "valid %d leaf %d bad %d",
340 data, d_in.done, perm_ok, rc_ok,
341 mbits, r.shift, valid, leaf, badtree)
342
343 # set pde and interpret as Radix Tree Page Table Entry (leaf=1 case)
344 comb += v.pde.eq(data)
345 comb += rpte.eq(data)
346 comb += rpde.eq(data)
347
348 with m.If(valid):
349 # valid & leaf: RADIX Page-Table Entry
350 with m.If(leaf):
351 # check permissions and RC bits
352 with m.If(r.priv | ~eaa[EAA_PRIV]):
353 with m.If(r.iside): # instruction-side request
354 # no IAMR, so no KUEP support for now
355 # deny execute permission if cache inhibited
356 comb += perm_ok.eq(eaa[EAA_EXE] & ~rpte.att[1])
357 with m.Else():
358 # Load/Store (read/write)
359 comb += perm_ok.eq(eaa[EAA_WR] |
360 (eaa[EAA_RD] & ~r.store))
361 comb += rc_ok.eq(rpte.r & (rpte.c | ~r.store))
362
363 # permissions / rc ok, load TLB, otherwise report error
364 with m.If(perm_ok & rc_ok):
365 comb += v.state.eq(State.RADIX_LOAD_TLB)
366 sync += Display("RADIX LEAF data %x att %x eaa %x "
367 "R %d C %d "
368 "shift %d pgbase %x ",
369 data, rpte.att, eaa,
370 rpte.r, rpte.c,
371 v.shift, v.pgbase
372 )
373 with m.Else():
374 comb += v.state.eq(State.RADIX_FINISH)
375 comb += v.perm_err.eq(~perm_ok)
376 # permission error takes precedence over RC error
377 comb += v.rc_error.eq(perm_ok)
378
379 # valid & !leaf: RADIX Page-Directory Entry
380 with m.Else():
381 comb += mbits.eq(rpde.nls) # 5 bits NLS into 6-bit-long mbits
382 comb += badtree.eq((mbits < 5) |
383 (mbits > 16) |
384 (mbits > r.shift))
385 with m.If(badtree):
386 comb += v.state.eq(State.RADIX_FINISH)
387 comb += v.badtree.eq(1)
388 with m.Else():
389 comb += v.shift.eq(r.shift - mbits)
390 comb += v.mask_size.eq(mbits)
391 # pagebase is first 48 bits of NLB, shifted up 1 byte
392 comb += v.pgbase.eq(Cat(C(0, 8), rpde.nlb[:48]))
393 comb += v.state.eq(State.RADIX_LOOKUP)
394
395 with m.Else():
396 # non-present PTE, generate a DSI
397 comb += v.state.eq(State.RADIX_FINISH)
398 comb += v.invalid.eq(1)
399 if (display_invalid):
400 sync += Display("MMU: non-present PTE, generate a DSI")
401
402 def segment_check(self, m, v, r, data, finalmask):
403 """segment_check: checks validity of the request before doing a
404 RADIX lookup. reports either segment error or bad tree if not ok
405 """
406 comb = m.d.comb
407
408 mbits = Signal(6, name="mbits_check")
409 nonzero = Signal()
410 comb += mbits.eq(r.mask_size)
411 comb += v.shift.eq(r.shift + (31 - 12) - mbits)
412 comb += nonzero.eq((r.addr[31:62] & ~finalmask[0:31]).bool())
413 with m.If((r.addr[63] != r.addr[62]) # pt3 == 0b11 and pt1 == 0b00
414 | nonzero):
415 comb += v.state.eq(State.RADIX_FINISH)
416 comb += v.segerror.eq(1)
417 with m.Elif((mbits < 5) | (mbits > 16) |
418 (mbits > (r.shift + (31-12)))):
419 comb += v.state.eq(State.RADIX_FINISH)
420 comb += v.badtree.eq(1)
421 with m.Else():
422 comb += v.state.eq(State.RADIX_LOOKUP)
423
424 def mmu_0(self, m, r, rin, l_in, l_out, d_out, addrsh, mask):
425 comb = m.d.comb
426 sync = m.d.sync
427
428 # Multiplex internal SPR values back to loadstore1,
429 # selected by l_in.sprn.
430 with m.If(l_in.sprn[9]):
431 comb += l_out.sprval.eq(r.prtbl)
432 with m.Else():
433 comb += l_out.sprval.eq(r.pid)
434
435 with m.If(rin.valid):
436 sync += Display("MMU got tlb miss for %x", rin.addr)
437
438 with m.If(l_out.done):
439 sync += Display("MMU completing op without error")
440
441 with m.If(l_out.err):
442 sync += Display("MMU completing op with err invalid="
443 "%d badtree=%d", l_out.invalid, l_out.badtree)
444
445 with m.If(rin.state == State.RADIX_LOOKUP):
446 sync += Display ("radix lookup shift=%x msize=%x",
447 rin.shift, mask)
448
449 with m.If(r.state == State.RADIX_LOOKUP):
450 sync += Display(f"send load addr=%x addrsh=%x mask=%x",
451 d_out.addr, addrsh, mask)
452
453 # update the internal register
454 sync += r.eq(rin)
455
456 def elaborate(self, platform):
457 m = Module()
458
459 comb = m.d.comb
460 sync = m.d.sync
461
462 addrsh = Signal(16)
463 mask = Signal(16)
464 finalmask = Signal(44)
465
466 self.rin = rin = RegStage("r_in")
467 r = RegStage("r")
468
469 # get access to prtbl and pid for debug / testing purposes ONLY
470 # (actually, not needed, because setup_regs() triggers mmu direct)
471 # self._prtbl = r.prtbl
472 # self._pid = r.pid
473
474 l_in = self.l_in
475 l_out = self.l_out
476 d_out = self.d_out
477 d_in = self.d_in
478 i_out = self.i_out
479
480 self.mmu_0(m, r, rin, l_in, l_out, d_out, addrsh, mask)
481
482 v = RegStage("v")
483 dcreq = Signal()
484 tlb_load = Signal()
485 itlb_load = Signal()
486 tlbie_req = Signal()
487 prtbl_rd = Signal()
488 effpid = Signal(32)
489 prtb_adr = Signal(64)
490 pgtb_adr = Signal(64)
491 pte = Signal(64)
492 tlb_data = Signal(64)
493 addr = Signal(64)
494
495 comb += v.eq(r)
496 comb += v.valid.eq(0)
497 comb += v.done.eq(0)
498 comb += v.err.eq(0)
499 comb += v.invalid.eq(0)
500 comb += v.badtree.eq(0)
501 comb += v.segerror.eq(0)
502 comb += v.perm_err.eq(0)
503 comb += v.rc_error.eq(0)
504 comb += v.inval_all.eq(0)
505
506 # Radix tree data structures in memory are
507 # big-endian, so we need to byte-swap them
508 data = byte_reverse(m, "data", d_in.data, 8)
509
510 # generate mask for extracting address fields for PTE addr generation
511 m.submodules.pte_mask = pte_mask = Mask(16-5)
512 pte_mask.mask.name = "pte_mask"
513 comb += pte_mask.shift.eq(r.mask_size - 5)
514 comb += mask.eq(Cat(C(0x1f, 5), pte_mask.mask))
515
516 # generate mask for extracting address bits to go in
517 # TLB entry in order to support pages > 4kB
518 m.submodules.tlb_mask = tlb_mask = Mask(44)
519 tlb_mask.mask.name = "tlb_mask"
520 comb += tlb_mask.shift.eq(r.shift)
521 comb += finalmask.eq(tlb_mask.mask)
522
523 # Shift address bits 61--12 right by 0--47 bits and
524 # supply the least significant 16 bits of the result.
525 comb += addrsh.eq(r.addr[12:62] >> r.shift)
526
527 with m.If(r.state != State.IDLE):
528 sync += Display("MMU state %d %016x", r.state, data)
529 sync += Display("addrsh %x r.shift %d r.addr[12:62] %x",
530 addrsh, r.shift, r.addr[12:62])
531
532 ##########
533 # Main FSM
534 ##########
535
536 with m.Switch(r.state):
537 with m.Case(State.IDLE):
538 self.radix_tree_idle(m, l_in, r, v)
539
540 with m.Case(State.DO_TLBIE):
541 comb += dcreq.eq(1)
542 comb += tlbie_req.eq(1)
543 comb += v.state.eq(State.TLB_WAIT)
544
545 with m.Case(State.TLB_WAIT):
546 with m.If(d_in.done):
547 comb += v.state.eq(State.RADIX_FINISH)
548
549 with m.Case(State.PROC_TBL_READ):
550 sync += Display(" TBL_READ %016x", prtb_adr)
551 comb += dcreq.eq(1)
552 comb += prtbl_rd.eq(1)
553 comb += v.state.eq(State.PROC_TBL_WAIT)
554
555 with m.Case(State.PROC_TBL_WAIT):
556 with m.If(d_in.done):
557 self.proc_tbl_wait(m, v, r, data)
558
559 with m.If(d_in.err):
560 comb += v.state.eq(State.RADIX_FINISH)
561 comb += v.badtree.eq(1)
562
563 with m.Case(State.SEGMENT_CHECK):
564 self.segment_check(m, v, r, data, finalmask)
565
566 with m.Case(State.RADIX_LOOKUP):
567 sync += Display(" RADIX_LOOKUP")
568 comb += dcreq.eq(1)
569 comb += v.state.eq(State.RADIX_READ_WAIT)
570
571 with m.Case(State.RADIX_READ_WAIT):
572 sync += Display(" READ_WAIT")
573 with m.If(d_in.done):
574 self.radix_read_wait(m, v, r, d_in, data)
575 with m.If(d_in.err):
576 comb += v.state.eq(State.RADIX_FINISH)
577 comb += v.badtree.eq(1)
578
579 with m.Case(State.RADIX_LOAD_TLB):
580 comb += tlb_load.eq(1)
581 with m.If(~r.iside):
582 comb += dcreq.eq(1)
583 comb += v.state.eq(State.TLB_WAIT)
584 with m.Else():
585 comb += itlb_load.eq(1)
586 comb += v.state.eq(State.IDLE)
587
588 with m.Case(State.RADIX_FINISH):
589 sync += Display(" RADIX_FINISH")
590 comb += v.state.eq(State.IDLE)
591
592 # check and report either error or done.
593 with m.If((v.state == State.RADIX_FINISH) |
594 ((v.state == State.RADIX_LOAD_TLB) & r.iside)):
595 comb += v.err.eq(v.invalid | v.badtree | v.segerror
596 | v.perm_err | v.rc_error)
597 comb += v.done.eq(~v.err)
598
599 # PID is only valid if MSB of address is zero, top 2 bits are Quadrant
600 with m.If(~r.addr[63]): # quadrant 0 (pt0)
601 comb += effpid.eq(r.pid)
602
603 # calculate Process Table Address
604 pr24 = Signal(24, reset_less=True)
605 prtbla = PRTBL("prtbla")
606 comb += prtbla.eq(r.prtbl)
607 rpdb = prtbla.rpdb
608 comb += pr24.eq(masked(rpdb[4:28], effpid[8:32], finalmask))
609 comb += prtb_adr.eq(Cat(C(0, 4), effpid[0:8], pr24, rpdb[28:48]))
610
611 # calculate Page Table Address
612 pg16 = Signal(16, reset_less=True)
613 comb += pg16.eq(masked(r.pgbase[3:19], addrsh, mask))
614 comb += pgtb_adr.eq(Cat(C(0, 3), pg16, r.pgbase[19:56]))
615
616 # calculate Page Table Entry from Real Page Number (leaf=1, RTPTE)
617 rpte = RTPTE(name="rpte")
618 comb += rpte.eq(r.pde)
619 pd44 = Signal(44, reset_less=True)
620 comb += pd44.eq(masked(rpte.rpn, r.addr[12:56], finalmask))
621 comb += pte.eq(Cat(r.pde[0:12], pd44))
622
623 # update registers
624 comb += rin.eq(v)
625
626 # drive outputs
627 with m.If(tlbie_req):
628 comb += addr.eq(r.addr)
629 with m.Elif(tlb_load):
630 comb += addr.eq(Cat(C(0, 12), r.addr[12:64]))
631 comb += tlb_data.eq(pte)
632 with m.Elif(prtbl_rd):
633 comb += addr.eq(prtb_adr)
634 with m.Else():
635 comb += addr.eq(pgtb_adr)
636 sync += Display(f"pagetable pg16=%x addrsh %x mask %x pgbase=%x "
637 "pgbase[19:56]=%x",
638 pg16, addrsh, mask, r.pgbase, r.pgbase[19:56])
639
640 # connect to other interfaces: LDST, D-Cache, I-Cache
641 comb += l_out.done.eq(r.done)
642 comb += l_out.err.eq(r.err)
643 comb += l_out.invalid.eq(r.invalid)
644 comb += l_out.badtree.eq(r.badtree)
645 comb += l_out.segerr.eq(r.segerror)
646 comb += l_out.perm_error.eq(r.perm_err)
647 comb += l_out.rc_error.eq(r.rc_error)
648
649 comb += d_out.valid.eq(dcreq)
650 comb += d_out.tlbie.eq(tlbie_req)
651 comb += d_out.doall.eq(r.inval_all)
652 comb += d_out.tlbld.eq(tlb_load)
653 comb += d_out.addr.eq(addr)
654 comb += d_out.pte.eq(tlb_data)
655
656 comb += i_out.tlbld.eq(itlb_load)
657 comb += i_out.tlbie.eq(tlbie_req)
658 comb += i_out.doall.eq(r.inval_all)
659 comb += i_out.addr.eq(addr)
660 comb += i_out.pte.eq(tlb_data)
661
662 return m
663
664 stop = False
665
666 def dcache_get(dut):
667 """simulator process for getting memory load requests
668 """
669
670 global stop
671
672 def b(x):
673 return int.from_bytes(x.to_bytes(8, byteorder='little'),
674 byteorder='big', signed=False)
675
676 mem = {0x0: 0x000000, # to get mtspr prtbl working
677
678 0x10000: # PARTITION_TABLE_2
679 # HR=1 RTS1=0x2 PRTB=0x300 RTS2=0x5 PRTS=0xb
680 b(0xc0000000000030ad),
681
682 0x30000: # RADIX_ROOT_PTE
683 # V = 1 L = 0 NLB = 0x400 NLS = 9
684 b(0x8000000000040009),
685
686 0x40000: # RADIX_SECOND_LEVEL
687 # V = 1 L = 1 SW = 0 RPN = 0
688 # R = 1 C = 1 ATT = 0 EAA 0x7
689 b(0xc000000000000187),
690
691 #
692 # slightly different from radix_walk_example.txt: address in microwatt
693 # has the top bit set to indicate hypervisor. here, Quadrant 3's
694 # process table entry is put instead into Quadrant 0. the entry
695 # PROCESS_TABLE_3 should, strictly speaking, be at 0x1000010
696
697 # 0x1000000: # PROCESS_TABLE_3 (pt0_valid)
698 # # RTS1 = 0x2 RPDB = 0x300 RTS2 = 0x5 RPDS = 12
699 # b(0x40000000000300ac),
700
701 0x1000000: # PROCESS_TABLE_3 (pt3_valid)
702 # RTS1 = 0x2 RPDB = 0x300 RTS2 = 0x5 RPDS = 13
703 b(0x40000000000300ad),
704 }
705
706 # microwatt mmu.bin first part of test 2.
707 # PRTBL must be set to 0x12000, PID to 1
708 mem = {
709 0x0: 0x000000, # to get mtspr prtbl working
710 0x13920: 0x86810000000000c0, # leaf, supposed to be at 0x13920
711 0x10000: 0x0930010000000080, # directory node
712 0x12010: 0x0a00010000000000, # page table
713 0x124000: 0x0000000badc0ffee, # memory to be looked up
714 }
715
716 # microwatt mmu.bin first part of test 4.
717 # PRTBL must be set to 0x12000, PID to 1
718 mem = {
719 0x0: 0x000000, # to get mtspr prtbl working
720 0x13858: 0x86a10000000000c0, # leaf node
721 0x10000: 0x0930010000000080, # directory node
722 0x12010: 0x0a00010000000000, # page table
723 }
724
725 while not stop:
726 while True: # wait for dc_valid
727 if stop:
728 return
729 dc_valid = yield (dut.d_out.valid)
730 tlbld = yield (dut.d_out.tlbld)
731 if dc_valid:
732 break
733 yield
734 addr = yield dut.d_out.addr
735 if tlbld:
736 pte = yield dut.d_out.pte
737 print (" DCACHE PTE %x -> %x" % (pte, addr))
738 yield dut.d_in.done.eq(1)
739 yield
740 yield dut.d_in.done.eq(0)
741 continue
742
743 if addr not in mem:
744 print (" DCACHE LOOKUP FAIL %x" % (addr))
745 stop = True
746 return
747
748 yield
749 data = mem[addr]
750 yield dut.d_in.data.eq(data)
751 print (" DCACHE GET %x data %x" % (addr, data))
752 yield dut.d_in.done.eq(1)
753 yield
754 yield dut.d_in.done.eq(0)
755
756
757 def mmu_wait(dut):
758 global stop
759 while not stop: # wait for dc_valid / err
760 d_valid = yield (dut.d_out.valid)
761 if d_valid:
762 tlbld = yield (dut.d_out.tlbld)
763 addr = yield (dut.d_out.addr)
764 print ("addr %x tlbld %d" % (addr, tlbld))
765 l_done = yield (dut.l_out.done)
766 l_err = yield (dut.l_out.err)
767 l_badtree = yield (dut.l_out.badtree)
768 l_permerr = yield (dut.l_out.perm_error)
769 l_rc_err = yield (dut.l_out.rc_error)
770 l_segerr = yield (dut.l_out.segerr)
771 l_invalid = yield (dut.l_out.invalid)
772 if (l_done or l_err or l_badtree or
773 l_permerr or l_rc_err or l_segerr or l_invalid):
774 break
775 yield
776 yield dut.l_in.valid.eq(0) # data already in MMU by now
777 yield dut.l_in.mtspr.eq(0) # captured by RegStage(s)
778 yield dut.l_in.load.eq(0) # can reset everything safely
779
780
781 def mmu_sim(dut):
782 global stop
783
784 # microwatt PRTBL = 0x12000, other test is 0x1000000
785 #prtbl = 0x100000
786 #pidr = 0x0
787 prtbl = 0x12000
788 pidr = 0x1
789
790 # MMU MTSPR set prtbl
791 yield dut.l_in.mtspr.eq(1)
792 yield dut.l_in.sprn[9].eq(1) # totally fake way to set SPR=prtbl
793 yield dut.l_in.rs.eq(prtbl) # set process table
794 yield dut.l_in.valid.eq(1)
795 yield from mmu_wait(dut)
796 yield
797 yield dut.l_in.sprn.eq(0)
798 yield dut.l_in.rs.eq(0)
799 yield
800
801 prtbl = yield (dut.rin.prtbl)
802 print ("prtbl after MTSPR %x" % prtbl)
803 assert prtbl == prtbl
804
805 if True: # microwatt test set PIDR
806 # MMU MTSPR set PIDR = 1
807 yield dut.l_in.mtspr.eq(1)
808 yield dut.l_in.sprn[9].eq(0) # totally fake way to set SPR=pidr
809 yield dut.l_in.rs.eq(pidr) # set process table
810 yield dut.l_in.valid.eq(1)
811 yield from mmu_wait(dut)
812 yield
813 yield dut.l_in.sprn.eq(0)
814 yield dut.l_in.rs.eq(0)
815 yield
816
817 #yield dut.rin.prtbl.eq(0x1000000) # manually set process table
818 #yield
819
820 #addr = 0x10000 # original test
821 #addr = 0x124108 # microwatt mmu.bin test 2
822 addr = 0x10b0d8 # microwatt mmu.bin test 4
823
824 # MMU PTE request
825 yield dut.l_in.load.eq(1)
826 yield dut.l_in.priv.eq(1)
827 yield dut.l_in.addr.eq(addr)
828 yield dut.l_in.valid.eq(1)
829 yield from mmu_wait(dut)
830
831 addr = yield dut.d_out.addr
832 pte = yield dut.d_out.pte
833 tlb_ld = yield dut.d_out.tlbld
834 l_done = yield (dut.l_out.done)
835 l_err = yield (dut.l_out.err)
836 l_badtree = yield (dut.l_out.badtree)
837 print ("translated done %d err %d badtree %d "
838 "addr %x pte %x tlb_ld %d" % \
839 (l_done, l_err, l_badtree, addr, pte, tlb_ld))
840
841 yield
842 yield dut.l_in.priv.eq(0)
843 yield dut.l_in.addr.eq(0)
844
845
846 stop = True
847
848
849 def test_mmu():
850 dut = MMU()
851 vl = rtlil.convert(dut, ports=[])#dut.ports())
852 with open("test_mmu.il", "w") as f:
853 f.write(vl)
854
855 m = Module()
856 m.submodules.mmu = dut
857
858 # nmigen Simulation
859 sim = Simulator(m)
860 sim.add_clock(1e-6)
861
862 sim.add_sync_process(wrap(mmu_sim(dut)))
863 sim.add_sync_process(wrap(dcache_get(dut)))
864 with sim.write_vcd('test_mmu.vcd'):
865 sim.run()
866
867 if __name__ == '__main__':
868 test_mmu()