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