mmu.py add skeleton sim and test functions from regfile/regfile.py
[soc.git] / src / soc / experiment / mmu.py
1 """MMU
2
3 based on Anton Blanchard microwatt mmu.vhdl
4
5 """
6 from enum import Enum, unique
7 from nmigen import (Module, Signal, Elaboratable, Mux, Cat, Repl, signed,
8 ResetSignal)
9 from nmigen.cli import main
10 from nmigen.iocontrol import RecordObject
11
12 # library ieee; use ieee.std_logic_1164.all;
13 # use ieee.numeric_std.all;
14
15 # library work; use work.common.all;
16
17
18 # start from common.vhdl
19 # type Loadstore1ToMmuType is record
20 # valid : std_ulogic;
21 # tlbie : std_ulogic;
22 # slbia : std_ulogic;
23 # mtspr : std_ulogic;
24 # iside : std_ulogic;
25 # load : std_ulogic;
26 # priv : std_ulogic;
27 # sprn : std_ulogic_vector(9 downto 0);
28 # addr : std_ulogic_vector(63 downto 0);
29 # rs : std_ulogic_vector(63 downto 0);
30 # end record;
31 class LoadStore1ToMmuType(RecordObject):
32 def __init__(self):
33 super().__init__()
34 self.valid = Signal()
35 self.tlbie = Signal()
36 self.slbia = Signal()
37 self.mtspr = Signal()
38 self.iside = Signal()
39 self.load = Signal()
40 self.priv = Signal()
41 self.sprn = Signal(10)
42 self.addr = Signal(64)
43 self.rs = Signal(64)
44
45 # type MmuToLoadstore1Type is record
46 # done : std_ulogic;
47 # err : std_ulogic;
48 # invalid : std_ulogic;
49 # badtree : std_ulogic;
50 # segerr : std_ulogic;
51 # perm_error : std_ulogic;
52 # rc_error : std_ulogic;
53 # sprval : std_ulogic_vector(63 downto 0);
54 # end record;
55 class MmuToLoadStore1Type(RecordObject):
56 def __init__(self):
57 super().__init__()
58 self.done = Signal()
59 self.err = Signal()
60 self.invalid = Signal()
61 self.badtree = Signal()
62 self.segerr = Signal()
63 self.perm_error = Signal()
64 self.rc_error = Signal()
65 self.sprval = Signal(64)
66
67 # type MmuToDcacheType is record
68 # valid : std_ulogic;
69 # tlbie : std_ulogic;
70 # doall : std_ulogic;
71 # tlbld : std_ulogic;
72 # addr : std_ulogic_vector(63 downto 0);
73 # pte : std_ulogic_vector(63 downto 0);
74 # end record;
75 class MmuToDcacheType(RecordObject):
76 def __init__(self):
77 super().__init__()
78 self.valid = Signal()
79 self.tlbie = Signal()
80 self.doall = Signal()
81 self.tlbld = Signal()
82 self.addr = Signal(64)
83 self.pte = Signal(64)
84
85 # type DcacheToMmuType is record
86 # stall : std_ulogic;
87 # done : std_ulogic;
88 # err : std_ulogic;
89 # data : std_ulogic_vector(63 downto 0);
90 # end record;
91 class DcacheToMmuType(RecordObject):
92 def __init__(self):
93 super().__init__()
94 self.stall = Signal()
95 self.done = Signal()
96 self.err = Signal()
97 self.data = Signal(64)
98
99
100 # type MmuToIcacheType is record
101 # tlbld : std_ulogic;
102 # tlbie : std_ulogic;
103 # doall : std_ulogic;
104 # addr : std_ulogic_vector(63 downto 0);
105 # pte : std_ulogic_vector(63 downto 0);
106 # end record;
107 class MmuToIcacheType(RecordObject):
108 def __init__(self):
109 self.tlbld = Signal()
110 self.tlbie = Signal()
111 self.doall = Signal()
112 self.addr = Signal(64)
113 self.pte = Signal(64)
114 # end from common.vhdl
115
116
117
118
119
120
121 # -- Radix MMU
122 # -- Supports 4-level trees as in arch 3.0B, but not the
123 # -- two-step translation
124 # -- for guests under a hypervisor (i.e. there is no gRA -> hRA translation).
125
126 # type state_t is
127 # (IDLE,
128 # DO_TLBIE,
129 # TLB_WAIT,
130 # PROC_TBL_READ,
131 # PROC_TBL_WAIT,
132 # SEGMENT_CHECK,
133 # RADIX_LOOKUP,
134 # RADIX_READ_WAIT,
135 # RADIX_LOAD_TLB,
136 # RADIX_FINISH
137 # );
138
139 # architecture behave of mmu is
140
141 @unique
142 class State(Enum):
143 IDLE = 0
144 DO_TLBIE = 1
145 TLB_WAIT = 2
146 PROC_TBL_READ = 3
147 PROC_TBL_WAIT = 4
148 SEGMENT_CHECK = 5
149 RADIX_LOOKUP = 6
150 RADIX_READ_WAIT = 7
151 RADIX_LOAD_TLB = 8
152 RADIX_FINISH = 9
153
154 # type reg_stage_t is record
155 # -- latched request from loadstore1
156 # valid : std_ulogic;
157 # iside : std_ulogic;
158 # store : std_ulogic;
159 # priv : std_ulogic;
160 # addr : std_ulogic_vector(63 downto 0);
161 # inval_all : std_ulogic;
162 # -- config SPRs
163 # prtbl : std_ulogic_vector(63 downto 0);
164 # pid : std_ulogic_vector(31 downto 0);
165 # -- internal state
166 # state : state_t;
167 # done : std_ulogic;
168 # err : std_ulogic;
169 # pgtbl0 : std_ulogic_vector(63 downto 0);
170 # pt0_valid : std_ulogic;
171 # pgtbl3 : std_ulogic_vector(63 downto 0);
172 # pt3_valid : std_ulogic;
173 # shift : unsigned(5 downto 0);
174 # mask_size : unsigned(4 downto 0);
175 # pgbase : std_ulogic_vector(55 downto 0);
176 # pde : std_ulogic_vector(63 downto 0);
177 # invalid : std_ulogic;
178 # badtree : std_ulogic;
179 # segerror : std_ulogic;
180 # perm_err : std_ulogic;
181 # rc_error : std_ulogic;
182 # end record;
183
184
185 class RegStage(RecordObject):
186 def __init__(self, name=None):
187 super().__init__(self, name=name)
188 # latched request from loadstore1
189 self.valid = Signal(reset_less=True)
190 self.iside = Signal(reset_less=True)
191 self.store = Signal(reset_less=True)
192 self.priv = Signal(reset_less=True)
193 self.addr = Signal(64, reset_less=True)
194 self.inval_all = Signal(reset_less=True)
195 # config SPRs
196 self.prtbl = Signal(64, reset_less=True)
197 self.pid = Signal(32, reset_less=True)
198 # internal state
199 self.state = State.IDLE
200 self.done = Signal(reset_less=True)
201 self.err = Signal(reset_less=True)
202 self.pgtbl0 = Signal(64, reset_less=True)
203 self.pt0_valid = Signal(reset_less=True)
204 self.pgtbl3 = Signal(64, reset_less=True)
205 self.pt3_valid = Signal(reset_less=True)
206 self.shift = Signal(6, reset_less=True)
207 self.mask_size = Signal(5, reset_less=True)
208 self.pgbase = Signal(56, reset_less=True)
209 self.pde = Signal(64, reset_less=True)
210 self.invalid = Signal(reset_less=True)
211 self.badtree = Signal(reset_less=True)
212 self.segerror = Signal(reset_less=True)
213 self.perm_err = Signal(reset_less=True)
214 self.rc_error = Signal(reset_less=True)
215
216
217 # Radix MMU
218 # Supports 4-level trees as in arch 3.0B, but not the
219 # two-step translation for guests under a hypervisor
220 # (i.e. there is no gRA -> hRA translation).
221 class MMU(Elaboratable):
222 # entity mmu is
223 # port (
224 # clk : in std_ulogic;
225 # rst : in std_ulogic;
226 #
227 # l_in : in Loadstore1ToMmuType;
228 # l_out : out MmuToLoadstore1Type;
229 #
230 # d_out : out MmuToDcacheType;
231 # d_in : in DcacheToMmuType;
232 #
233 # i_out : out MmuToIcacheType
234 # );
235 # end mmu;
236 def __init__(self):
237 self.l_in = LoadStore1ToMmuType()
238 self.l_out = MmuToLoadStore1Type()
239 self.d_out = MmuToDcacheType()
240 self.d_in = DcacheToMmuType()
241 self.i_out = MmuToIcacheType()
242
243 # signal addrsh : std_ulogic_vector(15 downto 0);
244 # signal mask : std_ulogic_vector(15 downto 0);
245 # signal finalmask : std_ulogic_vector(43 downto 0);
246 self.addrsh = Signal(16)
247 self.mask = Signal(16)
248 self.finalmask = Signal(44)
249
250 # signal r, rin : reg_stage_t;
251 self.r = RegStage()
252 self.rin = RegStage()
253
254 # begin
255 def elaborate(self, platform):
256 # -- Multiplex internal SPR values back to loadstore1,
257 # -- selected by l_in.sprn.
258 # Multiplex internal SPR values back to loadstore1,
259 # selected by l_in.sprn.
260 m = Module()
261
262 comb = m.d.comb
263 sync = m.d.sync
264
265 l_in = self.l_in
266 l_out = self.l_out
267 d_out = self.d_out
268 d_in = self.d_in
269 i_out = self.i_out
270
271 addrsh = self.addrsh
272 mask = self.mask
273 finalmask = self.finalmask
274
275 r = self.r
276 rin = self.rin
277
278 # l_out.sprval <= r.prtbl when l_in.sprn(9) = '1'
279 with m.If(l_in.sprn[9]):
280 comb += l_out.sprval.eq(r.prtbl)
281
282 # else x"00000000" & r.pid;
283 with m.Else():
284 comb += l_out.sprval.eq(Cat(r.pid,
285 Const(0x00000000, 32))
286
287 # if rin.valid = '1' then
288 # report "MMU got tlb miss for "
289 # & to_hstring(rin.addr);
290 # end if;
291 with m.If(rin.valid):
292 print(f"MMU got tlb miss for {rin.addr}")
293
294 # if l_out.done = '1' then
295 # report "MMU completing op without error";
296 # end if;
297 with m.If(l_out.done):
298 print("MMU completing op without error")
299
300 # if l_out.err = '1' then
301 # report "MMU completing op with err invalid=" &
302 # std_ulogic'image(l_out.invalid) &
303 # " badtree=" & std_ulogic'image(
304 # l_out.badtree);
305 # end if;
306 with m.If(l_out.err):
307 print(f"MMU completing op with err invalid=
308 {l_out.invalid} badtree={l_out.badtree}")
309
310 # if rin.state = RADIX_LOOKUP then
311 # report "radix lookup shift=" & integer'image(
312 # to_integer(rin.shift)) & " msize=" &
313 # integer'image(to_integer(rin.mask_size));
314 # end if;
315 with m.If(rin.state == State.RADIX_LOOKUP):
316 print(f"radix lookup shift={rin.shift}
317 msize={rin.mask_size}")
318
319 # if r.state = RADIX_LOOKUP then
320 # report "send load addr=" & to_hstring(d_out.addr)
321 # & " addrsh=" & to_hstring(addrsh) &
322 # " mask=" & to_hstring(mask);
323 # end if;
324 with m.If(r.state == State.RADIX_LOOKUP):
325 print(f"send load addr={d_out.addr}
326 addrsh={addrsh} mask={mask}")
327
328 # r <= rin;
329 sync += r.eq(rin)
330 # end process;
331
332 # -- generate mask for extracting address fields for PTE address
333 # -- generation
334 # addrmaskgen: process(all)
335 # generate mask for extracting address fields for PTE address
336 # generation
337 class AddrMaskGen(Elaboratable, MMU):
338 def __init__(self):
339 # variable m : std_ulogic_vector(15 downto 0);
340 super().__init__()
341 self.msk = Signal(16)
342
343 # begin
344 def elaborate(self, platform):
345 m = Module()
346
347 comb = m.d.comb
348 sync = m.d.sync
349
350 rst = ResetSignal()
351
352 msk = self.msk
353
354 r = self.r
355 mask = self.mask
356
357 # -- mask_count has to be >= 5
358 # m := x"001f";
359 # mask_count has to be >= 5
360 comb += mask.eq(Const(0x001F, 16)
361
362 # for i in 5 to 15 loop
363 for i in range(5,16):
364 # if i < to_integer(r.mask_size) then
365 with m.If(i < r.mask_size):
366 # m(i) := '1';
367 comb += msk[i].eq(1)
368 # end if;
369 # end loop;
370 # mask <= m;
371 comb += mask.eq(msk)
372 # end process;
373
374 # -- generate mask for extracting address bits to go in
375 # -- TLB entry in order to support pages > 4kB
376 # finalmaskgen: process(all)
377 # generate mask for extracting address bits to go in
378 # TLB entry in order to support pages > 4kB
379 class FinalMaskGen(Elaboratable, MMU):
380 def __init__(self):
381 # variable m : std_ulogic_vector(43 downto 0);
382 super().__init__()
383 self.msk = Signal(44)
384
385 # begin
386 def elaborate(self, platform):
387 m = Module()
388
389 comb = m.d.comb
390 sync = m.d.sync
391
392 rst = ResetSignal()
393
394 mask = self.mask
395 r = self.r
396
397 msk = self.msk
398
399 # for i in 0 to 43 loop
400 for i in range(44):
401 # if i < to_integer(r.shift) then
402 with m.If(i < r.shift):
403 # m(i) := '1';
404 comb += msk.eq(1)
405 # end if;
406 # end loop;
407 # finalmask <= m;
408 comb += self.finalmask(mask)
409 # end process;
410
411 # mmu_1: process(all)
412 class MMU1(Elaboratable):
413 def __init__(self):
414 # variable v : reg_stage_t;
415 # variable dcreq : std_ulogic;
416 # variable tlb_load : std_ulogic;
417 # variable itlb_load : std_ulogic;
418 # variable tlbie_req : std_ulogic;
419 # variable prtbl_rd : std_ulogic;
420 # variable pt_valid : std_ulogic;
421 # variable effpid : std_ulogic_vector(31 downto 0);
422 # variable prtable_addr : std_ulogic_vector(63 downto 0);
423 # variable rts : unsigned(5 downto 0);
424 # variable mbits : unsigned(5 downto 0);
425 # variable pgtable_addr : std_ulogic_vector(63 downto 0);
426 # variable pte : std_ulogic_vector(63 downto 0);
427 # variable tlb_data : std_ulogic_vector(63 downto 0);
428 # variable nonzero : std_ulogic;
429 # variable pgtbl : std_ulogic_vector(63 downto 0);
430 # variable perm_ok : std_ulogic;
431 # variable rc_ok : std_ulogic;
432 # variable addr : std_ulogic_vector(63 downto 0);
433 # variable data : std_ulogic_vector(63 downto 0);
434 self.v = RegStage()
435 self.dcrq = Signal()
436 self.tlb_load = Signal()
437 self.itlb_load = Signal()
438 self.tlbie_req = Signal()
439 self.prtbl_rd = Signal()
440 self.pt_valid = Signal()
441 self.effpid = Signal(32)
442 self.prtable_addr = Signal(64)
443 self.rts = Signal(6)
444 self.mbits = Signal(6)
445 self.pgtable_addr = Signal(64)
446 self.pte = Signal(64)
447 self.tlb_data = Signal(64)
448 self.nonzero = Signal()
449 self.pgtbl = Signal(64)
450 self.perm_ok = Signal()
451 self.rc_ok = Signal()
452 self.addr = Signal(64)
453 self.data = Signal(64)
454
455 # begin
456 def elaborate(self, platform):
457
458 m = Module()
459
460 comb = m.d.comb
461 sync = m.d.sync
462
463 l_in = self.l_in
464 l_out = self.l_out
465 d_out = self.d_out
466 d_in = self.d_in
467 i_out = self.i_out
468
469 r = self.r
470
471 v = self.v
472 dcrq = self.dcrq
473 tlb_load = self.tlb_load
474 itlb_load = self.itlb_load
475 tlbie_req = self.tlbie_req
476 prtbl_rd = self.prtbl_rd
477 pt_valid = self.pt_valid
478 effpid = self.effpid
479 prtable_addr = self.prtable_addr
480 rts = self.rts
481 mbits = self.mbits
482 pgtable_addr = self.pgtable_addr
483 pte = self.pte
484 tlb_data = self.tlb_data
485 nonzero = self.nonzero
486 pgtbl = self.pgtbl
487 perm_ok = self.perm_ok
488 rc_ok = self.rc_ok
489 addr = self.addr
490 data = self.data
491
492 # v := r;
493 # v.valid := '0';
494 # dcreq := '0';
495 # v.done := '0';
496 # v.err := '0';
497 # v.invalid := '0';
498 # v.badtree := '0';
499 # v.segerror := '0';
500 # v.perm_err := '0';
501 # v.rc_error := '0';
502 # tlb_load := '0';
503 # itlb_load := '0';
504 # tlbie_req := '0';
505 # v.inval_all := '0';
506 # prtbl_rd := '0';
507
508 comb += v.eq(r)
509 comb += v.valid.eq(0)
510 comb += dcreq.eq(0)
511 comb += v.done.eq(0)
512 comb += v.err.eq(0)
513 comb += v.invalid.eq(0)
514 comb += v.badtree.eq(0)
515 comb += v.segerror.eq(0)
516 comb += v.perm_err.eq(0)
517 comb += v.rc_error.eq(0)
518 comb += tlb_load.eq(0)
519 comb += itlb_load.eq(0)
520 comb += tlbie_req.eq(0)
521 comb += v.inval_all.eq(0)
522 comb += prtbl_rd.eq(0)
523
524
525 # -- Radix tree data structures in memory are
526 # -- big-endian, so we need to byte-swap them
527 # for i in 0 to 7 loop
528 # Radix tree data structures in memory are
529 # big-endian, so we need to byte-swap them
530 for i in range(8):
531 # data(i * 8 + 7 downto i * 8) := d_in.data(
532 # (7 - i) * 8 + 7 downto (7 - i) * 8);
533 comb += data[i * 8:i * 8 + 7 + 1].eq(
534 d_in.data[
535 (7 - i) * 8:(7 - i) * 8 + 7 + 1
536 ])
537 # end loop;
538
539 # case r.state is
540 with m.Switch(r.state):
541 # when IDLE =>
542 with m.Case(State.IDLE):
543 # if l_in.addr(63) = '0' then
544 # pgtbl := r.pgtbl0;
545 # pt_valid := r.pt0_valid;
546 with m.If(~l_in.addr[63]):
547 comb += pgtbl.eq(r.pgtbl0)
548 comb += pt_valid.eq(r.pt0_valid)
549 # else
550 # pgtbl := r.pgtbl3;
551 # pt_valid := r.pt3_valid;
552 with m.Else():
553 comb += pgtbl.eq(r.pt3_valid)
554 comb += pt_valid.eq(r.pt3_valid)
555 # end if;
556
557 # -- rts == radix tree size, # address bits being
558 # -- translated
559 # rts := unsigned('0' & pgtbl(62 downto 61) &
560 # pgtbl(7 downto 5));
561 # rts == radix tree size, number of address bits
562 # being translated
563 comb += rts.eq((Cat(
564 Cat(
565 pgtbl[5:8],
566 pgtbl[61:63]
567 ),
568 Const(0b0,1)
569 )).as_unsigned())
570
571 # -- mbits == # address bits to index top level
572 # -- of tree
573 # mbits := unsigned('0' & pgtbl(4 downto 0));
574 # mbits == number of address bits to index top
575 # level of tree
576 comb += mbits.eq((
577 Cat(pgtbl[0:5], Const(0b0, 1))
578 ).as_unsigned())
579 # -- set v.shift to rts so that we can use finalmask
580 # -- for the segment check
581 # v.shift := rts;
582 # v.mask_size := mbits(4 downto 0);
583 # v.pgbase := pgtbl(55 downto 8) & x"00";
584 # set v.shift to rts so that we can use finalmask
585 # for the segment check
586 comb += v.shift.eq(rts)
587 comb += v.mask_size.eq(mbits[0:5])
588 comb += v.pgbase.eq(Cat(
589 Cont(0x00, 8),
590 pgtbl[8:56]
591 ))
592
593 # if l_in.valid = '1' then
594 with m.If(l_in.valid):
595 # v.addr := l_in.addr;
596 # v.iside := l_in.iside;
597 # v.store := not (l_in.load or l_in.iside);
598 # v.priv := l_in.priv;
599 comb += v.addr.eq(l_in.addr
600 comb += v.iside.eq(l_in.iside)
601 comb += v.store.eq(~(l_in.load | l_in.siside))
602 # if l_in.tlbie = '1' then
603 with m.If(l_in.tlbie):
604 # -- Invalidate all iTLB/dTLB entries for
605 # -- tlbie with RB[IS] != 0 or RB[AP] != 0,
606 # -- or for slbia
607 # v.inval_all := l_in.slbia or l_in.addr(11)
608 # or l_in.addr(10) or
609 # l_in.addr(7) or l_in.addr(6)
610 # or l_in.addr(5);
611 # Invalidate all iTLB/dTLB entries for
612 # tlbie with RB[IS] != 0 or RB[AP] != 0,
613 # or for slbia
614 comb += v.inval_all.eq(l_in.slbia
615 | l_in.addr[11]
616 | l_in.addr[10]
617 | l_in.addr[7]
618 | l_in.addr[6]
619 | l_in.addr[5]
620 )
621 # -- The RIC field of the tlbie instruction
622 # -- comes across on the sprn bus as bits 2--3.
623 # -- RIC=2 flushes process table caches.
624 # if l_in.sprn(3) = '1' then
625 # The RIC field of the tlbie instruction
626 # comes across on the sprn bus as bits 2--3.
627 # RIC=2 flushes process table caches.
628 with m.If(l_in.sprn[3]):
629 # v.pt0_valid := '0';
630 # v.pt3_valid := '0';
631 comb += v.pt0_valid.eq(0)
632 comb += v.pt3_valid.eq(0)
633 # end if;
634 # v.state := DO_TLBIE;
635 comb += v.state.eq(State.DO_TLBIE)
636 # else
637 with m.Else():
638 # v.valid := '1';
639 comb += v.valid.eq(1)
640 # if pt_valid = '0' then
641 with m.If(~pt_valid):
642 # -- need to fetch process table entry
643 # -- set v.shift so we can use finalmask
644 # -- for generating the process table
645 # -- entry address
646 # v.shift := unsigned('0' & r.prtbl(
647 # 4 downto 0));
648 # v.state := PROC_TBL_READ;
649 # need to fetch process table entry
650 # set v.shift so we can use finalmask
651 # for generating the process table
652 # entry address
653 comb += v.shift.eq((Cat(
654 r.prtble[0:5],
655 Const(0b0, 1)
656 )).as_unsigned())
657 comb += v.state.eq(State.PROC_TBL_READ)
658
659 # elsif mbits = 0 then
660 with m.If(~mbits):
661 # -- Use RPDS = 0 to disable radix
662 # -- tree walks
663 # v.state := RADIX_FINISH;
664 # v.invalid := '1';
665 # Use RPDS = 0 to disable radix
666 # tree walks
667 comb += v.state.eq(State.RADIX_FINISH)
668 comb += v.invalid.eq(1)
669 # else
670 with m.Else():
671 # v.state := SEGMENT_CHECK;
672 comb += v.state.eq(State.SEGMENT_CHECK)
673 # end if;
674 # end if;
675 # end if;
676
677 # if l_in.mtspr = '1' then
678 with m.If(l_in.mtspr):
679 # -- Move to PID needs to invalidate L1 TLBs
680 # -- and cached pgtbl0 value. Move to PRTBL
681 # -- does that plus invalidating the cached
682 # -- pgtbl3 value as well.
683 # if l_in.sprn(9) = '0' then
684 # Move to PID needs to invalidate L1 TLBs
685 # and cached pgtbl0 value. Move to PRTBL
686 # does that plus invalidating the cached
687 # pgtbl3 value as well.
688 with m.If(~l_in.sprn[9]):
689 # v.pid := l_in.rs(31 downto 0);
690 comb += v.pid.eq(l_in.rs[0:32])
691 # else
692 with m.Else():
693 # v.prtbl := l_in.rs;
694 # v.pt3_valid := '0';
695 comb += v.prtbl.eq(l_in.rs)
696 comb += v.pt3_valid.eq(0)
697 # end if;
698
699 # v.pt0_valid := '0';
700 # v.inval_all := '1';
701 # v.state := DO_TLBIE;
702 comb += v.pt0_valid.eq(0)
703 comb += v.inval_all.eq(1)
704 comb += v.state.eq(State.DO_TLBIE)
705 # end if;
706
707 # when DO_TLBIE =>
708 with m.Case(State.DO_TLBIE):
709 # dcreq := '1';
710 # tlbie_req := '1';
711 # v.state := TLB_WAIT;
712 comb += dcreq.eq(1)
713 comb += tlbie_req.eq(1)
714 comb += v.state.eq(State.TLB_WAIT)
715
716 # when TLB_WAIT =>
717 with m.Case(State.TLB_WAIT):
718 # if d_in.done = '1' then
719 with m.If(d_in.done):
720 # v.state := RADIX_FINISH;
721 comb += v.state.eq(State.RADIX_FINISH)
722 # end if;
723
724 # when PROC_TBL_READ =>
725 with m.Case(State.PROC_TBL_READ):
726 # dcreq := '1';
727 # prtbl_rd := '1';
728 # v.state := PROC_TBL_WAIT;
729 comb += dcreq.eq(1)
730 comb += prtbl_rd.eq(1)
731 comb += v.state.eq(State.PROC_TBL_WAIT)
732
733 # when PROC_TBL_WAIT =>
734 with m.Case(State.PROC_TBL_WAIT):
735 # if d_in.done = '1' then
736 with m.If(d_in.done):
737 # if r.addr(63) = '1' then
738 with m.If(r.addr[63]):
739 # v.pgtbl3 := data;
740 # v.pt3_valid := '1';
741 comb += v.pgtbl3.eq(data)
742 comb += v.pt3_valid.eq(1)
743 # else
744 with m.Else():
745 # v.pgtbl0 := data;
746 # v.pt0_valid := '1';
747 comb += v.pgtbl0.eq(data)
748 comb += v.pt0_valid.eq(1)
749 # end if;
750 # -- rts == radix tree size, # address bits
751 # -- being translated
752 # rts := unsigned('0' & data(62 downto 61) &
753 # data(7 downto 5));
754 # rts == radix tree size, # address bits
755 # being translated
756 comb += rts.eq((
757 Cat(
758 Cat(
759 data[5:8],
760 data[61:63]
761 ),
762 Const(0b0, 1)
763 )
764 ).as_unsigned())
765
766 # -- mbits == # address bits to index
767 # -- top level of tree
768 # mbits := unsigned('0' & data(4 downto 0));
769 # mbits == # address bits to index
770 # top level of tree
771 comb += mbits.eq((
772 Cat(
773 data[0:5],
774 Const(0b0, 1)
775 )
776 ).as_unsigned())
777 # -- set v.shift to rts so that we can use
778 # -- finalmask for the segment check
779 # v.shift := rts;
780 # v.mask_size := mbits(4 downto 0);
781 # v.pgbase := data(55 downto 8) & x"00";
782 # set v.shift to rts so that we can use
783 # finalmask for the segment check
784 comb += v.shift.eq(rts)
785 comb += v.mask_size.eq(mbits[0:5])
786 comb += v.pgbase.eq(
787 Cat(
788 Const(0x00, 8),
789 data[8:56]
790 )
791 )
792
793 # if mbits = 0 then
794 with m.If(~mbits):
795 # v.state := RADIX_FINISH;
796 # v.invalid := '1';
797 comb += v.state.eq(State.RADIX_FINISH)
798 comb += v.invalid.eq(1)
799 # else
800 # v.state := SEGMENT_CHECK;
801 comb += v.state.eq(State.SEGMENT_CHECK)
802 # end if;
803 # end if;
804
805 # if d_in.err = '1' then
806 with m.If(d_in.err):
807 # v.state := RADIX_FINISH;
808 # v.badtree := '1';
809 comb += v.state.eq(State.RADIX_FINISH)
810 comb += v.badtree.eq(1)
811 # end if;
812
813 # when SEGMENT_CHECK =>
814 with m.Case(State.SEGMENT_CHECK):
815 # mbits := '0' & r.mask_size;
816 # v.shift := r.shift + (31 - 12) - mbits;
817 # nonzero := or(r.addr(61 downto 31) and
818 # not finalmask(30 downto 0));
819 comb += mbits.eq(
820 Cat(
821 r.mask_size,
822 Const(0b0, 1)
823 )
824 )
825 comb += v.shift.eq(r.shift + (31 -12) - mbits)
826 comb += nonzero.eq((
827 r.addr[31:62] & ~finalmask[0:31]
828 ).bool())
829 # if r.addr(63) /= r.addr(62) or nonzero = '1' then
830 # v.state := RADIX_FINISH;
831 # v.segerror := '1';
832 with m.If((r.addr[63] != r.addr[62])
833 | nonzero):
834 comb += v.state.eq(State.RADIX_FINISH)
835 comb += v.segerror.eq(1)
836 # elsif mbits < 5 or mbits > 16 or mbits
837 # > (r.shift + (31 - 12)) then
838 # v.state := RADIX_FINISH;
839 # v.badtree := '1';
840 with m.If((mbits < 5) | (mbits > 16)
841 | (mbits > (r.shift + (31-12)))):
842 comb += v.state.eq(State.RADIX_FINISH)
843 comb += v.badtree.eq(1)
844 # else
845 # v.state := RADIX_LOOKUP;
846 with m.Else():
847 comb += v.state.eq(State.RADIX_LOOKUP)
848 # end if;
849 #
850 # when RADIX_LOOKUP =>
851 with m.Case(State.RADIX_LOOKUP):
852 # dcreq := '1';
853 # v.state := RADIX_READ_WAIT;
854 comb += dcreq.eq(1)
855 comb += v.state.eq(State.RADIX_READ_WAIT)
856
857 # when RADIX_READ_WAIT =>
858 with m.Case(State.RADIX_READ_WAIT):
859 # if d_in.done = '1' then
860 with m.If(d_in.done):
861 # v.pde := data;
862 comb += v.pde.eq(data)
863 # -- test valid bit
864 # if data(63) = '1' then
865 # test valid bit
866 with m.If(data[63]):
867 # -- test leaf bit
868 # if data(62) = '1' then
869 # test leaf bit
870 with m.If(data[62]):
871 # -- check permissions and RC bits
872 # perm_ok := '0';
873 comb += perm_ok.eq(0)
874 # if r.priv = '1' or data(3) = '0' then
875 with m.If(r.priv | ~data[3])):
876 # if r.iside = '0' then
877 # perm_ok := data(1) or (data(2)
878 # and not r.store);
879 with m.If(~r.iside):
880 comb += perm_ok.eq(
881 (data[1] | data[2])
882 & (~r.store)
883 )
884 # else
885 with m.Else():
886 # -- no IAMR, so no KUEP support
887 # -- for now deny execute
888 # -- permission if cache inhibited
889 # perm_ok :=
890 # data(0) and not data(5);
891 # no IAMR, so no KUEP support
892 # for now deny execute
893 # permission if cache inhibited
894 comb += perm_ok.eq(
895 data[0] & ~data[5]
896 )
897 # end if;
898 # end if;
899
900 # rc_ok := data(8) and (data(7) or
901 # not r.store);
902 comb += rc_ok.eq(
903 data[8] &
904 (data[7] | (~r.store))
905 )
906 # if perm_ok = '1' and rc_ok = '1' then
907 # v.state := RADIX_LOAD_TLB;
908 with m.If(perm_ok & rc_ok):
909 comb += v.state.eq(
910 State.RADIX_LOAD_TLB
911 )
912 # else
913 with m.Else():
914 # v.state := RADIX_FINISH;
915 # v.perm_err := not perm_ok;
916 # -- permission error takes precedence
917 # -- over RC error
918 # v.rc_error := perm_ok;
919 comb += vl.state.eq(
920 State.RADIX_FINISH
921 )
922 comb += v.perm_err.eq(~perm_ok)
923 # permission error takes precedence
924 # over RC error
925 comb += v.rc_error.eq(perm_ok)
926 # end if;
927 # else
928 with m.Else():
929 # mbits := unsigned('0' &
930 # data(4 downto 0));
931 comb += mbits.eq((
932 Cat(
933 data[0:5]
934 Cont(0b0,1)
935 )
936 ).as_unsigned())
937 # if mbits < 5 or mbits > 16 or
938 # mbits > r.shift then
939 # v.state := RADIX_FINISH;
940 # v.badtree := '1';
941 with m.If((mbits < 5) | (mbits > 16) |
942 (mbits > r.shift)):
943 comb += v.state.eq(
944 State.RADIX_FINISH
945 )
946 comb += v.badtree.eq(1)
947 # else
948 with m.Else():
949 # v.shift := v.shift - mbits;
950 # v.mask_size := mbits(4 downto 0);
951 # v.pgbase := data(55 downto 8)
952 # & x"00";
953 # v.state := RADIX_LOOKUP;
954 comb += v.shift.eq(v.shif - mbits)
955 comb += v.mask_size.eq(mbits[0:5])
956 comb += v.pgbase.eq(
957 Cat(
958 Const(0x00, 8),
959 mbits[8:56]
960 )
961 )
962 comb += v.state.eq(
963 State.RADIX_LOOKUP
964 )
965 # end if;
966 # end if;
967 # else
968 with m.Else():
969 # -- non-present PTE, generate a DSI
970 # v.state := RADIX_FINISH;
971 # v.invalid := '1';
972 # non-present PTE, generate a DSI
973 comb += v.state.eq(State.RADIX_FINISH)
974 comb += v.invalid.eq(1)
975 # end if;
976 # end if;
977
978 # if d_in.err = '1' then
979 with m.If(d_in.err):
980 # v.state := RADIX_FINISH;
981 # v.badtree := '1';
982 comb += v.state.eq(State.RADIX_FINISH)
983 comb += v.badtree.eq(1)
984 # end if;
985
986 # when RADIX_LOAD_TLB =>
987 with m.Case(State.RADIX_LOAD_TLB):
988 # tlb_load := '1';
989 comb += tlb_load.eq(1)
990 # if r.iside = '0' then
991 with m.If(~r.iside):
992 # dcreq := '1';
993 # v.state := TLB_WAIT;
994 comb += dcreq.eq(1)
995 comb += v.state.eq(State.TLB_WAIT)
996 # else
997 with m.Else():
998 # itlb_load := '1';
999 # v.state := IDLE;
1000 comb += itlb_load.eq(1)
1001 comb += v.state.eq(State.IDLE)
1002 # end if;
1003
1004 # when RADIX_FINISH =>
1005 # v.state := IDLE;
1006 with m.Case(State.RADIX_FINISH):
1007 # v.state := IDLE
1008 comb += v.state.eq(State.IDLE)
1009 # end case;
1010 #
1011 # if v.state = RADIX_FINISH or (v.state = RADIX_LOAD_TLB
1012 # and r.iside = '1') then
1013 with m.If(v.state == State.RADIX_FINISH
1014 | (v.state == State.RADIX_LOAD_TLB & r.iside)
1015 )
1016 # v.err := v.invalid or v.badtree or v.segerror
1017 # or v.perm_err or v.rc_error;
1018 # v.done := not v.err;
1019 comb += v.err.eq(v.invalid | v.badtree | v.segerror
1020 | v.perm_err | v.rc_error)
1021 comb += v.done.eq(~v.err)
1022 # end if;
1023
1024 # if r.addr(63) = '1' then
1025 with m.If(r.addr[63]):
1026 # effpid := x"00000000";
1027 comb += effpid.eq(Const(0x00000000, 32))
1028 # else
1029 with m.Else():
1030 # effpid := r.pid;
1031 comb += effpid.eq(r.pid)
1032 # end if;
1033 # prtable_addr := x"00" & r.prtbl(55 downto 36) &
1034 # ((r.prtbl(35 downto 12) and not finalmask(
1035 # 23 downto 0)) or (effpid(31 downto 8) and
1036 # finalmask(23 downto 0))) & effpid(7 downto 0)
1037 # & "0000";
1038 comb += prtable_addr.eq(
1039 Cat(
1040 Cat(
1041 Cat(
1042 Cat(Const(0b0000, 4), effpid[0:8]),
1043 (
1044 (r.prtble[12:36] & ~finalmask[0:24])
1045 | effpid[8:32] & finalmask[0:24]
1046 )
1047 ),
1048 r.prtbl[36:56]
1049 ),
1050 Const(0x00, 8)
1051 )
1052 )
1053
1054 # pgtable_addr := x"00" & r.pgbase(55 downto 19) &
1055 # ((r.pgbase(18 downto 3) and not mask) or
1056 # (addrsh and mask)) & "000";
1057 comb += pgtable_addr.eq(
1058 Cat(
1059 Cat(
1060 Const(0b000, 3),
1061 (
1062 (r.pgbase[3:19] & ~mask)
1063 | (addrsh & mask)
1064 )
1065 ),
1066 Const(0x00, 8)
1067 )
1068 )
1069
1070 # pte := x"00" & ((r.pde(55 downto 12) and not finalmask) or
1071 # (r.addr(55 downto 12) and finalmask)) & r.pde(11 downto 0);
1072 comb += pte.eq(
1073 Cat(
1074 Cat(
1075 r.pde[0:12],
1076 (
1077 (r.pde[12:56] & ~finalmask)
1078 | (r.addr[12:56] & finalmask)
1079 )
1080 ),
1081 Const(0x00, 8)
1082 )
1083 )
1084
1085 # -- update registers
1086 # rin <= v;
1087 # update registers
1088 rin.eq(v)
1089
1090 # -- drive outputs
1091 # if tlbie_req = '1' then
1092 # drive outputs
1093 with m.If(tlbie_req):
1094 # addr := r.addr;
1095 # tlb_data := (others => '0');
1096 comb += addr.eq(r.addr)
1097 # elsif tlb_load = '1' then
1098 with m.If(tlb_load):
1099 # addr := r.addr(63 downto 12) & x"000";
1100 # tlb_data := pte;
1101 comb += addr.eq(Cat(Const(0x000, 12), r.addr[12:64]))
1102 # elsif prtbl_rd = '1' then
1103 with m.If(prtbl_rd):
1104 # addr := prtable_addr;
1105 # tlb_data := (others => '0');
1106 comb += addr.eq(prtable_addr)
1107 # else
1108 with m.Else():
1109 # addr := pgtable_addr;
1110 # tlb_data := (others => '0');
1111 comb += addr.eq(pgtable_addr)
1112 # end if;
1113
1114 # l_out.done <= r.done;
1115 # l_out.err <= r.err;
1116 # l_out.invalid <= r.invalid;
1117 # l_out.badtree <= r.badtree;
1118 # l_out.segerr <= r.segerror;
1119 # l_out.perm_error <= r.perm_err;
1120 # l_out.rc_error <= r.rc_error;
1121 comb += l_out.done.eq(r.done)
1122 comb += l_out.err.eq(r.err)
1123 comb += l_out.invalid.eq(r.invalid)
1124 comb += l_out.badtree.eq(r.badtree)
1125 comb += l_out.segerr.eq(r.segerror)
1126 comb += l_out.perm_error.eq(r.perm_err)
1127 comb += l_out.rc_error.eq(r.rc_error)
1128
1129 # d_out.valid <= dcreq;
1130 # d_out.tlbie <= tlbie_req;
1131 # d_out.doall <= r.inval_all;
1132 # d_out.tlbld <= tlb_load;
1133 # d_out.addr <= addr;
1134 # d_out.pte <= tlb_data;
1135 comb += d_out.valid.eq(dcreq)
1136 comb += d_out.tlbie.eq(tlbie_req)
1137 comb += d_out.doall.eq(r.inval_all)
1138 comb += d_out.tlbld.eeq(tlb_load)
1139 comb += d_out.addr.eq(addr)
1140 comb += d_out.pte.eq(tlb_data)
1141
1142 # i_out.tlbld <= itlb_load;
1143 # i_out.tlbie <= tlbie_req;
1144 # i_out.doall <= r.inval_all;
1145 # i_out.addr <= addr;
1146 # i_out.pte <= tlb_data;
1147 comb += i_out.tlbld.eq(itlb_load)
1148 comb += i_out.tblie.eq(tlbie_req)
1149 comb += i_out.doall.eq(r.inval_all)
1150 comb += i_out.addr.eq(addr)
1151 comb += i_out.pte.eq(tlb_data)
1152
1153 # end process;
1154 # end;
1155
1156
1157 def mmu_sim():
1158 yield wp.waddr.eq(1)
1159 yield wp.data_i.eq(2)
1160 yield wp.wen.eq(1)
1161 yield
1162 yield wp.wen.eq(0)
1163 yield rp.ren.eq(1)
1164 yield rp.raddr.eq(1)
1165 yield Settle()
1166 data = yield rp.data_o
1167 print(data)
1168 assert data == 2
1169 yield
1170
1171 yield wp.waddr.eq(5)
1172 yield rp.raddr.eq(5)
1173 yield rp.ren.eq(1)
1174 yield wp.wen.eq(1)
1175 yield wp.data_i.eq(6)
1176 yield Settle()
1177 data = yield rp.data_o
1178 print(data)
1179 assert data == 6
1180 yield
1181 yield wp.wen.eq(0)
1182 yield rp.ren.eq(0)
1183 yield Settle()
1184 data = yield rp.data_o
1185 print(data)
1186 assert data == 0
1187 yield
1188 data = yield rp.data_o
1189 print(data)
1190
1191 def test_mmu():
1192 dut = MMU()
1193 rp = dut.read_port()
1194 wp = dut.write_port()
1195 vl = rtlil.convert(dut, ports=dut.ports())
1196 with open("test_mmu.il", "w") as f:
1197 f.write(vl)
1198
1199 run_simulation(dut, mmu_sim(), vcd_name='test_mmu.vcd')
1200
1201 if __name__ == '__main__':
1202 test_mmu()