5d60a66e8bbb6690775c36399393383e0b984f4c
[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)
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)
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)
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)
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)
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=%d msize=%d",
447 addrsh, mask)
448
449 with m.If(r.state == State.RADIX_LOOKUP):
450 sync += Display(f"send load addr=%x addrsh=%d 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()
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 += dcreq.eq(0)
498 comb += v.done.eq(0)
499 comb += v.err.eq(0)
500 comb += v.invalid.eq(0)
501 comb += v.badtree.eq(0)
502 comb += v.segerror.eq(0)
503 comb += v.perm_err.eq(0)
504 comb += v.rc_error.eq(0)
505 comb += tlb_load.eq(0)
506 comb += itlb_load.eq(0)
507 comb += tlbie_req.eq(0)
508 comb += v.inval_all.eq(0)
509 comb += prtbl_rd.eq(0)
510
511 # Radix tree data structures in memory are
512 # big-endian, so we need to byte-swap them
513 data = byte_reverse(m, "data", d_in.data, 8)
514
515 # generate mask for extracting address fields for PTE addr generation
516 m.submodules.pte_mask = pte_mask = Mask(16-5)
517 comb += pte_mask.shift.eq(r.mask_size - 5)
518 comb += mask.eq(Cat(C(0x1f, 5), pte_mask.mask))
519
520 # generate mask for extracting address bits to go in
521 # TLB entry in order to support pages > 4kB
522 m.submodules.tlb_mask = tlb_mask = Mask(44)
523 comb += tlb_mask.shift.eq(r.shift)
524 comb += finalmask.eq(tlb_mask.mask)
525
526 # Shift address bits 61--12 right by 0--47 bits and
527 # supply the least significant 16 bits of the result.
528 comb += addrsh.eq(r.addr[12:62] << r.shift)
529
530 with m.If(r.state != State.IDLE):
531 sync += Display("MMU state %d %016x", r.state, data)
532
533 ##########
534 # Main FSM
535 ##########
536
537 with m.Switch(r.state):
538 with m.Case(State.IDLE):
539 self.radix_tree_idle(m, l_in, r, v)
540
541 with m.Case(State.DO_TLBIE):
542 comb += dcreq.eq(1)
543 comb += tlbie_req.eq(1)
544 comb += v.state.eq(State.TLB_WAIT)
545
546 with m.Case(State.TLB_WAIT):
547 with m.If(d_in.done):
548 comb += v.state.eq(State.RADIX_FINISH)
549
550 with m.Case(State.PROC_TBL_READ):
551 sync += Display(" TBL_READ %016x", prtb_adr)
552 comb += dcreq.eq(1)
553 comb += prtbl_rd.eq(1)
554 comb += v.state.eq(State.PROC_TBL_WAIT)
555
556 with m.Case(State.PROC_TBL_WAIT):
557 with m.If(d_in.done):
558 self.proc_tbl_wait(m, v, r, data)
559
560 with m.If(d_in.err):
561 comb += v.state.eq(State.RADIX_FINISH)
562 comb += v.badtree.eq(1)
563
564 with m.Case(State.SEGMENT_CHECK):
565 self.segment_check(m, v, r, data, finalmask)
566
567 with m.Case(State.RADIX_LOOKUP):
568 sync += Display(" RADIX_LOOKUP")
569 comb += dcreq.eq(1)
570 comb += v.state.eq(State.RADIX_READ_WAIT)
571
572 with m.Case(State.RADIX_READ_WAIT):
573 sync += Display(" READ_WAIT")
574 with m.If(d_in.done):
575 self.radix_read_wait(m, v, r, d_in, data)
576 with m.If(d_in.err):
577 comb += v.state.eq(State.RADIX_FINISH)
578 comb += v.badtree.eq(1)
579
580 with m.Case(State.RADIX_LOAD_TLB):
581 comb += tlb_load.eq(1)
582 with m.If(~r.iside):
583 comb += dcreq.eq(1)
584 comb += v.state.eq(State.TLB_WAIT)
585 with m.Else():
586 comb += itlb_load.eq(1)
587 comb += v.state.eq(State.IDLE)
588
589 with m.Case(State.RADIX_FINISH):
590 sync += Display(" RADIX_FINISH")
591 comb += v.state.eq(State.IDLE)
592
593 # check and report either error or done.
594 with m.If((v.state == State.RADIX_FINISH) |
595 ((v.state == State.RADIX_LOAD_TLB) & r.iside)):
596 comb += v.err.eq(v.invalid | v.badtree | v.segerror
597 | v.perm_err | v.rc_error)
598 comb += v.done.eq(~v.err)
599
600 # PID is only valid if MSB of address is zero, top 2 bits are Quadrant
601 with m.If(~r.addr[63]): # quadrant 0 (pt0)
602 comb += effpid.eq(r.pid)
603
604 # calculate Process Table Address
605 pr24 = Signal(24, reset_less=True)
606 prtbla = PRTBL("prtbla")
607 comb += prtbla.eq(r.prtbl)
608 rpdb = prtbla.rpdb
609 comb += pr24.eq(masked(rpdb[4:28], effpid[8:32], finalmask))
610 comb += prtb_adr.eq(Cat(C(0, 4), effpid[0:8], pr24, rpdb[28:48]))
611
612 # calculate Page Table Address
613 pg16 = Signal(16, reset_less=True)
614 comb += pg16.eq(masked(r.pgbase[3:19], addrsh, mask))
615 comb += pgtb_adr.eq(Cat(C(0, 3), pg16, r.pgbase[19:56]))
616
617 # calculate Page Table Entry from Real Page Number (leaf=1, RTPTE)
618 rpte = RTPTE(name="rpte")
619 comb += rpte.eq(r.pde)
620 pd44 = Signal(44, reset_less=True)
621 comb += pd44.eq(masked(rpte.rpn, r.addr[12:56], finalmask))
622 comb += pte.eq(Cat(r.pde[0:12], pd44))
623
624 # update registers
625 comb += rin.eq(v)
626
627 # drive outputs
628 with m.If(tlbie_req):
629 comb += addr.eq(r.addr)
630 with m.Elif(tlb_load):
631 comb += addr.eq(Cat(C(0, 12), r.addr[12:64]))
632 comb += tlb_data.eq(pte)
633 with m.Elif(prtbl_rd):
634 comb += addr.eq(prtb_adr)
635 with m.Else():
636 comb += addr.eq(pgtb_adr)
637
638 # connect to other interfaces: LDST, D-Cache, I-Cache
639 comb += l_out.done.eq(r.done)
640 comb += l_out.err.eq(r.err)
641 comb += l_out.invalid.eq(r.invalid)
642 comb += l_out.badtree.eq(r.badtree)
643 comb += l_out.segerr.eq(r.segerror)
644 comb += l_out.perm_error.eq(r.perm_err)
645 comb += l_out.rc_error.eq(r.rc_error)
646
647 comb += d_out.valid.eq(dcreq)
648 comb += d_out.tlbie.eq(tlbie_req)
649 comb += d_out.doall.eq(r.inval_all)
650 comb += d_out.tlbld.eq(tlb_load)
651 comb += d_out.addr.eq(addr)
652 comb += d_out.pte.eq(tlb_data)
653
654 comb += i_out.tlbld.eq(itlb_load)
655 comb += i_out.tlbie.eq(tlbie_req)
656 comb += i_out.doall.eq(r.inval_all)
657 comb += i_out.addr.eq(addr)
658 comb += i_out.pte.eq(tlb_data)
659
660 return m
661
662 stop = False
663
664 def dcache_get(dut):
665 """simulator process for getting memory load requests
666 """
667
668 global stop
669
670 def b(x):
671 return int.from_bytes(x.to_bytes(8, byteorder='little'),
672 byteorder='big', signed=False)
673
674 mem = {0x0: 0x000000, # to get mtspr prtbl working
675
676 0x10000: # PARTITION_TABLE_2
677 # HR=1 RTS1=0x2 PRTB=0x300 RTS2=0x5 PRTS=0xb
678 b(0xc0000000000030ad),
679
680 0x30000: # RADIX_ROOT_PTE
681 # V = 1 L = 0 NLB = 0x400 NLS = 9
682 b(0x8000000000040009),
683
684 0x40000: # RADIX_SECOND_LEVEL
685 # V = 1 L = 1 SW = 0 RPN = 0
686 # R = 1 C = 1 ATT = 0 EAA 0x7
687 b(0xc000000000000187),
688
689 #
690 # slightly different from radix_walk_example.txt: address in microwatt
691 # has the top bit set to indicate hypervisor. here, Quadrant 3's
692 # process table entry is put instead into Quadrant 0. the entry
693 # PROCESS_TABLE_3 should, strictly speaking, be at 0x1000010
694
695 # 0x1000000: # PROCESS_TABLE_3 (pt0_valid)
696 # # RTS1 = 0x2 RPDB = 0x300 RTS2 = 0x5 RPDS = 12
697 # b(0x40000000000300ac),
698
699 0x1000000: # PROCESS_TABLE_3 (pt3_valid)
700 # RTS1 = 0x2 RPDB = 0x300 RTS2 = 0x5 RPDS = 13
701 b(0x40000000000300ad),
702 }
703
704 # microwatt mmu.bin first part of test 2.
705 # PRTBL must be set to 0x12000, PID to 1
706 mem = {
707 0x0: 0x000000, # to get mtspr prtbl working
708 0x13920: 0x86810000000000c0, # leaf, supposed to be at 0x13920
709 0x10000: 0x0930010000000080, # directory node
710 0x12010: 0x0a00010000000000, # page table
711 0x124000: 0x0000000badc0ffee, # memory to be looked up
712 }
713
714
715 while not stop:
716 while True: # wait for dc_valid
717 if stop:
718 return
719 dc_valid = yield (dut.d_out.valid)
720 if dc_valid:
721 break
722 yield
723 addr = yield dut.d_out.addr
724 if addr not in mem:
725 print (" DCACHE LOOKUP FAIL %x" % (addr))
726 stop = True
727 return
728
729 yield
730 data = mem[addr]
731 yield dut.d_in.data.eq(data)
732 print (" DCACHE GET %x data %x" % (addr, data))
733 yield dut.d_in.done.eq(1)
734 yield
735 yield dut.d_in.done.eq(0)
736
737
738 def mmu_wait(dut):
739 global stop
740 while not stop: # wait for dc_valid / err
741 d_valid = yield (dut.d_out.valid)
742 if d_valid:
743 tlbld = yield (dut.d_out.tlbld)
744 addr = yield (dut.d_out.addr)
745 print ("addr %x tlbld %d" % (addr, tlbld))
746 l_done = yield (dut.l_out.done)
747 l_err = yield (dut.l_out.err)
748 l_badtree = yield (dut.l_out.badtree)
749 l_permerr = yield (dut.l_out.perm_error)
750 l_rc_err = yield (dut.l_out.rc_error)
751 l_segerr = yield (dut.l_out.segerr)
752 l_invalid = yield (dut.l_out.invalid)
753 if (l_done or l_err or l_badtree or
754 l_permerr or l_rc_err or l_segerr or l_invalid):
755 break
756 yield
757 yield dut.l_in.valid.eq(0) # data already in MMU by now
758 yield dut.l_in.mtspr.eq(0) # captured by RegStage(s)
759 yield dut.l_in.load.eq(0) # can reset everything safely
760
761
762 def mmu_sim(dut):
763 global stop
764
765 # microwatt PRTBL = 0x12000, other test is 0x1000000
766 #prtbl = 0x100000
767 #pidr = 0x0
768 prtbl = 0x12000
769 pidr = 0x1
770
771 # MMU MTSPR set prtbl
772 yield dut.l_in.mtspr.eq(1)
773 yield dut.l_in.sprn[9].eq(1) # totally fake way to set SPR=prtbl
774 yield dut.l_in.rs.eq(prtbl) # set process table
775 yield dut.l_in.valid.eq(1)
776 yield from mmu_wait(dut)
777 yield
778 yield dut.l_in.sprn.eq(0)
779 yield dut.l_in.rs.eq(0)
780 yield
781
782 prtbl = yield (dut.rin.prtbl)
783 print ("prtbl after MTSPR %x" % prtbl)
784 assert prtbl == prtbl
785
786 if True: # microwatt test set PIDR
787 # MMU MTSPR set PIDR = 1
788 yield dut.l_in.mtspr.eq(1)
789 yield dut.l_in.sprn[9].eq(0) # totally fake way to set SPR=pidr
790 yield dut.l_in.rs.eq(pidr) # set process table
791 yield dut.l_in.valid.eq(1)
792 yield from mmu_wait(dut)
793 yield
794 yield dut.l_in.sprn.eq(0)
795 yield dut.l_in.rs.eq(0)
796 yield
797
798 #yield dut.rin.prtbl.eq(0x1000000) # manually set process table
799 #yield
800
801 #addr = 0x10000
802 addr = 0x124108
803
804 # MMU PTE request
805 yield dut.l_in.load.eq(1)
806 yield dut.l_in.priv.eq(1)
807 yield dut.l_in.addr.eq(addr)
808 yield dut.l_in.valid.eq(1)
809 yield from mmu_wait(dut)
810
811 addr = yield dut.d_out.addr
812 pte = yield dut.d_out.pte
813 tlb_ld = yield dut.d_out.tlbld
814 l_done = yield (dut.l_out.done)
815 l_err = yield (dut.l_out.err)
816 l_badtree = yield (dut.l_out.badtree)
817 print ("translated done %d err %d badtree %d "
818 "addr %x pte %x tlb_ld %d" % \
819 (l_done, l_err, l_badtree, addr, pte, tlb_ld))
820
821 yield
822 yield dut.l_in.priv.eq(0)
823 yield dut.l_in.addr.eq(0)
824
825
826 stop = True
827
828
829 def test_mmu():
830 dut = MMU()
831 vl = rtlil.convert(dut, ports=[])#dut.ports())
832 with open("test_mmu.il", "w") as f:
833 f.write(vl)
834
835 m = Module()
836 m.submodules.mmu = dut
837
838 # nmigen Simulation
839 sim = Simulator(m)
840 sim.add_clock(1e-6)
841
842 sim.add_sync_process(wrap(mmu_sim(dut)))
843 sim.add_sync_process(wrap(dcache_get(dut)))
844 with sim.write_vcd('test_mmu.vcd'):
845 sim.run()
846
847 if __name__ == '__main__':
848 test_mmu()