4850269028ccaa1e68a3c2280f70b0b0d7d16bae
3 # License for original copyright mmu.vhdl by microwatt authors: CC4
4 # License for copyrighted modifications made in mmu.py: LGPLv3+
6 # This derivative work although includes CC4 licensed material is
7 # covered by the LGPLv3+
11 based on Anton Blanchard microwatt mmu.vhdl
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
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
27 from nmutil
.util
import wrap
29 from soc
.experiment
.mem_types
import (LoadStore1ToMMUType
,
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
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
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
73 display_invalid
= True
77 IDLE
= 0 # zero is default on reset for r.state
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
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()
110 self
.addr
= Signal(64)
111 self
.inval_all
= Signal()
114 self
.prtbl
= Signal(64)
115 self
.pid
= Signal(32)
118 self
.state
= Signal(State
) # resets to IDLE
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()
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()
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
157 class MMU(Elaboratable
):
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).
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")
171 def radix_tree_idle(self
, m
, l_in
, r
, v
):
172 """radix_tree_idle - the main decision-point. valid actions include:
173 * LDST incoming TLBIE request (invalidate TLB entry)
174 * LDST incoming RADIX walk request
175 * set either PRTBL or PID SPRs (which then fires a TLB invalidate)
181 pgtbl
= PGTBL("pgtbl")
183 mbits
= Signal(6, name
="mbits_idle")
185 with m
.If(l_in
.addr
[63]): # quadrant 3
186 comb
+= pgtbl
.eq(r
.pgtbl3
)
187 comb
+= pt_valid
.eq(r
.pt3_valid
)
189 comb
+= pgtbl
.eq(r
.pgtbl0
)
190 comb
+= pt_valid
.eq(r
.pt0_valid
)
192 # rts == radix tree size, number of address bits
193 # being translated. takes bits 5:7 and 61:62
194 comb
+= rts
.eq(Cat(pgtbl
.rts2
, pgtbl
.rts1
, C(0)))
196 # mbits == number of address bits to index top
197 # level of tree. takes bits 0:4
198 comb
+= mbits
.eq(pgtbl
.rpds
)
200 # set v.shift to rts so that we can use finalmask
201 # for the segment check.
202 # note: rpdb (52 bits long) is truncated to 48 bits
203 comb
+= v
.shift
.eq(rts
)
204 comb
+= v
.mask_size
.eq(mbits
[0:5])
206 # create the page base from root page directory base (48 bits with 8 0s)
207 comb
+= v
.pgbase
.eq(Cat(C(0, 8), pgtbl
.rpdb
[:48])) # bits 8:55
209 # request either TLB invalidate
210 # or start a RADIX walk
212 with m
.If(l_in
.valid
):
213 comb
+= v
.addr
.eq(l_in
.addr
)
214 comb
+= v
.iside
.eq(l_in
.iside
)
215 comb
+= v
.store
.eq(~
(l_in
.load | l_in
.iside
))
216 comb
+= v
.priv
.eq(l_in
.priv
)
218 sync
+= Display("state %d l_in.valid addr %x iside %d store %d "
219 "rpdb %x rts %d mbits %d pt_valid %d",
220 v
.state
, v
.addr
, v
.iside
, v
.store
,
221 pgtbl
.rpdb
, rts
, mbits
, pt_valid
)
223 with m
.If(l_in
.tlbie
):
224 # Invalidate all iTLB/dTLB entries for
225 # tlbie with RB[IS] != 0 or RB[AP] != 0,
227 comb
+= v
.inval_all
.eq(l_in
.slbia
234 # The RIC field of the tlbie instruction
235 # comes across on the sprn bus as bits 2--3.
236 # RIC=2 flushes process table caches.
237 with m
.If(l_in
.sprn
[3]):
238 comb
+= v
.pt0_valid
.eq(0)
239 comb
+= v
.pt3_valid
.eq(0)
240 comb
+= v
.state
.eq(State
.DO_TLBIE
)
242 comb
+= v
.valid
.eq(1)
243 with m
.If(~pt_valid
):
244 # need to fetch process table entry
245 # set v.shift so we can use finalmask
246 # for generating the process table
248 prtbl
= PRTBL("prtbl")
249 comb
+= prtbl
.eq(r
.prtbl
)
250 comb
+= v
.shift
.eq(prtbl
.rpds
)
251 comb
+= v
.state
.eq(State
.PROC_TBL_READ
)
253 with m
.Elif(mbits
== 0):
254 # Use RPDS = 0 to disable radix tree walks
255 comb
+= v
.state
.eq(State
.RADIX_FINISH
)
256 comb
+= v
.invalid
.eq(1)
258 sync
+= Display("MMUBUG: Use RPDS = 0 to disable"
261 comb
+= v
.state
.eq(State
.SEGMENT_CHECK
)
263 # set either PID or PRTBL SPRs
264 # (then invalidate TLBs)
266 with m
.If(l_in
.mtspr
):
267 # Move to PID needs to invalidate L1 TLBs
268 # and cached pgtbl0 value.
269 # Move to PRTBL does that plus invalidating the cached
270 # pgtbl3 value as well.
271 with m
.If(~l_in
.sprn
[9]):
272 comb
+= v
.pid
.eq(l_in
.rs
[0:32])
274 comb
+= v
.prtbl
.eq(l_in
.rs
)
275 comb
+= v
.pt3_valid
.eq(0)
277 comb
+= v
.pt0_valid
.eq(0)
278 comb
+= v
.inval_all
.eq(1)
279 comb
+= v
.state
.eq(State
.DO_TLBIE
)
281 def proc_tbl_wait(self
, m
, v
, r
, data
):
285 mbits
= Signal(6, name
="mbits_tbl_wait")
286 prtbl
= PRTBL("prtblw")
287 comb
+= prtbl
.eq(data
)
289 with m
.If(r
.addr
[63]): # top bit of quadrant selects pt3
290 comb
+= v
.pgtbl3
.eq(prtbl
)
291 comb
+= v
.pt3_valid
.eq(1)
293 comb
+= v
.pgtbl0
.eq(prtbl
)
294 comb
+= v
.pt0_valid
.eq(1)
296 # rts == radix tree size, # address bits being translated
297 comb
+= rts
.eq(Cat(prtbl
.rts2
, prtbl
.rts1
, C(0)))
299 # mbits == # address bits to index top level of tree
300 comb
+= mbits
.eq(prtbl
.rpds
[0:5])
302 # set v.shift to rts so that we can use finalmask for the segment check
303 comb
+= v
.shift
.eq(rts
)
304 comb
+= v
.mask_size
.eq(mbits
[0:5])
306 # create the page base from root page directory base (48 bits with 8 0s)
307 comb
+= v
.pgbase
.eq(Cat(C(0, 8), prtbl
.rpdb
[:48])) # bits 8:55
310 comb
+= v
.state
.eq(State
.SEGMENT_CHECK
)
311 sync
+= Display("PROC TBL %d data %x rts1 %x rts2 %x rts %d "
312 "rpdb %x mbits %d pgbase %x "
313 " pt0_valid %d, pt3_valid %d",
314 v
.state
, data
, prtbl
.rts1
, prtbl
.rts2
, rts
,
315 prtbl
.rpdb
, mbits
, v
.pgbase
,
316 v
.pt0_valid
, v
.pt3_valid
)
318 comb
+= v
.state
.eq(State
.RADIX_FINISH
)
319 comb
+= v
.invalid
.eq(1)
320 if (display_invalid
): m
.d
.sync
+= Display("MMU: mbits is invalid")
322 def radix_read_wait(self
, m
, v
, r
, d_in
, data
):
326 rpte
= RTPTE(name
="radix_rpte") # page-table (leaf) entry
327 rpde
= RTPDE(name
="radix_rpde") # page-directory (non-leaf) entry
331 mbits
= Signal(6, name
="mbits_read_wait")
337 sync
+= Display("RDW %016x done %d "
338 "perm %d rc %d mbits %d shf %d "
339 "valid %d leaf %d bad %d",
340 data
, d_in
.done
, perm_ok
, rc_ok
,
341 mbits
, r
.shift
, valid
, leaf
, badtree
)
343 # set pde and interpret as Radix Tree Page Table Entry (leaf=1 case)
344 comb
+= v
.pde
.eq(data
)
345 comb
+= rpte
.eq(data
)
346 comb
+= rpde
.eq(data
)
349 # valid & leaf: RADIX Page-Table Entry
351 # check permissions and RC bits
352 with m
.If(r
.priv | ~eaa
[EAA_PRIV
]):
353 with m
.If(r
.iside
): # instruction-side request
354 # no IAMR, so no KUEP support for now
355 # deny execute permission if cache inhibited
356 comb
+= perm_ok
.eq(eaa
[EAA_EXE
] & ~rpte
.att
[1])
358 # Load/Store (read/write)
359 comb
+= perm_ok
.eq(eaa
[EAA_WR
] |
360 (eaa
[EAA_RD
] & ~r
.store
))
361 comb
+= rc_ok
.eq(rpte
.r
& (rpte
.c | ~r
.store
))
363 # permissions / rc ok, load TLB, otherwise report error
364 with m
.If(perm_ok
& rc_ok
):
365 comb
+= v
.state
.eq(State
.RADIX_LOAD_TLB
)
366 sync
+= Display("RADIX LEAF data %x att %x eaa %x "
368 "shift %d pgbase %x ",
374 comb
+= v
.state
.eq(State
.RADIX_FINISH
)
375 comb
+= v
.perm_err
.eq(~perm_ok
)
376 # permission error takes precedence over RC error
377 comb
+= v
.rc_error
.eq(perm_ok
)
379 # valid & !leaf: RADIX Page-Directory Entry
381 comb
+= mbits
.eq(rpde
.nls
) # 5 bits NLS into 6-bit-long mbits
382 comb
+= badtree
.eq((mbits
< 5) |
386 comb
+= v
.state
.eq(State
.RADIX_FINISH
)
387 comb
+= v
.badtree
.eq(1)
389 comb
+= v
.shift
.eq(r
.shift
- mbits
)
390 comb
+= v
.mask_size
.eq(mbits
)
391 # pagebase is first 48 bits of NLB, shifted up 1 byte
392 comb
+= v
.pgbase
.eq(Cat(C(0, 8), rpde
.nlb
[:48]))
393 comb
+= v
.state
.eq(State
.RADIX_LOOKUP
)
396 # non-present PTE, generate a DSI
397 comb
+= v
.state
.eq(State
.RADIX_FINISH
)
398 comb
+= v
.invalid
.eq(1)
399 if (display_invalid
):
400 sync
+= Display("MMU: non-present PTE, generate a DSI")
402 def segment_check(self
, m
, v
, r
, data
, finalmask
):
403 """segment_check: checks validity of the request before doing a
404 RADIX lookup. reports either segment error or bad tree if not ok
408 mbits
= Signal(6, name
="mbits_check")
410 comb
+= mbits
.eq(r
.mask_size
)
411 comb
+= v
.shift
.eq(r
.shift
+ (31 - 12) - mbits
)
412 comb
+= nonzero
.eq((r
.addr
[31:62] & ~finalmask
[0:31]).bool())
413 with m
.If((r
.addr
[63] != r
.addr
[62]) # pt3 == 0b11 and pt1 == 0b00
415 comb
+= v
.state
.eq(State
.RADIX_FINISH
)
416 comb
+= v
.segerror
.eq(1)
417 with m
.Elif((mbits
< 5) |
(mbits
> 16) |
418 (mbits
> (r
.shift
+ (31-12)))):
419 comb
+= v
.state
.eq(State
.RADIX_FINISH
)
420 comb
+= v
.badtree
.eq(1)
422 comb
+= v
.state
.eq(State
.RADIX_LOOKUP
)
424 def mmu_0(self
, m
, r
, rin
, l_in
, l_out
, d_out
, addrsh
, mask
):
428 # Multiplex internal SPR values back to loadstore1,
429 # selected by l_in.sprn.
430 with m
.If(l_in
.sprn
[9]):
431 comb
+= l_out
.sprval
.eq(r
.prtbl
)
433 comb
+= l_out
.sprval
.eq(r
.pid
)
435 with m
.If(rin
.valid
):
436 sync
+= Display("MMU got tlb miss for %x", rin
.addr
)
438 with m
.If(l_out
.done
):
439 sync
+= Display("MMU completing op without error")
441 with m
.If(l_out
.err
):
442 sync
+= Display("MMU completing op with err invalid="
443 "%d badtree=%d", l_out
.invalid
, l_out
.badtree
)
445 with m
.If(rin
.state
== State
.RADIX_LOOKUP
):
446 sync
+= Display ("radix lookup shift=%x msize=%x",
449 with m
.If(r
.state
== State
.RADIX_LOOKUP
):
450 sync
+= Display(f
"send load addr=%x addrsh=%x mask=%x",
451 d_out
.addr
, addrsh
, mask
)
453 # update the internal register
456 def elaborate(self
, platform
):
464 finalmask
= Signal(44)
466 self
.rin
= rin
= RegStage("r_in")
469 # get access to prtbl and pid for debug / testing purposes ONLY
470 # (actually, not needed, because setup_regs() triggers mmu direct)
471 # self._prtbl = r.prtbl
480 self
.mmu_0(m
, r
, rin
, l_in
, l_out
, d_out
, addrsh
, mask
)
489 prtb_adr
= Signal(64)
490 pgtb_adr
= Signal(64)
492 tlb_data
= Signal(64)
496 comb
+= v
.valid
.eq(0)
499 comb
+= v
.invalid
.eq(0)
500 comb
+= v
.badtree
.eq(0)
501 comb
+= v
.segerror
.eq(0)
502 comb
+= v
.perm_err
.eq(0)
503 comb
+= v
.rc_error
.eq(0)
504 comb
+= v
.inval_all
.eq(0)
506 # Radix tree data structures in memory are
507 # big-endian, so we need to byte-swap them
508 data
= byte_reverse(m
, "data", d_in
.data
, 8)
510 # generate mask for extracting address fields for PTE addr generation
511 m
.submodules
.pte_mask
= pte_mask
= Mask(16-5)
512 pte_mask
.mask
.name
= "pte_mask"
513 comb
+= pte_mask
.shift
.eq(r
.mask_size
- 5)
514 comb
+= mask
.eq(Cat(C(0x1f, 5), pte_mask
.mask
))
516 # generate mask for extracting address bits to go in
517 # TLB entry in order to support pages > 4kB
518 m
.submodules
.tlb_mask
= tlb_mask
= Mask(44)
519 tlb_mask
.mask
.name
= "tlb_mask"
520 comb
+= tlb_mask
.shift
.eq(r
.shift
)
521 comb
+= finalmask
.eq(tlb_mask
.mask
)
523 # Shift address bits 61--12 right by 0--47 bits and
524 # supply the least significant 16 bits of the result.
525 comb
+= addrsh
.eq(r
.addr
[12:62] >> r
.shift
)
527 with m
.If(r
.state
!= State
.IDLE
):
528 sync
+= Display("MMU state %d %016x", r
.state
, data
)
529 sync
+= Display("addrsh %x r.shift %d r.addr[12:62] %x",
530 addrsh
, r
.shift
, r
.addr
[12:62])
536 with m
.Switch(r
.state
):
537 with m
.Case(State
.IDLE
):
538 self
.radix_tree_idle(m
, l_in
, r
, v
)
540 with m
.Case(State
.DO_TLBIE
):
542 comb
+= tlbie_req
.eq(1)
543 comb
+= v
.state
.eq(State
.TLB_WAIT
)
545 with m
.Case(State
.TLB_WAIT
):
546 with m
.If(d_in
.done
):
547 comb
+= v
.state
.eq(State
.RADIX_FINISH
)
549 with m
.Case(State
.PROC_TBL_READ
):
550 sync
+= Display(" TBL_READ %016x", prtb_adr
)
552 comb
+= prtbl_rd
.eq(1)
553 comb
+= v
.state
.eq(State
.PROC_TBL_WAIT
)
555 with m
.Case(State
.PROC_TBL_WAIT
):
556 with m
.If(d_in
.done
):
557 self
.proc_tbl_wait(m
, v
, r
, data
)
560 comb
+= v
.state
.eq(State
.RADIX_FINISH
)
561 comb
+= v
.badtree
.eq(1)
563 with m
.Case(State
.SEGMENT_CHECK
):
564 self
.segment_check(m
, v
, r
, data
, finalmask
)
566 with m
.Case(State
.RADIX_LOOKUP
):
567 sync
+= Display(" RADIX_LOOKUP")
569 comb
+= v
.state
.eq(State
.RADIX_READ_WAIT
)
571 with m
.Case(State
.RADIX_READ_WAIT
):
572 sync
+= Display(" READ_WAIT")
573 with m
.If(d_in
.done
):
574 self
.radix_read_wait(m
, v
, r
, d_in
, data
)
576 comb
+= v
.state
.eq(State
.RADIX_FINISH
)
577 comb
+= v
.badtree
.eq(1)
579 with m
.Case(State
.RADIX_LOAD_TLB
):
580 comb
+= tlb_load
.eq(1)
583 comb
+= v
.state
.eq(State
.TLB_WAIT
)
585 comb
+= itlb_load
.eq(1)
586 comb
+= v
.state
.eq(State
.IDLE
)
588 with m
.Case(State
.RADIX_FINISH
):
589 sync
+= Display(" RADIX_FINISH")
590 comb
+= v
.state
.eq(State
.IDLE
)
592 # check and report either error or done.
593 with m
.If((v
.state
== State
.RADIX_FINISH
) |
594 ((v
.state
== State
.RADIX_LOAD_TLB
) & r
.iside
)):
595 comb
+= v
.err
.eq(v
.invalid | v
.badtree | v
.segerror
596 | v
.perm_err | v
.rc_error
)
597 comb
+= v
.done
.eq(~v
.err
)
599 # PID is only valid if MSB of address is zero, top 2 bits are Quadrant
600 with m
.If(~r
.addr
[63]): # quadrant 0 (pt0)
601 comb
+= effpid
.eq(r
.pid
)
603 # calculate Process Table Address
604 pr24
= Signal(24, reset_less
=True)
605 prtbla
= PRTBL("prtbla")
606 comb
+= prtbla
.eq(r
.prtbl
)
608 comb
+= pr24
.eq(masked(rpdb
[4:28], effpid
[8:32], finalmask
))
609 comb
+= prtb_adr
.eq(Cat(C(0, 4), effpid
[0:8], pr24
, rpdb
[28:48]))
611 # calculate Page Table Address
612 pg16
= Signal(16, reset_less
=True)
613 comb
+= pg16
.eq(masked(r
.pgbase
[3:19], addrsh
, mask
))
614 comb
+= pgtb_adr
.eq(Cat(C(0, 3), pg16
, r
.pgbase
[19:56]))
616 # calculate Page Table Entry from Real Page Number (leaf=1, RTPTE)
617 rpte
= RTPTE(name
="rpte")
618 comb
+= rpte
.eq(r
.pde
)
619 pd44
= Signal(44, reset_less
=True)
620 comb
+= pd44
.eq(masked(rpte
.rpn
, r
.addr
[12:56], finalmask
))
621 comb
+= pte
.eq(Cat(r
.pde
[0:12], pd44
))
627 with m
.If(tlbie_req
):
628 comb
+= addr
.eq(r
.addr
)
629 with m
.Elif(tlb_load
):
630 comb
+= addr
.eq(Cat(C(0, 12), r
.addr
[12:64]))
631 comb
+= tlb_data
.eq(pte
)
632 with m
.Elif(prtbl_rd
):
633 comb
+= addr
.eq(prtb_adr
)
635 comb
+= addr
.eq(pgtb_adr
)
636 sync
+= Display(f
"pagetable pg16=%x addrsh %x mask %x pgbase=%x "
638 pg16
, addrsh
, mask
, r
.pgbase
, r
.pgbase
[19:56])
640 # connect to other interfaces: LDST, D-Cache, I-Cache
641 comb
+= l_out
.done
.eq(r
.done
)
642 comb
+= l_out
.err
.eq(r
.err
)
643 comb
+= l_out
.invalid
.eq(r
.invalid
)
644 comb
+= l_out
.badtree
.eq(r
.badtree
)
645 comb
+= l_out
.segerr
.eq(r
.segerror
)
646 comb
+= l_out
.perm_error
.eq(r
.perm_err
)
647 comb
+= l_out
.rc_error
.eq(r
.rc_error
)
649 comb
+= d_out
.valid
.eq(dcreq
)
650 comb
+= d_out
.tlbie
.eq(tlbie_req
)
651 comb
+= d_out
.doall
.eq(r
.inval_all
)
652 comb
+= d_out
.tlbld
.eq(tlb_load
)
653 comb
+= d_out
.addr
.eq(addr
)
654 comb
+= d_out
.pte
.eq(tlb_data
)
656 comb
+= i_out
.tlbld
.eq(itlb_load
)
657 comb
+= i_out
.tlbie
.eq(tlbie_req
)
658 comb
+= i_out
.doall
.eq(r
.inval_all
)
659 comb
+= i_out
.addr
.eq(addr
)
660 comb
+= i_out
.pte
.eq(tlb_data
)
667 """simulator process for getting memory load requests
673 return int.from_bytes(x
.to_bytes(8, byteorder
='little'),
674 byteorder
='big', signed
=False)
676 mem
= {0x0: 0x000000, # to get mtspr prtbl working
678 0x10000: # PARTITION_TABLE_2
679 # HR=1 RTS1=0x2 PRTB=0x300 RTS2=0x5 PRTS=0xb
680 b(0xc0000000000030ad),
682 0x30000: # RADIX_ROOT_PTE
683 # V = 1 L = 0 NLB = 0x400 NLS = 9
684 b(0x8000000000040009),
686 0x40000: # RADIX_SECOND_LEVEL
687 # V = 1 L = 1 SW = 0 RPN = 0
688 # R = 1 C = 1 ATT = 0 EAA 0x7
689 b(0xc000000000000187),
692 # slightly different from radix_walk_example.txt: address in microwatt
693 # has the top bit set to indicate hypervisor. here, Quadrant 3's
694 # process table entry is put instead into Quadrant 0. the entry
695 # PROCESS_TABLE_3 should, strictly speaking, be at 0x1000010
697 # 0x1000000: # PROCESS_TABLE_3 (pt0_valid)
698 # # RTS1 = 0x2 RPDB = 0x300 RTS2 = 0x5 RPDS = 12
699 # b(0x40000000000300ac),
701 0x1000000: # PROCESS_TABLE_3 (pt3_valid)
702 # RTS1 = 0x2 RPDB = 0x300 RTS2 = 0x5 RPDS = 13
703 b(0x40000000000300ad),
706 # microwatt mmu.bin first part of test 2.
707 # PRTBL must be set to 0x12000, PID to 1
709 0x0: 0x000000, # to get mtspr prtbl working
710 0x13920: 0x86810000000000c0, # leaf, supposed to be at 0x13920
711 0x10000: 0x0930010000000080, # directory node
712 0x12010: 0x0a00010000000000, # page table
713 0x124000: 0x0000000badc0ffee, # memory to be looked up
716 # microwatt mmu.bin first part of test 4.
717 # PRTBL must be set to 0x12000, PID to 1
719 0x0: 0x000000, # to get mtspr prtbl working
720 0x13858: 0x86a10000000000c0, # leaf node
721 0x10000: 0x0930010000000080, # directory node
722 0x12010: 0x0a00010000000000, # page table
725 # microwatt mmu.bin test 5.
726 # PRTBL must be set to 0x12000, PID to 1
728 0x0: 0x000000, # to get mtspr prtbl working
729 0x13cf8: 0x86b10000000000c0, # leaf node
730 0x13d00: 0x0000000000000000, # invalid leaf node
731 0x10008: 0x0930010000000080, # directory node
732 0x12010: 0x0a00010000000000, # page table
736 while True: # wait for dc_valid
739 dc_valid
= yield (dut
.d_out
.valid
)
740 tlbld
= yield (dut
.d_out
.tlbld
)
744 addr
= yield dut
.d_out
.addr
746 pte
= yield dut
.d_out
.pte
747 print (" DCACHE PTE %x -> %x" % (pte
, addr
))
748 yield dut
.d_in
.done
.eq(1)
750 yield dut
.d_in
.done
.eq(0)
754 print (" DCACHE LOOKUP FAIL %x" % (addr
))
760 yield dut
.d_in
.data
.eq(data
)
761 print (" DCACHE GET %x data %x" % (addr
, data
))
762 yield dut
.d_in
.done
.eq(1)
764 yield dut
.d_in
.done
.eq(0)
769 while not stop
: # wait for dc_valid / err
770 d_valid
= yield (dut
.d_out
.valid
)
772 tlbld
= yield (dut
.d_out
.tlbld
)
773 addr
= yield (dut
.d_out
.addr
)
774 print ("addr %x tlbld %d" % (addr
, tlbld
))
775 l_done
= yield (dut
.l_out
.done
)
776 l_err
= yield (dut
.l_out
.err
)
777 l_badtree
= yield (dut
.l_out
.badtree
)
778 l_permerr
= yield (dut
.l_out
.perm_error
)
779 l_rc_err
= yield (dut
.l_out
.rc_error
)
780 l_segerr
= yield (dut
.l_out
.segerr
)
781 l_invalid
= yield (dut
.l_out
.invalid
)
782 if (l_done
or l_err
or l_badtree
or
783 l_permerr
or l_rc_err
or l_segerr
or l_invalid
):
786 yield dut
.l_in
.valid
.eq(0) # data already in MMU by now
787 yield dut
.l_in
.mtspr
.eq(0) # captured by RegStage(s)
788 yield dut
.l_in
.load
.eq(0) # can reset everything safely
794 # microwatt PRTBL = 0x12000, other test is 0x1000000
800 # MMU MTSPR set prtbl
801 yield dut
.l_in
.mtspr
.eq(1)
802 yield dut
.l_in
.sprn
[9].eq(1) # totally fake way to set SPR=prtbl
803 yield dut
.l_in
.rs
.eq(prtbl
) # set process table
804 yield dut
.l_in
.valid
.eq(1)
805 yield from mmu_wait(dut
)
807 yield dut
.l_in
.sprn
.eq(0)
808 yield dut
.l_in
.rs
.eq(0)
811 prtbl
= yield (dut
.rin
.prtbl
)
812 print ("prtbl after MTSPR %x" % prtbl
)
813 assert prtbl
== prtbl
815 if True: # microwatt test set PIDR
816 # MMU MTSPR set PIDR = 1
817 yield dut
.l_in
.mtspr
.eq(1)
818 yield dut
.l_in
.sprn
[9].eq(0) # totally fake way to set SPR=pidr
819 yield dut
.l_in
.rs
.eq(pidr
) # set process table
820 yield dut
.l_in
.valid
.eq(1)
821 yield from mmu_wait(dut
)
823 yield dut
.l_in
.sprn
.eq(0)
824 yield dut
.l_in
.rs
.eq(0)
827 #yield dut.rin.prtbl.eq(0x1000000) # manually set process table
830 #addr = 0x10000 # original test
831 #addr = 0x124108 # microwatt mmu.bin test 2
832 #addr = 0x10b0d8 # microwatt mmu.bin test 4
833 # these are a misalignment test. one load results in two actual
834 # lookups, one of which has a valid page table entry, the other
835 # does not. we currently do not support misaligned in Loadstore1
836 # therefore these tests fail with an align_intr (0x600) at 0x39fffd
837 addr
= 0x39fffd # microwatt mmu.bin test 5
838 addr
= 0x3a0000 # microwatt mmu.bin test 5
841 yield dut
.l_in
.load
.eq(1)
842 yield dut
.l_in
.priv
.eq(1)
843 yield dut
.l_in
.addr
.eq(addr
)
844 yield dut
.l_in
.valid
.eq(1)
845 yield from mmu_wait(dut
)
847 addr
= yield dut
.d_out
.addr
848 pte
= yield dut
.d_out
.pte
849 tlb_ld
= yield dut
.d_out
.tlbld
850 l_done
= yield (dut
.l_out
.done
)
851 l_err
= yield (dut
.l_out
.err
)
852 l_badtree
= yield (dut
.l_out
.badtree
)
853 print ("translated done %d err %d badtree %d "
854 "addr %x pte %x tlb_ld %d" % \
855 (l_done
, l_err
, l_badtree
, addr
, pte
, tlb_ld
))
858 yield dut
.l_in
.priv
.eq(0)
859 yield dut
.l_in
.addr
.eq(0)
867 vl
= rtlil
.convert(dut
, ports
=[])#dut.ports())
868 with
open("test_mmu.il", "w") as f
:
872 m
.submodules
.mmu
= dut
878 sim
.add_sync_process(wrap(mmu_sim(dut
)))
879 sim
.add_sync_process(wrap(dcache_get(dut
)))
880 with sim
.write_vcd('test_mmu.vcd'):
883 if __name__
== '__main__':