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