more code-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):
330 comb += perm_ok.eq(eaa[EAA_WR] |
331 (eaa[EAA_RD] & ~r.store))
332 with m.Else():
333 # no IAMR, so no KUEP support for now
334 # deny execute permission if cache inhibited
335 comb += perm_ok.eq(eaa[EAA_EXE] & ~rpte.att[1])
336 comb += rc_ok.eq(rpte.r & (rpte.c | ~r.store))
337
338 # permissions / rc ok, load TLB, otherwise report error
339 with m.If(perm_ok & rc_ok):
340 comb += v.state.eq(State.RADIX_LOAD_TLB)
341 with m.Else():
342 comb += v.state.eq(State.RADIX_FINISH)
343 comb += v.perm_err.eq(~perm_ok)
344 # permission error takes precedence over RC error
345 comb += v.rc_error.eq(perm_ok)
346
347 # valid & !leaf: RADIX Page-Directory Entry
348 with m.Else():
349 comb += mbits.eq(rpde.nls) # 5 bits NLS into 6-bit-long mbits
350 comb += badtree.eq((mbits < 5) |
351 (mbits > 16) |
352 (mbits > r.shift))
353 with m.If(badtree):
354 comb += v.state.eq(State.RADIX_FINISH)
355 comb += v.badtree.eq(1)
356 with m.Else():
357 comb += v.shift.eq(r.shift - mbits)
358 comb += v.mask_size.eq(mbits)
359 # pagebase is first 48 bits of NLB, shifted up 1 byte
360 comb += v.pgbase.eq(Cat(C(0, 8), rpde.nlb[:48]))
361 comb += v.state.eq(State.RADIX_LOOKUP)
362
363 with m.Else():
364 # non-present PTE, generate a DSI
365 comb += v.state.eq(State.RADIX_FINISH)
366 comb += v.invalid.eq(1)
367 if(display_invalid):
368 sync += Display("MMUBUG: non-present PTE, generate a DSI")
369
370 def segment_check(self, m, v, r, data, finalmask):
371 comb = m.d.comb
372
373 mbits = Signal(6)
374 nonzero = Signal()
375 comb += mbits.eq(r.mask_size)
376 comb += v.shift.eq(r.shift + (31 - 12) - mbits)
377 comb += nonzero.eq((r.addr[31:62] & ~finalmask[0:31]).bool())
378 with m.If((r.addr[63] ^ r.addr[62]) # pt3 == 0b11 and pt1 == 0b00
379 | nonzero):
380 comb += v.state.eq(State.RADIX_FINISH)
381 comb += v.segerror.eq(1)
382 with m.Elif((mbits < 5) | (mbits > 16) |
383 (mbits > (r.shift + (31-12)))):
384 comb += v.state.eq(State.RADIX_FINISH)
385 comb += v.badtree.eq(1)
386 with m.Else():
387 comb += v.state.eq(State.RADIX_LOOKUP)
388
389 def mmu_0(self, m, r, rin, l_in, l_out, d_out, addrsh, mask):
390 comb = m.d.comb
391 sync = m.d.sync
392
393 # Multiplex internal SPR values back to loadstore1,
394 # selected by l_in.sprn.
395 with m.If(l_in.sprn[9]):
396 comb += l_out.sprval.eq(r.prtbl)
397 with m.Else():
398 comb += l_out.sprval.eq(r.pid)
399
400 with m.If(rin.valid):
401 sync += Display("MMU got tlb miss for %x", rin.addr)
402
403 with m.If(l_out.done):
404 sync += Display("MMU completing op without error")
405
406 with m.If(l_out.err):
407 sync += Display("MMU completing op with err invalid="
408 "%d badtree=%d", l_out.invalid, l_out.badtree)
409
410 with m.If(rin.state == State.RADIX_LOOKUP):
411 sync += Display ("radix lookup shift=%d msize=%d",
412 rin.shift, rin.mask_size)
413
414 with m.If(r.state == State.RADIX_LOOKUP):
415 sync += Display(f"send load addr=%x addrsh=%d mask=%x",
416 d_out.addr, addrsh, mask)
417 sync += r.eq(rin)
418
419 def elaborate(self, platform):
420 m = Module()
421
422 comb = m.d.comb
423 sync = m.d.sync
424
425 addrsh = Signal(16)
426 mask = Signal(16)
427 finalmask = Signal(44)
428
429 self.rin = rin = RegStage("r_in")
430 r = RegStage("r")
431
432 # get access to prtbl and pid for debug / testing purposes ONLY
433 # (actually, not needed, because setup_regs() triggers mmu direct)
434 # self._prtbl = r.prtbl
435 # self._pid = r.pid
436
437 l_in = self.l_in
438 l_out = self.l_out
439 d_out = self.d_out
440 d_in = self.d_in
441 i_out = self.i_out
442
443 self.mmu_0(m, r, rin, l_in, l_out, d_out, addrsh, mask)
444
445 v = RegStage()
446 dcreq = Signal()
447 tlb_load = Signal()
448 itlb_load = Signal()
449 tlbie_req = Signal()
450 prtbl_rd = Signal()
451 effpid = Signal(32)
452 prtb_adr = Signal(64)
453 pgtb_adr = Signal(64)
454 pte = Signal(64)
455 tlb_data = Signal(64)
456 addr = Signal(64)
457
458 comb += v.eq(r)
459 comb += v.valid.eq(0)
460 comb += dcreq.eq(0)
461 comb += v.done.eq(0)
462 comb += v.err.eq(0)
463 comb += v.invalid.eq(0)
464 comb += v.badtree.eq(0)
465 comb += v.segerror.eq(0)
466 comb += v.perm_err.eq(0)
467 comb += v.rc_error.eq(0)
468 comb += tlb_load.eq(0)
469 comb += itlb_load.eq(0)
470 comb += tlbie_req.eq(0)
471 comb += v.inval_all.eq(0)
472 comb += prtbl_rd.eq(0)
473
474 # Radix tree data structures in memory are
475 # big-endian, so we need to byte-swap them
476 data = byte_reverse(m, "data", d_in.data, 8)
477
478 # generate mask for extracting address fields for PTE addr generation
479 m.submodules.pte_mask = pte_mask = Mask(16-5)
480 comb += pte_mask.shift.eq(r.mask_size - 5)
481 comb += mask.eq(Cat(C(0x1f, 5), pte_mask.mask))
482
483 # generate mask for extracting address bits to go in
484 # TLB entry in order to support pages > 4kB
485 m.submodules.tlb_mask = tlb_mask = Mask(44)
486 comb += tlb_mask.shift.eq(r.shift)
487 comb += finalmask.eq(tlb_mask.mask)
488
489 with m.If(r.state != State.IDLE):
490 sync += Display("MMU state %d %016x", r.state, data)
491
492 ##########
493 # Main FSM
494 ##########
495
496 with m.Switch(r.state):
497 with m.Case(State.IDLE):
498 self.radix_tree_idle(m, l_in, r, v)
499
500 with m.Case(State.DO_TLBIE):
501 comb += dcreq.eq(1)
502 comb += tlbie_req.eq(1)
503 comb += v.state.eq(State.TLB_WAIT)
504
505 with m.Case(State.TLB_WAIT):
506 with m.If(d_in.done):
507 comb += v.state.eq(State.RADIX_FINISH)
508
509 with m.Case(State.PROC_TBL_READ):
510 sync += Display(" TBL_READ %016x", prtb_adr)
511 comb += dcreq.eq(1)
512 comb += prtbl_rd.eq(1)
513 comb += v.state.eq(State.PROC_TBL_WAIT)
514
515 with m.Case(State.PROC_TBL_WAIT):
516 with m.If(d_in.done):
517 self.proc_tbl_wait(m, v, r, data)
518
519 with m.If(d_in.err):
520 comb += v.state.eq(State.RADIX_FINISH)
521 comb += v.badtree.eq(1)
522
523 with m.Case(State.SEGMENT_CHECK):
524 self.segment_check(m, v, r, data, finalmask)
525
526 with m.Case(State.RADIX_LOOKUP):
527 sync += Display(" RADIX_LOOKUP")
528 comb += dcreq.eq(1)
529 comb += v.state.eq(State.RADIX_READ_WAIT)
530
531 with m.Case(State.RADIX_READ_WAIT):
532 sync += Display(" READ_WAIT")
533 with m.If(d_in.done):
534 self.radix_read_wait(m, v, r, d_in, data)
535 with m.If(d_in.err):
536 comb += v.state.eq(State.RADIX_FINISH)
537 comb += v.badtree.eq(1)
538
539 with m.Case(State.RADIX_LOAD_TLB):
540 comb += tlb_load.eq(1)
541 with m.If(~r.iside):
542 comb += dcreq.eq(1)
543 comb += v.state.eq(State.TLB_WAIT)
544 with m.Else():
545 comb += itlb_load.eq(1)
546 comb += v.state.eq(State.IDLE)
547
548 with m.Case(State.RADIX_FINISH):
549 sync += Display(" RADIX_FINISH")
550 comb += v.state.eq(State.IDLE)
551
552 # check and report either error or done.
553 with m.If((v.state == State.RADIX_FINISH) |
554 ((v.state == State.RADIX_LOAD_TLB) & r.iside)):
555 comb += v.err.eq(v.invalid | v.badtree | v.segerror
556 | v.perm_err | v.rc_error)
557 comb += v.done.eq(~v.err)
558
559 # PID is only valid if MSB of address is zero, top 2 bits are Quadrant
560 with m.If(~r.addr[63]): # quadrant 0 (pt0)
561 comb += effpid.eq(r.pid)
562
563 # calculate Process Table Address
564 pr24 = Signal(24, reset_less=True)
565 prtbla = PRTBL("prtbla")
566 comb += prtbla.eq(r.prtbl)
567 rpdb = prtbla.rpdb
568 comb += pr24.eq(masked(rpdb[4:28], effpid[8:32], finalmask))
569 comb += prtb_adr.eq(Cat(C(0, 4), effpid[0:8], pr24, rpdb[28:48]))
570
571 # calculate Page Table Address
572 pg16 = Signal(16, reset_less=True)
573 comb += pg16.eq(masked(r.pgbase[3:19], addrsh, mask))
574 comb += pgtb_adr.eq(Cat(C(0, 3), pg16, r.pgbase[19:56]))
575
576 # calculate Page Table Entry from Real Page Number (leaf=1, RTPTE)
577 rpte = RTPTE(name="rpte")
578 comb += rpte.eq(r.pde)
579 pd44 = Signal(44, reset_less=True)
580 comb += pd44.eq(masked(rpte.rpn, r.addr[12:56], finalmask))
581 comb += pte.eq(Cat(r.pde[0:12], pd44))
582
583 # update registers
584 comb += rin.eq(v)
585
586 # drive outputs
587 with m.If(tlbie_req):
588 comb += addr.eq(r.addr)
589 with m.Elif(tlb_load):
590 comb += addr.eq(Cat(C(0, 12), r.addr[12:64]))
591 comb += tlb_data.eq(pte)
592 with m.Elif(prtbl_rd):
593 comb += addr.eq(prtb_adr)
594 with m.Else():
595 comb += addr.eq(pgtb_adr)
596
597 # connect to other interfaces: LDST, D-Cache, I-Cache
598 comb += l_out.done.eq(r.done)
599 comb += l_out.err.eq(r.err)
600 comb += l_out.invalid.eq(r.invalid)
601 comb += l_out.badtree.eq(r.badtree)
602 comb += l_out.segerr.eq(r.segerror)
603 comb += l_out.perm_error.eq(r.perm_err)
604 comb += l_out.rc_error.eq(r.rc_error)
605
606 comb += d_out.valid.eq(dcreq)
607 comb += d_out.tlbie.eq(tlbie_req)
608 comb += d_out.doall.eq(r.inval_all)
609 comb += d_out.tlbld.eq(tlb_load)
610 comb += d_out.addr.eq(addr)
611 comb += d_out.pte.eq(tlb_data)
612
613 comb += i_out.tlbld.eq(itlb_load)
614 comb += i_out.tlbie.eq(tlbie_req)
615 comb += i_out.doall.eq(r.inval_all)
616 comb += i_out.addr.eq(addr)
617 comb += i_out.pte.eq(tlb_data)
618
619 return m
620
621 stop = False
622
623 def dcache_get(dut):
624 """simulator process for getting memory load requests
625 """
626
627 global stop
628
629 def b(x):
630 return int.from_bytes(x.to_bytes(8, byteorder='little'),
631 byteorder='big', signed=False)
632
633 mem = {0x0: 0x000000, # to get mtspr prtbl working
634
635 0x10000: # PARTITION_TABLE_2
636 # PATB_GR=1 PRTB=0x1000 PRTS=0xb
637 b(0x800000000100000b),
638
639 0x30000: # RADIX_ROOT_PTE
640 # V = 1 L = 0 NLB = 0x400 NLS = 9
641 b(0x8000000000040009),
642
643 0x40000: # RADIX_SECOND_LEVEL
644 # V = 1 L = 1 SW = 0 RPN = 0
645 # R = 1 C = 1 ATT = 0 EAA 0x7
646 b(0xc000000000000187),
647
648 0x1000000: # PROCESS_TABLE_3
649 # RTS1 = 0x2 RPDB = 0x300 RTS2 = 0x5 RPDS = 13
650 b(0x40000000000300ad),
651 }
652
653 while not stop:
654 while True: # wait for dc_valid
655 if stop:
656 return
657 dc_valid = yield (dut.d_out.valid)
658 if dc_valid:
659 break
660 yield
661 addr = yield dut.d_out.addr
662 if addr not in mem:
663 print (" DCACHE LOOKUP FAIL %x" % (addr))
664 stop = True
665 return
666
667 yield
668 data = mem[addr]
669 yield dut.d_in.data.eq(data)
670 print (" DCACHE GET %x data %x" % (addr, data))
671 yield dut.d_in.done.eq(1)
672 yield
673 yield dut.d_in.done.eq(0)
674
675 def mmu_wait(dut):
676 global stop
677 while not stop: # wait for dc_valid / err
678 l_done = yield (dut.l_out.done)
679 l_err = yield (dut.l_out.err)
680 l_badtree = yield (dut.l_out.badtree)
681 l_permerr = yield (dut.l_out.perm_error)
682 l_rc_err = yield (dut.l_out.rc_error)
683 l_segerr = yield (dut.l_out.segerr)
684 l_invalid = yield (dut.l_out.invalid)
685 if (l_done or l_err or l_badtree or
686 l_permerr or l_rc_err or l_segerr or l_invalid):
687 break
688 yield
689 yield dut.l_in.valid.eq(0) # data already in MMU by now
690 yield dut.l_in.mtspr.eq(0) # captured by RegStage(s)
691 yield dut.l_in.load.eq(0) # can reset everything safely
692
693 def mmu_sim(dut):
694 global stop
695
696 # MMU MTSPR set prtbl
697 yield dut.l_in.mtspr.eq(1)
698 yield dut.l_in.sprn[9].eq(1) # totally fake way to set SPR=prtbl
699 yield dut.l_in.rs.eq(0x1000000) # set process table
700 yield dut.l_in.valid.eq(1)
701 yield from mmu_wait(dut)
702 yield
703 yield dut.l_in.sprn.eq(0)
704 yield dut.l_in.rs.eq(0)
705 yield
706
707 prtbl = yield (dut.rin.prtbl)
708 print ("prtbl after MTSPR %x" % prtbl)
709 assert prtbl == 0x1000000
710
711 #yield dut.rin.prtbl.eq(0x1000000) # manually set process table
712 #yield
713
714
715 # MMU PTE request
716 yield dut.l_in.load.eq(1)
717 yield dut.l_in.priv.eq(1)
718 yield dut.l_in.addr.eq(0x10000)
719 yield dut.l_in.valid.eq(1)
720 yield from mmu_wait(dut)
721
722 addr = yield dut.d_out.addr
723 pte = yield dut.d_out.pte
724 l_done = yield (dut.l_out.done)
725 l_err = yield (dut.l_out.err)
726 l_badtree = yield (dut.l_out.badtree)
727 print ("translated done %d err %d badtree %d addr %x pte %x" % \
728 (l_done, l_err, l_badtree, addr, pte))
729 yield
730 yield dut.l_in.priv.eq(0)
731 yield dut.l_in.addr.eq(0)
732
733
734 stop = True
735
736
737 def test_mmu():
738 dut = MMU()
739 vl = rtlil.convert(dut, ports=[])#dut.ports())
740 with open("test_mmu.il", "w") as f:
741 f.write(vl)
742
743 m = Module()
744 m.submodules.mmu = dut
745
746 # nmigen Simulation
747 sim = Simulator(m)
748 sim.add_clock(1e-6)
749
750 sim.add_sync_process(wrap(mmu_sim(dut)))
751 sim.add_sync_process(wrap(dcache_get(dut)))
752 with sim.write_vcd('test_mmu.vcd'):
753 sim.run()
754
755 if __name__ == '__main__':
756 test_mmu()