4850269028ccaa1e68a3c2280f70b0b0d7d16bae
[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 # microwatt mmu.bin test 5.
726 # PRTBL must be set to 0x12000, PID to 1
727 mem = {
728 0x0: 0x000000, # to get mtspr prtbl working
729 0x13cf8: 0x86b10000000000c0, # leaf node
730 0x13d00: 0x0000000000000000, # invalid leaf node
731 0x10008: 0x0930010000000080, # directory node
732 0x12010: 0x0a00010000000000, # page table
733 }
734
735 while not stop:
736 while True: # wait for dc_valid
737 if stop:
738 return
739 dc_valid = yield (dut.d_out.valid)
740 tlbld = yield (dut.d_out.tlbld)
741 if dc_valid:
742 break
743 yield
744 addr = yield dut.d_out.addr
745 if tlbld:
746 pte = yield dut.d_out.pte
747 print (" DCACHE PTE %x -> %x" % (pte, addr))
748 yield dut.d_in.done.eq(1)
749 yield
750 yield dut.d_in.done.eq(0)
751 continue
752
753 if addr not in mem:
754 print (" DCACHE LOOKUP FAIL %x" % (addr))
755 stop = True
756 return
757
758 yield
759 data = mem[addr]
760 yield dut.d_in.data.eq(data)
761 print (" DCACHE GET %x data %x" % (addr, data))
762 yield dut.d_in.done.eq(1)
763 yield
764 yield dut.d_in.done.eq(0)
765
766
767 def mmu_wait(dut):
768 global stop
769 while not stop: # wait for dc_valid / err
770 d_valid = yield (dut.d_out.valid)
771 if d_valid:
772 tlbld = yield (dut.d_out.tlbld)
773 addr = yield (dut.d_out.addr)
774 print ("addr %x tlbld %d" % (addr, tlbld))
775 l_done = yield (dut.l_out.done)
776 l_err = yield (dut.l_out.err)
777 l_badtree = yield (dut.l_out.badtree)
778 l_permerr = yield (dut.l_out.perm_error)
779 l_rc_err = yield (dut.l_out.rc_error)
780 l_segerr = yield (dut.l_out.segerr)
781 l_invalid = yield (dut.l_out.invalid)
782 if (l_done or l_err or l_badtree or
783 l_permerr or l_rc_err or l_segerr or l_invalid):
784 break
785 yield
786 yield dut.l_in.valid.eq(0) # data already in MMU by now
787 yield dut.l_in.mtspr.eq(0) # captured by RegStage(s)
788 yield dut.l_in.load.eq(0) # can reset everything safely
789
790
791 def mmu_sim(dut):
792 global stop
793
794 # microwatt PRTBL = 0x12000, other test is 0x1000000
795 #prtbl = 0x100000
796 #pidr = 0x0
797 prtbl = 0x12000
798 pidr = 0x1
799
800 # MMU MTSPR set prtbl
801 yield dut.l_in.mtspr.eq(1)
802 yield dut.l_in.sprn[9].eq(1) # totally fake way to set SPR=prtbl
803 yield dut.l_in.rs.eq(prtbl) # set process table
804 yield dut.l_in.valid.eq(1)
805 yield from mmu_wait(dut)
806 yield
807 yield dut.l_in.sprn.eq(0)
808 yield dut.l_in.rs.eq(0)
809 yield
810
811 prtbl = yield (dut.rin.prtbl)
812 print ("prtbl after MTSPR %x" % prtbl)
813 assert prtbl == prtbl
814
815 if True: # microwatt test set PIDR
816 # MMU MTSPR set PIDR = 1
817 yield dut.l_in.mtspr.eq(1)
818 yield dut.l_in.sprn[9].eq(0) # totally fake way to set SPR=pidr
819 yield dut.l_in.rs.eq(pidr) # set process table
820 yield dut.l_in.valid.eq(1)
821 yield from mmu_wait(dut)
822 yield
823 yield dut.l_in.sprn.eq(0)
824 yield dut.l_in.rs.eq(0)
825 yield
826
827 #yield dut.rin.prtbl.eq(0x1000000) # manually set process table
828 #yield
829
830 #addr = 0x10000 # original test
831 #addr = 0x124108 # microwatt mmu.bin test 2
832 #addr = 0x10b0d8 # microwatt mmu.bin test 4
833 # these are a misalignment test. one load results in two actual
834 # lookups, one of which has a valid page table entry, the other
835 # does not. we currently do not support misaligned in Loadstore1
836 # therefore these tests fail with an align_intr (0x600) at 0x39fffd
837 addr = 0x39fffd # microwatt mmu.bin test 5
838 addr = 0x3a0000 # microwatt mmu.bin test 5
839
840 # MMU PTE request
841 yield dut.l_in.load.eq(1)
842 yield dut.l_in.priv.eq(1)
843 yield dut.l_in.addr.eq(addr)
844 yield dut.l_in.valid.eq(1)
845 yield from mmu_wait(dut)
846
847 addr = yield dut.d_out.addr
848 pte = yield dut.d_out.pte
849 tlb_ld = yield dut.d_out.tlbld
850 l_done = yield (dut.l_out.done)
851 l_err = yield (dut.l_out.err)
852 l_badtree = yield (dut.l_out.badtree)
853 print ("translated done %d err %d badtree %d "
854 "addr %x pte %x tlb_ld %d" % \
855 (l_done, l_err, l_badtree, addr, pte, tlb_ld))
856
857 yield
858 yield dut.l_in.priv.eq(0)
859 yield dut.l_in.addr.eq(0)
860
861
862 stop = True
863
864
865 def test_mmu():
866 dut = MMU()
867 vl = rtlil.convert(dut, ports=[])#dut.ports())
868 with open("test_mmu.il", "w") as f:
869 f.write(vl)
870
871 m = Module()
872 m.submodules.mmu = dut
873
874 # nmigen Simulation
875 sim = Simulator(m)
876 sim.add_clock(1e-6)
877
878 sim.add_sync_process(wrap(mmu_sim(dut)))
879 sim.add_sync_process(wrap(dcache_get(dut)))
880 with sim.write_vcd('test_mmu.vcd'):
881 sim.run()
882
883 if __name__ == '__main__':
884 test_mmu()