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