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