add RTPDE - Radit Tree Page Directory Entry - Record and use it in MMU
[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 # config SPRs
113 self.prtbl = Signal(64)
114 self.pid = Signal(32)
115 # internal state
116 self.state = Signal(State) # resets to IDLE
117 self.done = Signal()
118 self.err = Signal()
119 self.pgtbl0 = Signal(64)
120 self.pt0_valid = Signal()
121 self.pgtbl3 = Signal(64)
122 self.pt3_valid = Signal()
123 self.shift = Signal(6)
124 self.mask_size = Signal(5)
125 self.pgbase = Signal(56)
126 self.pde = Signal(64)
127 self.invalid = Signal()
128 self.badtree = Signal()
129 self.segerror = Signal()
130 self.perm_err = Signal()
131 self.rc_error = Signal()
132
133
134 # Page Table Record - note that HR bit is treated as part of rts below
135 # (near-identical to Process Table Record - same format)
136 # v3.0C Book III Section 6.7.6.1 p1003
137 class PGTBL(RecordObject):
138 def __init__(self, name=None):
139 super().__init__(name=name)
140 self.rpds = Signal(5) # Root Page Directory Size 59:63 LSB0 0:4
141 self.rts2 = Signal(3) # Radix Tree Size part 2 56:58 LSB0 5:7
142 self.rpdb = Signal(52) # Root Page Directory Base 4:55 LSB0 8:59
143 self.s = Signal(1) # Host Secure 3 LSB0 60
144 self.rts1 = Signal(2) # Radix Tree Size part 1 1:2 LSB0 61:62
145 self.hr = Signal(1) # Host Radix 0 LSB0 63
146
147
148 class MMU(Elaboratable):
149 """Radix MMU
150
151 Supports 4-level trees as in arch 3.0B, but not the
152 two-step translation for guests under a hypervisor
153 (i.e. there is no gRA -> hRA translation).
154 """
155 def __init__(self):
156 self.l_in = LoadStore1ToMMUType("l_in")
157 self.l_out = MMUToLoadStore1Type("l_out")
158 self.d_out = MMUToDCacheType("d_out")
159 self.d_in = DCacheToMMUType("d_in")
160 self.i_out = MMUToICacheType("i_out")
161
162 def radix_tree_idle(self, m, l_in, r, v):
163 comb = m.d.comb
164 sync = m.d.sync
165
166 pt_valid = Signal()
167 pgtbl = PGTBL("pgtbl")
168 rts = Signal(6)
169 mbits = Signal(6)
170
171 with m.If(~l_in.addr[63]):
172 comb += pgtbl.eq(r.pgtbl0)
173 comb += pt_valid.eq(r.pt0_valid)
174 with m.Else():
175 comb += pgtbl.eq(r.pgtbl3)
176 comb += pt_valid.eq(r.pt3_valid)
177
178 # rts == radix tree size, number of address bits
179 # being translated. takes bits 5:7 and 61:63
180 comb += rts.eq(Cat(pgtbl.rts2, pgtbl.rts1, pgtbl.hr))
181
182 # mbits == number of address bits to index top
183 # level of tree. takes bits 0:4
184 comb += mbits.eq(pgtbl.rpds)
185
186 # set v.shift to rts so that we can use finalmask
187 # for the segment check.
188 # note: rpdb (52 bits long) is truncated to 48 bits
189 comb += v.shift.eq(rts)
190 comb += v.mask_size.eq(mbits[0:5])
191 comb += v.pgbase.eq(Cat(C(0, 8), pgtbl.rpdb[:48])) # bits 8:55
192
193 with m.If(l_in.valid):
194 comb += v.addr.eq(l_in.addr)
195 comb += v.iside.eq(l_in.iside)
196 comb += v.store.eq(~(l_in.load | l_in.iside))
197 comb += v.priv.eq(l_in.priv)
198
199 comb += Display("state %d l_in.valid addr %x iside %d store %d "
200 "rts %x mbits %x pt_valid %d",
201 v.state, v.addr, v.iside, v.store,
202 rts, mbits, pt_valid)
203
204 with m.If(l_in.tlbie):
205 # Invalidate all iTLB/dTLB entries for
206 # tlbie with RB[IS] != 0 or RB[AP] != 0,
207 # or for slbia
208 comb += v.inval_all.eq(l_in.slbia
209 | l_in.addr[11]
210 | l_in.addr[10]
211 | l_in.addr[7]
212 | l_in.addr[6]
213 | l_in.addr[5]
214 )
215 # The RIC field of the tlbie instruction
216 # comes across on the sprn bus as bits 2--3.
217 # RIC=2 flushes process table caches.
218 with m.If(l_in.sprn[3]):
219 comb += v.pt0_valid.eq(0)
220 comb += v.pt3_valid.eq(0)
221 comb += v.state.eq(State.DO_TLBIE)
222 with m.Else():
223 comb += v.valid.eq(1)
224 with m.If(~pt_valid):
225 # need to fetch process table entry
226 # set v.shift so we can use finalmask
227 # for generating the process table
228 # entry address
229 prtbl = PRTBL("prtbl")
230 comb += prtbl.eq(r.prtbl)
231 comb += v.shift.eq(prtbl.rpds)
232 comb += v.state.eq(State.PROC_TBL_READ)
233
234 with m.Elif(mbits == 0):
235 # Use RPDS = 0 to disable radix tree walks
236 comb += v.state.eq(State.RADIX_FINISH)
237 comb += v.invalid.eq(1)
238 if(display_invalid):
239 sync += Display("MMUBUG: Use RPDS = 0 to disable"
240 " radix tree walks")
241 with m.Else():
242 comb += v.state.eq(State.SEGMENT_CHECK)
243
244 with m.If(l_in.mtspr):
245 # Move to PID needs to invalidate L1 TLBs
246 # and cached pgtbl0 value. Move to PRTBL
247 # does that plus invalidating the cached
248 # pgtbl3 value as well.
249 with m.If(~l_in.sprn[9]):
250 comb += v.pid.eq(l_in.rs[0:32])
251 with m.Else():
252 comb += v.prtbl.eq(l_in.rs)
253 comb += v.pt3_valid.eq(0)
254
255 comb += v.pt0_valid.eq(0)
256 comb += v.inval_all.eq(1)
257 comb += v.state.eq(State.DO_TLBIE)
258
259 def proc_tbl_wait(self, m, v, r, data):
260 comb = m.d.comb
261 with m.If(r.addr[63]):
262 comb += v.pgtbl3.eq(data)
263 comb += v.pt3_valid.eq(1)
264 with m.Else():
265 comb += v.pgtbl0.eq(data)
266 comb += v.pt0_valid.eq(1)
267
268 rts = Signal(6)
269 mbits = Signal(6)
270
271 # rts == radix tree size, # address bits being translated
272 comb += rts.eq(Cat(data[5:8], data[61:63]))
273
274 # mbits == # address bits to index top level of tree
275 comb += mbits.eq(data[0:5])
276
277 # set v.shift to rts so that we can use finalmask for the segment check
278 comb += v.shift.eq(rts)
279 comb += v.mask_size.eq(mbits[0:5])
280 comb += v.pgbase.eq(Cat(C(0, 8), data[8:56]))
281
282 with m.If(mbits):
283 comb += v.state.eq(State.SEGMENT_CHECK)
284 with m.Else():
285 comb += v.state.eq(State.RADIX_FINISH)
286 comb += v.invalid.eq(1)
287 if(display_invalid): m.d.sync += Display("MMUBUG: mbits is invalid")
288
289 def radix_read_wait(self, m, v, r, d_in, data):
290 comb = m.d.comb
291 sync = m.d.sync
292
293 rpte = RTPTE(name="radix_rpte") # page-table (leaf) entry
294 rpde = RTPDE(name="radix_rpde") # page-directory (non-leaf) entry
295
296 perm_ok = Signal()
297 rc_ok = Signal()
298 mbits = Signal(6)
299 valid = rpte.valid
300 eaa = rpte.eaa
301 leaf = rpte.leaf
302 badtree = Signal()
303
304 comb += Display("RDW %016x done %d "
305 "perm %d rc %d mbits %d shf %d "
306 "valid %d leaf %d bad %d",
307 data, d_in.done, perm_ok, rc_ok,
308 mbits, r.shift, valid, leaf, badtree)
309
310 # set pde and interpret as Radix Tree Page Table Entry (leaf=1 case)
311 comb += v.pde.eq(data)
312 comb += rpte.eq(data)
313 comb += rpde.eq(data)
314
315 with m.If(valid):
316 # valid & leaf: RADIX Page-Table Entry
317 with m.If(leaf):
318 # check permissions and RC bits
319 with m.If(r.priv | ~eaa[EAA_PRIV]):
320 with m.If(~r.iside):
321 comb += perm_ok.eq(eaa[EAA_WR] |
322 (eaa[EAA_RD] & ~r.store))
323 with m.Else():
324 # no IAMR, so no KUEP support for now
325 # deny execute permission if cache inhibited
326 comb += perm_ok.eq(eaa[EAA_EXE] & ~rpte.att[1])
327 comb += rc_ok.eq(rpte.r & (rpte.c | ~r.store))
328
329 # permissions / rc ok, load TLB, otherwise report error
330 with m.If(perm_ok & rc_ok):
331 comb += v.state.eq(State.RADIX_LOAD_TLB)
332 with m.Else():
333 comb += v.state.eq(State.RADIX_FINISH)
334 comb += v.perm_err.eq(~perm_ok)
335 # permission error takes precedence over RC error
336 comb += v.rc_error.eq(perm_ok)
337
338 # valid & !leaf: RADIX Page-Directory Entry
339 with m.Else():
340 comb += mbits.eq(rpde.nls) # 5 bits NLS into 6-bit-long mbits
341 comb += badtree.eq((mbits < 5) |
342 (mbits > 16) |
343 (mbits > r.shift))
344 with m.If(badtree):
345 comb += v.state.eq(State.RADIX_FINISH)
346 comb += v.badtree.eq(1)
347 with m.Else():
348 comb += v.shift.eq(r.shift - mbits)
349 comb += v.mask_size.eq(mbits)
350 # pagebase is first 48 bits of NLB, shifted up 1 byte
351 comb += v.pgbase.eq(Cat(C(0, 8), rpde.nlb[:48]))
352 comb += v.state.eq(State.RADIX_LOOKUP)
353
354 with m.Else():
355 # non-present PTE, generate a DSI
356 comb += v.state.eq(State.RADIX_FINISH)
357 comb += v.invalid.eq(1)
358 if(display_invalid):
359 sync += Display("MMUBUG: non-present PTE, generate a DSI")
360
361 def segment_check(self, m, v, r, data, finalmask):
362 comb = m.d.comb
363
364 mbits = Signal(6)
365 nonzero = Signal()
366 comb += mbits.eq(r.mask_size)
367 comb += v.shift.eq(r.shift + (31 - 12) - mbits)
368 comb += nonzero.eq((r.addr[31:62] & ~finalmask[0:31]).bool())
369 with m.If((r.addr[63] ^ r.addr[62]) | nonzero):
370 comb += v.state.eq(State.RADIX_FINISH)
371 comb += v.segerror.eq(1)
372 with m.Elif((mbits < 5) | (mbits > 16) |
373 (mbits > (r.shift + (31-12)))):
374 comb += v.state.eq(State.RADIX_FINISH)
375 comb += v.badtree.eq(1)
376 with m.Else():
377 comb += v.state.eq(State.RADIX_LOOKUP)
378
379 def mmu_0(self, m, r, rin, l_in, l_out, d_out, addrsh, mask):
380 comb = m.d.comb
381 sync = m.d.sync
382
383 # Multiplex internal SPR values back to loadstore1,
384 # selected by l_in.sprn.
385 with m.If(l_in.sprn[9]):
386 comb += l_out.sprval.eq(r.prtbl)
387 with m.Else():
388 comb += l_out.sprval.eq(r.pid)
389
390 with m.If(rin.valid):
391 sync += Display("MMU got tlb miss for %x", rin.addr)
392
393 with m.If(l_out.done):
394 sync += Display("MMU completing op without error")
395
396 with m.If(l_out.err):
397 sync += Display("MMU completing op with err invalid="
398 "%d badtree=%d", l_out.invalid, l_out.badtree)
399
400 with m.If(rin.state == State.RADIX_LOOKUP):
401 sync += Display ("radix lookup shift=%d msize=%d",
402 rin.shift, rin.mask_size)
403
404 with m.If(r.state == State.RADIX_LOOKUP):
405 sync += Display(f"send load addr=%x addrsh=%d mask=%x",
406 d_out.addr, addrsh, mask)
407 sync += r.eq(rin)
408
409 def elaborate(self, platform):
410 m = Module()
411
412 comb = m.d.comb
413 sync = m.d.sync
414
415 addrsh = Signal(16)
416 mask = Signal(16)
417 finalmask = Signal(44)
418
419 self.rin = rin = RegStage("r_in")
420 r = RegStage("r")
421
422 # get access to prtbl and pid for debug / testing purposes ONLY
423 # (actually, not needed, because setup_regs() triggers mmu direct)
424 # self._prtbl = r.prtbl
425 # self._pid = r.pid
426
427 l_in = self.l_in
428 l_out = self.l_out
429 d_out = self.d_out
430 d_in = self.d_in
431 i_out = self.i_out
432
433 self.mmu_0(m, r, rin, l_in, l_out, d_out, addrsh, mask)
434
435 v = RegStage()
436 dcreq = Signal()
437 tlb_load = Signal()
438 itlb_load = Signal()
439 tlbie_req = Signal()
440 prtbl_rd = Signal()
441 effpid = Signal(32)
442 prtb_adr = Signal(64)
443 pgtb_adr = Signal(64)
444 pte = Signal(64)
445 tlb_data = Signal(64)
446 addr = Signal(64)
447
448 comb += v.eq(r)
449 comb += v.valid.eq(0)
450 comb += dcreq.eq(0)
451 comb += v.done.eq(0)
452 comb += v.err.eq(0)
453 comb += v.invalid.eq(0)
454 comb += v.badtree.eq(0)
455 comb += v.segerror.eq(0)
456 comb += v.perm_err.eq(0)
457 comb += v.rc_error.eq(0)
458 comb += tlb_load.eq(0)
459 comb += itlb_load.eq(0)
460 comb += tlbie_req.eq(0)
461 comb += v.inval_all.eq(0)
462 comb += prtbl_rd.eq(0)
463
464 # Radix tree data structures in memory are
465 # big-endian, so we need to byte-swap them
466 data = byte_reverse(m, "data", d_in.data, 8)
467
468 # generate mask for extracting address fields for PTE addr generation
469 m.submodules.pte_mask = pte_mask = Mask(16-5)
470 comb += pte_mask.shift.eq(r.mask_size - 5)
471 comb += mask.eq(Cat(C(0x1f, 5), pte_mask.mask))
472
473 # generate mask for extracting address bits to go in
474 # TLB entry in order to support pages > 4kB
475 m.submodules.tlb_mask = tlb_mask = Mask(44)
476 comb += tlb_mask.shift.eq(r.shift)
477 comb += finalmask.eq(tlb_mask.mask)
478
479 with m.If(r.state != State.IDLE):
480 sync += Display("MMU state %d %016x", r.state, data)
481
482 ##########
483 # Main FSM
484 ##########
485
486 with m.Switch(r.state):
487 with m.Case(State.IDLE):
488 self.radix_tree_idle(m, l_in, r, v)
489
490 with m.Case(State.DO_TLBIE):
491 comb += dcreq.eq(1)
492 comb += tlbie_req.eq(1)
493 comb += v.state.eq(State.TLB_WAIT)
494
495 with m.Case(State.TLB_WAIT):
496 with m.If(d_in.done):
497 comb += v.state.eq(State.RADIX_FINISH)
498
499 with m.Case(State.PROC_TBL_READ):
500 sync += Display(" TBL_READ %016x", prtb_adr)
501 comb += dcreq.eq(1)
502 comb += prtbl_rd.eq(1)
503 comb += v.state.eq(State.PROC_TBL_WAIT)
504
505 with m.Case(State.PROC_TBL_WAIT):
506 with m.If(d_in.done):
507 self.proc_tbl_wait(m, v, r, data)
508
509 with m.If(d_in.err):
510 comb += v.state.eq(State.RADIX_FINISH)
511 comb += v.badtree.eq(1)
512
513 with m.Case(State.SEGMENT_CHECK):
514 self.segment_check(m, v, r, data, finalmask)
515
516 with m.Case(State.RADIX_LOOKUP):
517 sync += Display(" RADIX_LOOKUP")
518 comb += dcreq.eq(1)
519 comb += v.state.eq(State.RADIX_READ_WAIT)
520
521 with m.Case(State.RADIX_READ_WAIT):
522 sync += Display(" READ_WAIT")
523 with m.If(d_in.done):
524 self.radix_read_wait(m, v, r, d_in, data)
525 with m.If(d_in.err):
526 comb += v.state.eq(State.RADIX_FINISH)
527 comb += v.badtree.eq(1)
528
529 with m.Case(State.RADIX_LOAD_TLB):
530 comb += tlb_load.eq(1)
531 with m.If(~r.iside):
532 comb += dcreq.eq(1)
533 comb += v.state.eq(State.TLB_WAIT)
534 with m.Else():
535 comb += itlb_load.eq(1)
536 comb += v.state.eq(State.IDLE)
537
538 with m.Case(State.RADIX_FINISH):
539 sync += Display(" RADIX_FINISH")
540 comb += v.state.eq(State.IDLE)
541
542 with m.If((v.state == State.RADIX_FINISH) |
543 ((v.state == State.RADIX_LOAD_TLB) & r.iside)):
544 comb += v.err.eq(v.invalid | v.badtree | v.segerror
545 | v.perm_err | v.rc_error)
546 comb += v.done.eq(~v.err)
547
548 with m.If(~r.addr[63]):
549 comb += effpid.eq(r.pid)
550
551 # calculate Process Table Address
552 pr24 = Signal(24, reset_less=True)
553 prtbla = PRTBL("prtbla")
554 comb += prtbla.eq(r.prtbl)
555 rpdb = prtbla.rpdb
556 comb += pr24.eq(masked(rpdb[4:28], effpid[8:32], finalmask))
557 comb += prtb_adr.eq(Cat(C(0, 4), effpid[0:8], pr24, rpdb[28:48]))
558
559 # calculate Page Table Address
560 pg16 = Signal(16, reset_less=True)
561 comb += pg16.eq(masked(r.pgbase[3:19], addrsh, mask))
562 comb += pgtb_adr.eq(Cat(C(0, 3), pg16, r.pgbase[19:56]))
563
564 # calculate Page Table Entry from Real Page Number (leaf=1, RTPTE)
565 rpte = RTPTE(name="rpte")
566 comb += rpte.eq(r.pde)
567 pd44 = Signal(44, reset_less=True)
568 comb += pd44.eq(masked(rpte.rpn, r.addr[12:56], finalmask))
569 comb += pte.eq(Cat(r.pde[0:12], pd44))
570
571 # update registers
572 comb += rin.eq(v)
573
574 # drive outputs
575 with m.If(tlbie_req):
576 comb += addr.eq(r.addr)
577 with m.Elif(tlb_load):
578 comb += addr.eq(Cat(C(0, 12), r.addr[12:64]))
579 comb += tlb_data.eq(pte)
580 with m.Elif(prtbl_rd):
581 comb += addr.eq(prtb_adr)
582 with m.Else():
583 comb += addr.eq(pgtb_adr)
584
585 # connect to other interfaces: LDST, D-Cache, I-Cache
586 comb += l_out.done.eq(r.done)
587 comb += l_out.err.eq(r.err)
588 comb += l_out.invalid.eq(r.invalid)
589 comb += l_out.badtree.eq(r.badtree)
590 comb += l_out.segerr.eq(r.segerror)
591 comb += l_out.perm_error.eq(r.perm_err)
592 comb += l_out.rc_error.eq(r.rc_error)
593
594 comb += d_out.valid.eq(dcreq)
595 comb += d_out.tlbie.eq(tlbie_req)
596 comb += d_out.doall.eq(r.inval_all)
597 comb += d_out.tlbld.eq(tlb_load)
598 comb += d_out.addr.eq(addr)
599 comb += d_out.pte.eq(tlb_data)
600
601 comb += i_out.tlbld.eq(itlb_load)
602 comb += i_out.tlbie.eq(tlbie_req)
603 comb += i_out.doall.eq(r.inval_all)
604 comb += i_out.addr.eq(addr)
605 comb += i_out.pte.eq(tlb_data)
606
607 return m
608
609 stop = False
610
611 def dcache_get(dut):
612 """simulator process for getting memory load requests
613 """
614
615 global stop
616
617 def b(x):
618 return int.from_bytes(x.to_bytes(8, byteorder='little'),
619 byteorder='big', signed=False)
620
621 mem = {0x0: 0x000000, # to get mtspr prtbl working
622
623 0x10000: # PARTITION_TABLE_2
624 # PATB_GR=1 PRTB=0x1000 PRTS=0xb
625 b(0x800000000100000b),
626
627 0x30000: # RADIX_ROOT_PTE
628 # V = 1 L = 0 NLB = 0x400 NLS = 9
629 b(0x8000000000040009),
630
631 0x40000: # RADIX_SECOND_LEVEL
632 # V = 1 L = 1 SW = 0 RPN = 0
633 # R = 1 C = 1 ATT = 0 EAA 0x7
634 b(0xc000000000000187),
635
636 0x1000000: # PROCESS_TABLE_3
637 # RTS1 = 0x2 RPDB = 0x300 RTS2 = 0x5 RPDS = 13
638 b(0x40000000000300ad),
639 }
640
641 while not stop:
642 while True: # wait for dc_valid
643 if stop:
644 return
645 dc_valid = yield (dut.d_out.valid)
646 if dc_valid:
647 break
648 yield
649 addr = yield dut.d_out.addr
650 if addr not in mem:
651 print (" DCACHE LOOKUP FAIL %x" % (addr))
652 stop = True
653 return
654
655 yield
656 data = mem[addr]
657 yield dut.d_in.data.eq(data)
658 print (" DCACHE GET %x data %x" % (addr, data))
659 yield dut.d_in.done.eq(1)
660 yield
661 yield dut.d_in.done.eq(0)
662
663 def mmu_wait(dut):
664 global stop
665 while not stop: # wait for dc_valid / err
666 l_done = yield (dut.l_out.done)
667 l_err = yield (dut.l_out.err)
668 l_badtree = yield (dut.l_out.badtree)
669 l_permerr = yield (dut.l_out.perm_error)
670 l_rc_err = yield (dut.l_out.rc_error)
671 l_segerr = yield (dut.l_out.segerr)
672 l_invalid = yield (dut.l_out.invalid)
673 if (l_done or l_err or l_badtree or
674 l_permerr or l_rc_err or l_segerr or l_invalid):
675 break
676 yield
677 yield dut.l_in.valid.eq(0) # data already in MMU by now
678 yield dut.l_in.mtspr.eq(0) # captured by RegStage(s)
679 yield dut.l_in.load.eq(0) # can reset everything safely
680
681 def mmu_sim(dut):
682 global stop
683
684 # MMU MTSPR set prtbl
685 yield dut.l_in.mtspr.eq(1)
686 yield dut.l_in.sprn[9].eq(1) # totally fake way to set SPR=prtbl
687 yield dut.l_in.rs.eq(0x1000000) # set process table
688 yield dut.l_in.valid.eq(1)
689 yield from mmu_wait(dut)
690 yield
691 yield dut.l_in.sprn.eq(0)
692 yield dut.l_in.rs.eq(0)
693 yield
694
695 prtbl = yield (dut.rin.prtbl)
696 print ("prtbl after MTSPR %x" % prtbl)
697 assert prtbl == 0x1000000
698
699 #yield dut.rin.prtbl.eq(0x1000000) # manually set process table
700 #yield
701
702
703 # MMU PTE request
704 yield dut.l_in.load.eq(1)
705 yield dut.l_in.priv.eq(1)
706 yield dut.l_in.addr.eq(0x10000)
707 yield dut.l_in.valid.eq(1)
708 yield from mmu_wait(dut)
709
710 addr = yield dut.d_out.addr
711 pte = yield dut.d_out.pte
712 l_done = yield (dut.l_out.done)
713 l_err = yield (dut.l_out.err)
714 l_badtree = yield (dut.l_out.badtree)
715 print ("translated done %d err %d badtree %d addr %x pte %x" % \
716 (l_done, l_err, l_badtree, addr, pte))
717 yield
718 yield dut.l_in.priv.eq(0)
719 yield dut.l_in.addr.eq(0)
720
721
722 stop = True
723
724
725 def test_mmu():
726 dut = MMU()
727 vl = rtlil.convert(dut, ports=[])#dut.ports())
728 with open("test_mmu.il", "w") as f:
729 f.write(vl)
730
731 m = Module()
732 m.submodules.mmu = dut
733
734 # nmigen Simulation
735 sim = Simulator(m)
736 sim.add_clock(1e-6)
737
738 sim.add_sync_process(wrap(mmu_sim(dut)))
739 sim.add_sync_process(wrap(dcache_get(dut)))
740 with sim.write_vcd('test_mmu.vcd'):
741 sim.run()
742
743 if __name__ == '__main__':
744 test_mmu()