1c23e81a6cc90aaabadf9c2e36f05ebd184c82d0
[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.pgtbl3)
173 comb += pt_valid.eq(r.pt3_valid)
174 with m.Else():
175 comb += pgtbl.eq(r.pgtbl0)
176 comb += pt_valid.eq(r.pt0_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 # check and report either error or done.
543 with m.If((v.state == State.RADIX_FINISH) |
544 ((v.state == State.RADIX_LOAD_TLB) & r.iside)):
545 comb += v.err.eq(v.invalid | v.badtree | v.segerror
546 | v.perm_err | v.rc_error)
547 comb += v.done.eq(~v.err)
548
549 # PID is only valid if MSB of address is zero
550 with m.If(~r.addr[63]):
551 comb += effpid.eq(r.pid)
552
553 # calculate Process Table Address
554 pr24 = Signal(24, reset_less=True)
555 prtbla = PRTBL("prtbla")
556 comb += prtbla.eq(r.prtbl)
557 rpdb = prtbla.rpdb
558 comb += pr24.eq(masked(rpdb[4:28], effpid[8:32], finalmask))
559 comb += prtb_adr.eq(Cat(C(0, 4), effpid[0:8], pr24, rpdb[28:48]))
560
561 # calculate Page Table Address
562 pg16 = Signal(16, reset_less=True)
563 comb += pg16.eq(masked(r.pgbase[3:19], addrsh, mask))
564 comb += pgtb_adr.eq(Cat(C(0, 3), pg16, r.pgbase[19:56]))
565
566 # calculate Page Table Entry from Real Page Number (leaf=1, RTPTE)
567 rpte = RTPTE(name="rpte")
568 comb += rpte.eq(r.pde)
569 pd44 = Signal(44, reset_less=True)
570 comb += pd44.eq(masked(rpte.rpn, r.addr[12:56], finalmask))
571 comb += pte.eq(Cat(r.pde[0:12], pd44))
572
573 # update registers
574 comb += rin.eq(v)
575
576 # drive outputs
577 with m.If(tlbie_req):
578 comb += addr.eq(r.addr)
579 with m.Elif(tlb_load):
580 comb += addr.eq(Cat(C(0, 12), r.addr[12:64]))
581 comb += tlb_data.eq(pte)
582 with m.Elif(prtbl_rd):
583 comb += addr.eq(prtb_adr)
584 with m.Else():
585 comb += addr.eq(pgtb_adr)
586
587 # connect to other interfaces: LDST, D-Cache, I-Cache
588 comb += l_out.done.eq(r.done)
589 comb += l_out.err.eq(r.err)
590 comb += l_out.invalid.eq(r.invalid)
591 comb += l_out.badtree.eq(r.badtree)
592 comb += l_out.segerr.eq(r.segerror)
593 comb += l_out.perm_error.eq(r.perm_err)
594 comb += l_out.rc_error.eq(r.rc_error)
595
596 comb += d_out.valid.eq(dcreq)
597 comb += d_out.tlbie.eq(tlbie_req)
598 comb += d_out.doall.eq(r.inval_all)
599 comb += d_out.tlbld.eq(tlb_load)
600 comb += d_out.addr.eq(addr)
601 comb += d_out.pte.eq(tlb_data)
602
603 comb += i_out.tlbld.eq(itlb_load)
604 comb += i_out.tlbie.eq(tlbie_req)
605 comb += i_out.doall.eq(r.inval_all)
606 comb += i_out.addr.eq(addr)
607 comb += i_out.pte.eq(tlb_data)
608
609 return m
610
611 stop = False
612
613 def dcache_get(dut):
614 """simulator process for getting memory load requests
615 """
616
617 global stop
618
619 def b(x):
620 return int.from_bytes(x.to_bytes(8, byteorder='little'),
621 byteorder='big', signed=False)
622
623 mem = {0x0: 0x000000, # to get mtspr prtbl working
624
625 0x10000: # PARTITION_TABLE_2
626 # PATB_GR=1 PRTB=0x1000 PRTS=0xb
627 b(0x800000000100000b),
628
629 0x30000: # RADIX_ROOT_PTE
630 # V = 1 L = 0 NLB = 0x400 NLS = 9
631 b(0x8000000000040009),
632
633 0x40000: # RADIX_SECOND_LEVEL
634 # V = 1 L = 1 SW = 0 RPN = 0
635 # R = 1 C = 1 ATT = 0 EAA 0x7
636 b(0xc000000000000187),
637
638 0x1000000: # PROCESS_TABLE_3
639 # RTS1 = 0x2 RPDB = 0x300 RTS2 = 0x5 RPDS = 13
640 b(0x40000000000300ad),
641 }
642
643 while not stop:
644 while True: # wait for dc_valid
645 if stop:
646 return
647 dc_valid = yield (dut.d_out.valid)
648 if dc_valid:
649 break
650 yield
651 addr = yield dut.d_out.addr
652 if addr not in mem:
653 print (" DCACHE LOOKUP FAIL %x" % (addr))
654 stop = True
655 return
656
657 yield
658 data = mem[addr]
659 yield dut.d_in.data.eq(data)
660 print (" DCACHE GET %x data %x" % (addr, data))
661 yield dut.d_in.done.eq(1)
662 yield
663 yield dut.d_in.done.eq(0)
664
665 def mmu_wait(dut):
666 global stop
667 while not stop: # wait for dc_valid / err
668 l_done = yield (dut.l_out.done)
669 l_err = yield (dut.l_out.err)
670 l_badtree = yield (dut.l_out.badtree)
671 l_permerr = yield (dut.l_out.perm_error)
672 l_rc_err = yield (dut.l_out.rc_error)
673 l_segerr = yield (dut.l_out.segerr)
674 l_invalid = yield (dut.l_out.invalid)
675 if (l_done or l_err or l_badtree or
676 l_permerr or l_rc_err or l_segerr or l_invalid):
677 break
678 yield
679 yield dut.l_in.valid.eq(0) # data already in MMU by now
680 yield dut.l_in.mtspr.eq(0) # captured by RegStage(s)
681 yield dut.l_in.load.eq(0) # can reset everything safely
682
683 def mmu_sim(dut):
684 global stop
685
686 # MMU MTSPR set prtbl
687 yield dut.l_in.mtspr.eq(1)
688 yield dut.l_in.sprn[9].eq(1) # totally fake way to set SPR=prtbl
689 yield dut.l_in.rs.eq(0x1000000) # set process table
690 yield dut.l_in.valid.eq(1)
691 yield from mmu_wait(dut)
692 yield
693 yield dut.l_in.sprn.eq(0)
694 yield dut.l_in.rs.eq(0)
695 yield
696
697 prtbl = yield (dut.rin.prtbl)
698 print ("prtbl after MTSPR %x" % prtbl)
699 assert prtbl == 0x1000000
700
701 #yield dut.rin.prtbl.eq(0x1000000) # manually set process table
702 #yield
703
704
705 # MMU PTE request
706 yield dut.l_in.load.eq(1)
707 yield dut.l_in.priv.eq(1)
708 yield dut.l_in.addr.eq(0x10000)
709 yield dut.l_in.valid.eq(1)
710 yield from mmu_wait(dut)
711
712 addr = yield dut.d_out.addr
713 pte = yield dut.d_out.pte
714 l_done = yield (dut.l_out.done)
715 l_err = yield (dut.l_out.err)
716 l_badtree = yield (dut.l_out.badtree)
717 print ("translated done %d err %d badtree %d addr %x pte %x" % \
718 (l_done, l_err, l_badtree, addr, pte))
719 yield
720 yield dut.l_in.priv.eq(0)
721 yield dut.l_in.addr.eq(0)
722
723
724 stop = True
725
726
727 def test_mmu():
728 dut = MMU()
729 vl = rtlil.convert(dut, ports=[])#dut.ports())
730 with open("test_mmu.il", "w") as f:
731 f.write(vl)
732
733 m = Module()
734 m.submodules.mmu = dut
735
736 # nmigen Simulation
737 sim = Simulator(m)
738 sim.add_clock(1e-6)
739
740 sim.add_sync_process(wrap(mmu_sim(dut)))
741 sim.add_sync_process(wrap(dcache_get(dut)))
742 with sim.write_vcd('test_mmu.vcd'):
743 sim.run()
744
745 if __name__ == '__main__':
746 test_mmu()