Initial commit of translation of microwatt mmu.vhdl into nmigen
[soc.git] / src / soc / experiment / mmu.py
1 from enum import Enum, unique
2
3 from nmigen import (Module, Signal, Elaboratable, Mux, Cat, Repl,
4 signed, ResetSignal)
5 from nmigen.cli import main
6
7
8 # library ieee;
9 # use ieee.std_logic_1164.all;
10 # use ieee.numeric_std.all;
11
12 # library work;
13 # use work.common.all;
14
15 # -- Radix MMU
16 # -- Supports 4-level trees as in arch 3.0B, but not the two-step translation for
17 # -- guests under a hypervisor (i.e. there is no gRA -> hRA translation).
18
19 # type state_t is (IDLE,
20 # DO_TLBIE,
21 # TLB_WAIT,
22 # PROC_TBL_READ,
23 # PROC_TBL_WAIT,
24 # SEGMENT_CHECK,
25 # RADIX_LOOKUP,
26 # RADIX_READ_WAIT,
27 # RADIX_LOAD_TLB,
28 # RADIX_FINISH
29 # );
30
31 @unique
32 class State(Enum):
33 IDLE = 0
34 DO_TLBIE = 1
35 TLB_WAIT = 2
36 PROC_TBL_READ = 3
37 PROC_TBL_WAIT = 4
38 SEGMENT_CHECK = 5
39 RADIX_LOOKUP = 6
40 RADIX_READ_WAIT = 7
41 RADIX_LOAD_TLB = 8
42 RADIX_FINIS = 9
43
44 # type reg_stage_t is record
45 # -- latched request from loadstore1
46 # valid : std_ulogic;
47 # iside : std_ulogic;
48 # store : std_ulogic;
49 # priv : std_ulogic;
50 # addr : std_ulogic_vector(63 downto 0);
51 # inval_all : std_ulogic;
52 # -- config SPRs
53 # prtbl : std_ulogic_vector(63 downto 0);
54 # pid : std_ulogic_vector(31 downto 0);
55 # -- internal state
56 # state : state_t;
57 # done : std_ulogic;
58 # err : std_ulogic;
59 # pgtbl0 : std_ulogic_vector(63 downto 0);
60 # pt0_valid : std_ulogic;
61 # pgtbl3 : std_ulogic_vector(63 downto 0);
62 # pt3_valid : std_ulogic;
63 # shift : unsigned(5 downto 0);
64 # mask_size : unsigned(4 downto 0);
65 # pgbase : std_ulogic_vector(55 downto 0);
66 # pde : std_ulogic_vector(63 downto 0);
67 # invalid : std_ulogic;
68 # badtree : std_ulogic;
69 # segerror : std_ulogic;
70 # perm_err : std_ulogic;
71 # rc_error : std_ulogic;
72 # end record;
73
74
75 class RegStage():
76 def __init__(self):
77 # latched request from loadstore1
78 self.valid = Signal(0),
79 self.iside = Signal(0),
80 self.store = Signal(0),
81 self.priv = Signal(0),
82 self.addr = [
83 Signal(1, reset_less=True, name=f"reg_stage_addr{i}") for i in range(64)],
84 self.inval_all = Signal(0),
85 # config SPRs
86 self.prtbl = [
87 Signal(1, reset_less=True, name=f"reg_stage_addr{i}") for i in range(64)],
88 self.pid = [
89 Signal(1, reset_less=True, name=f"reg_stage_addr{i}") for i in range(32)],
90 # internal state
91 self.state = State.IDLE,
92 self.done = Signal(0),
93 self.err = Signal(0),
94 self.pgtbl0 = [
95 Signal(1, reset_less=True, name=f"reg_stage_addr{i}") for i in range(64)],
96 self.pt0_valid = Signal(0),
97 self.pgtbl3 = [
98 Signal(1, reset_less=True, name=f"reg_stage_addr{i}") for i in range(64)],
99 self.pt3_valid = Signal(0),
100 self.shift = Signal(5),
101 self.mask_size = Signal(4),
102 self.pgbase = [
103 Signal(1, reset_less=True, name=f"reg_stage_addr{i}") for i in range(56)],
104 self.pde = [
105 Signal(1, reset_less=True, name=f"reg_stage_addr{i}") for i in range(64)],
106 self.invalid = Signal(0),
107 self.badtree = Signal(0),
108 self.segerror = Signal(0),
109 self.perm_err = Signal(0),
110 self.rc_error = Signal(0),
111
112
113 # architecture behave of mmu is
114 class MMU(Elaboratable):
115
116 # entity mmu is
117 # port (
118 # clk : in std_ulogic;
119 # rst : in std_ulogic;
120
121 # l_in : in Loadstore1ToMmuType;
122 # l_out : out MmuToLoadstore1Type;
123
124 # d_out : out MmuToDcacheType;
125 # d_in : in DcacheToMmuType;
126
127 # i_out : out MmuToIcacheType
128 # );
129 # end mmu;
130 def __init__(self, l_in, l_out, d_out, d_in, i_out):
131 self.l_in = l_in
132 self.l_out = l_out
133 self.d_out = d_out
134 self.d_in = d_in
135 self.i_out = i_out
136
137 # begin
138 def elaborate(self, platform):
139 # -- Multiplex internal SPR values back to loadstore1, selected
140 # -- by l_in.sprn.
141 # l_out.sprval <= r.prtbl when l_in.sprn(9) = '1' else x"00000000" & r.pid;
142
143 # Multiplex internal SPR values back to loadstore1, selected by l_in.sprn.
144 m = Module()
145
146 comb = m.d.comb
147 sync = m.d.sync
148
149 rst = ResetSignal()
150 l_in = self.l_in
151 l_out = self.l_out
152 d_out = self.d_out
153 d_in = self.d_in
154 i_out = self.i_out
155
156 # non-existant variable, to be removed when I understand how to do VHDL rising_edge(clk) in nmigen
157 rising_edge = False
158
159 # signal r, rin : reg_stage_t;
160 r = RegStage()
161 rin = RegStage()
162
163 # signal addrsh : std_ulogic_vector(15 downto 0);
164 # signal mask : std_ulogic_vector(15 downto 0);
165 # signal finalmask : std_ulogic_vector(43 downto 0);
166
167 addrsh = [
168 Signal(1, reset_less=True, name=f"reg_stage_addr{i}") for i in range(16)]
169 mask = [Signal(1, reset_less=True,
170 name=f"reg_stage_addr{i}") for i in range(15)]
171 finalmask = [
172 Signal(1, reset_less=True, name=f"reg_stage_addr{i}") for i in range(44)]
173
174 with m.If(l_in.sprn[9] == 1):
175 m.d.comb += l_out.sprval.eq(r.prtbl)
176
177 with m.Else():
178 m.d.comb += l_out.sprval.eq(0x00000000 & r)
179
180 # mmu_0: process(clk)
181 # begin
182 # if rising_edge(clk) then
183 # if rst = '1' then
184 # r.state <= IDLE;
185 # r.valid <= '0';
186 # r.pt0_valid <= '0';
187 # r.pt3_valid <= '0';
188 # r.prtbl <= (others => '0');
189 with m.If(rising_edge):
190 with m.If(rst == 1):
191 r.state = State.IDLE
192 r.valid = 0
193 r.pt0_valid = 0
194 r.pt3_valid = 0
195 # value should be vhdl (others => '0') in nmigen
196 r.prtbl = 0
197 # else
198 with m.Else():
199 # if rin.valid = '1' then
200 # report "MMU got tlb miss for " & to_hstring(rin.addr);
201 # end if;
202 with m.If(rin.valid == 1):
203 print(f"MMU got tlb miss for {rin.addr}")
204
205 # if l_out.done = '1' then
206 # report "MMU completing op without error";
207 # end if;
208 with m.If(l_out.done == 1):
209 print("MMU completing op without error")
210
211 # if l_out.err = '1' then
212 # report "MMU completing op with err invalid=" & std_ulogic'image(l_out.invalid) &
213 # " badtree=" & std_ulogic'image(l_out.badtree);
214 # end if;
215 with m.If(l_out.err == 1):
216 print(
217 f"MMU completing op with err invalid={l_out.invalid} badtree={l_out.badtree}")
218 # if rin.state = RADIX_LOOKUP then
219 # report "radix lookup shift=" & integer'image(to_integer(rin.shift)) &
220 # " msize=" & integer'image(to_integer(rin.mask_size));
221 # end if;
222 with m.If(rin.state == State.RADIX_LOOKUP):
223 print(
224 f"radix lookup shift={rin.shift} msize={rin.mask_size}")
225 # if r.state = RADIX_LOOKUP then
226 # report "send load addr=" & to_hstring(d_out.addr) &
227 # " addrsh=" & to_hstring(addrsh) & " mask=" & to_hstring(mask);
228 # end if;
229 with m.If(r.state == State.RADIX_LOOKUP):
230 print(
231 f"send load addr={d_out.addr} addrsh={addrsh} mask={mask}")
232 # r <= rin;
233 comb += r.eq(rin)
234 # end if;
235 # end if;
236 # end process;
237
238 # -- Shift address bits 61--12 right by 0--47 bits and
239 # -- supply the least significant 16 bits of the result.
240 # addrshifter: process(all)
241 # variable sh1 : std_ulogic_vector(30 downto 0);
242 # variable sh2 : std_ulogic_vector(18 downto 0);
243 # variable result : std_ulogic_vector(15 downto 0);
244 # begin
245 # case r.shift(5 downto 4) is
246 # when "00" =>
247 # sh1 := r.addr(42 downto 12);
248 # when "01" =>
249 # sh1 := r.addr(58 downto 28);
250 # when others =>
251 # sh1 := "0000000000000" & r.addr(61 downto 44);
252 # end case;
253 # case r.shift(3 downto 2) is
254 # when "00" =>
255 # sh2 := sh1(18 downto 0);
256 # when "01" =>
257 # sh2 := sh1(22 downto 4);
258 # when "10" =>
259 # sh2 := sh1(26 downto 8);
260 # when others =>
261 # sh2 := sh1(30 downto 12);
262 # end case;
263 # case r.shift(1 downto 0) is
264 # when "00" =>
265 # result := sh2(15 downto 0);
266 # when "01" =>
267 # result := sh2(16 downto 1);
268 # when "10" =>
269 # result := sh2(17 downto 2);
270 # when others =>
271 # result := sh2(18 downto 3);
272 # end case;
273 # addrsh <= result;
274 # end process;
275
276 # -- generate mask for extracting address fields for PTE address generation
277 # addrmaskgen: process(all)
278 # variable m : std_ulogic_vector(15 downto 0);
279 # begin
280 # -- mask_count has to be >= 5
281 # m := x"001f";
282 # for i in 5 to 15 loop
283 # if i < to_integer(r.mask_size) then
284 # m(i) := '1';
285 # end if;
286 # end loop;
287 # mask <= m;
288 # end process;
289
290 # -- generate mask for extracting address bits to go in TLB entry
291 # -- in order to support pages > 4kB
292 # finalmaskgen: process(all)
293 # variable m : std_ulogic_vector(43 downto 0);
294 # begin
295 # m := (others => '0');
296 # for i in 0 to 43 loop
297 # if i < to_integer(r.shift) then
298 # m(i) := '1';
299 # end if;
300 # end loop;
301 # finalmask <= m;
302 # end process;
303
304 # mmu_1: process(all)
305 # variable v : reg_stage_t;
306 # variable dcreq : std_ulogic;
307 # variable tlb_load : std_ulogic;
308 # variable itlb_load : std_ulogic;
309 # variable tlbie_req : std_ulogic;
310 # variable prtbl_rd : std_ulogic;
311 # variable pt_valid : std_ulogic;
312 # variable effpid : std_ulogic_vector(31 downto 0);
313 # variable prtable_addr : std_ulogic_vector(63 downto 0);
314 # variable rts : unsigned(5 downto 0);
315 # variable mbits : unsigned(5 downto 0);
316 # variable pgtable_addr : std_ulogic_vector(63 downto 0);
317 # variable pte : std_ulogic_vector(63 downto 0);
318 # variable tlb_data : std_ulogic_vector(63 downto 0);
319 # variable nonzero : std_ulogic;
320 # variable pgtbl : std_ulogic_vector(63 downto 0);
321 # variable perm_ok : std_ulogic;
322 # variable rc_ok : std_ulogic;
323 # variable addr : std_ulogic_vector(63 downto 0);
324 # variable data : std_ulogic_vector(63 downto 0);
325 # begin
326 # v := r;
327 # v.valid := '0';
328 # dcreq := '0';
329 # v.done := '0';
330 # v.err := '0';
331 # v.invalid := '0';
332 # v.badtree := '0';
333 # v.segerror := '0';
334 # v.perm_err := '0';
335 # v.rc_error := '0';
336 # tlb_load := '0';
337 # itlb_load := '0';
338 # tlbie_req := '0';
339 # v.inval_all := '0';
340 # prtbl_rd := '0';
341
342 # -- Radix tree data structures in memory are big-endian,
343 # -- so we need to byte-swap them
344 # for i in 0 to 7 loop
345 # data(i * 8 + 7 downto i * 8) := d_in.data((7 - i) * 8 + 7 downto (7 - i) * 8);
346 # end loop;
347
348 # case r.state is
349 # when IDLE =>
350 # if l_in.addr(63) = '0' then
351 # pgtbl := r.pgtbl0;
352 # pt_valid := r.pt0_valid;
353 # else
354 # pgtbl := r.pgtbl3;
355 # pt_valid := r.pt3_valid;
356 # end if;
357 # -- rts == radix tree size, # address bits being translated
358 # rts := unsigned('0' & pgtbl(62 downto 61) & pgtbl(7 downto 5));
359 # -- mbits == # address bits to index top level of tree
360 # mbits := unsigned('0' & pgtbl(4 downto 0));
361 # -- set v.shift to rts so that we can use finalmask for the segment check
362 # v.shift := rts;
363 # v.mask_size := mbits(4 downto 0);
364 # v.pgbase := pgtbl(55 downto 8) & x"00";
365
366 # if l_in.valid = '1' then
367 # v.addr := l_in.addr;
368 # v.iside := l_in.iside;
369 # v.store := not (l_in.load or l_in.iside);
370 # v.priv := l_in.priv;
371 # if l_in.tlbie = '1' then
372 # -- Invalidate all iTLB/dTLB entries for tlbie with
373 # -- RB[IS] != 0 or RB[AP] != 0, or for slbia
374 # v.inval_all := l_in.slbia or l_in.addr(11) or l_in.addr(10) or
375 # l_in.addr(7) or l_in.addr(6) or l_in.addr(5);
376 # -- The RIC field of the tlbie instruction comes across on the
377 # -- sprn bus as bits 2--3. RIC=2 flushes process table caches.
378 # if l_in.sprn(3) = '1' then
379 # v.pt0_valid := '0';
380 # v.pt3_valid := '0';
381 # end if;
382 # v.state := DO_TLBIE;
383 # else
384 # v.valid := '1';
385 # if pt_valid = '0' then
386 # -- need to fetch process table entry
387 # -- set v.shift so we can use finalmask for generating
388 # -- the process table entry address
389 # v.shift := unsigned('0' & r.prtbl(4 downto 0));
390 # v.state := PROC_TBL_READ;
391 # elsif mbits = 0 then
392 # -- Use RPDS = 0 to disable radix tree walks
393 # v.state := RADIX_FINISH;
394 # v.invalid := '1';
395 # else
396 # v.state := SEGMENT_CHECK;
397 # end if;
398 # end if;
399 # end if;
400 # if l_in.mtspr = '1' then
401 # -- Move to PID needs to invalidate L1 TLBs and cached
402 # -- pgtbl0 value. Move to PRTBL does that plus
403 # -- invalidating the cached pgtbl3 value as well.
404 # if l_in.sprn(9) = '0' then
405 # v.pid := l_in.rs(31 downto 0);
406 # else
407 # v.prtbl := l_in.rs;
408 # v.pt3_valid := '0';
409 # end if;
410 # v.pt0_valid := '0';
411 # v.inval_all := '1';
412 # v.state := DO_TLBIE;
413 # end if;
414
415 # when DO_TLBIE =>
416 # dcreq := '1';
417 # tlbie_req := '1';
418 # v.state := TLB_WAIT;
419
420 # when TLB_WAIT =>
421 # if d_in.done = '1' then
422 # v.state := RADIX_FINISH;
423 # end if;
424
425 # when PROC_TBL_READ =>
426 # dcreq := '1';
427 # prtbl_rd := '1';
428 # v.state := PROC_TBL_WAIT;
429
430 # when PROC_TBL_WAIT =>
431 # if d_in.done = '1' then
432 # if r.addr(63) = '1' then
433 # v.pgtbl3 := data;
434 # v.pt3_valid := '1';
435 # else
436 # v.pgtbl0 := data;
437 # v.pt0_valid := '1';
438 # end if;
439 # -- rts == radix tree size, # address bits being translated
440 # rts := unsigned('0' & data(62 downto 61) & data(7 downto 5));
441 # -- mbits == # address bits to index top level of tree
442 # mbits := unsigned('0' & data(4 downto 0));
443 # -- set v.shift to rts so that we can use finalmask for the segment check
444 # v.shift := rts;
445 # v.mask_size := mbits(4 downto 0);
446 # v.pgbase := data(55 downto 8) & x"00";
447 # if mbits = 0 then
448 # v.state := RADIX_FINISH;
449 # v.invalid := '1';
450 # else
451 # v.state := SEGMENT_CHECK;
452 # end if;
453 # end if;
454 # if d_in.err = '1' then
455 # v.state := RADIX_FINISH;
456 # v.badtree := '1';
457 # end if;
458
459 # when SEGMENT_CHECK =>
460 # mbits := '0' & r.mask_size;
461 # v.shift := r.shift + (31 - 12) - mbits;
462 # nonzero := or(r.addr(61 downto 31) and not finalmask(30 downto 0));
463 # if r.addr(63) /= r.addr(62) or nonzero = '1' then
464 # v.state := RADIX_FINISH;
465 # v.segerror := '1';
466 # elsif mbits < 5 or mbits > 16 or mbits > (r.shift + (31 - 12)) then
467 # v.state := RADIX_FINISH;
468 # v.badtree := '1';
469 # else
470 # v.state := RADIX_LOOKUP;
471 # end if;
472
473 # when RADIX_LOOKUP =>
474 # dcreq := '1';
475 # v.state := RADIX_READ_WAIT;
476
477 # when RADIX_READ_WAIT =>
478 # if d_in.done = '1' then
479 # v.pde := data;
480 # -- test valid bit
481 # if data(63) = '1' then
482 # -- test leaf bit
483 # if data(62) = '1' then
484 # -- check permissions and RC bits
485 # perm_ok := '0';
486 # if r.priv = '1' or data(3) = '0' then
487 # if r.iside = '0' then
488 # perm_ok := data(1) or (data(2) and not r.store);
489 # else
490 # -- no IAMR, so no KUEP support for now
491 # -- deny execute permission if cache inhibited
492 # perm_ok := data(0) and not data(5);
493 # end if;
494 # end if;
495 # rc_ok := data(8) and (data(7) or not r.store);
496 # if perm_ok = '1' and rc_ok = '1' then
497 # v.state := RADIX_LOAD_TLB;
498 # else
499 # v.state := RADIX_FINISH;
500 # v.perm_err := not perm_ok;
501 # -- permission error takes precedence over RC error
502 # v.rc_error := perm_ok;
503 # end if;
504 # else
505 # mbits := unsigned('0' & data(4 downto 0));
506 # if mbits < 5 or mbits > 16 or mbits > r.shift then
507 # v.state := RADIX_FINISH;
508 # v.badtree := '1';
509 # else
510 # v.shift := v.shift - mbits;
511 # v.mask_size := mbits(4 downto 0);
512 # v.pgbase := data(55 downto 8) & x"00";
513 # v.state := RADIX_LOOKUP;
514 # end if;
515 # end if;
516 # else
517 # -- non-present PTE, generate a DSI
518 # v.state := RADIX_FINISH;
519 # v.invalid := '1';
520 # end if;
521 # end if;
522 # if d_in.err = '1' then
523 # v.state := RADIX_FINISH;
524 # v.badtree := '1';
525 # end if;
526
527 # when RADIX_LOAD_TLB =>
528 # tlb_load := '1';
529 # if r.iside = '0' then
530 # dcreq := '1';
531 # v.state := TLB_WAIT;
532 # else
533 # itlb_load := '1';
534 # v.state := IDLE;
535 # end if;
536
537 # when RADIX_FINISH =>
538 # v.state := IDLE;
539
540 # end case;
541
542 # if v.state = RADIX_FINISH or (v.state = RADIX_LOAD_TLB and r.iside = '1') then
543 # v.err := v.invalid or v.badtree or v.segerror or v.perm_err or v.rc_error;
544 # v.done := not v.err;
545 # end if;
546
547 # if r.addr(63) = '1' then
548 # effpid := x"00000000";
549 # else
550 # effpid := r.pid;
551 # end if;
552 # prtable_addr := x"00" & r.prtbl(55 downto 36) &
553 # ((r.prtbl(35 downto 12) and not finalmask(23 downto 0)) or
554 # (effpid(31 downto 8) and finalmask(23 downto 0))) &
555 # effpid(7 downto 0) & "0000";
556
557 # pgtable_addr := x"00" & r.pgbase(55 downto 19) &
558 # ((r.pgbase(18 downto 3) and not mask) or (addrsh and mask)) &
559 # "000";
560 # pte := x"00" &
561 # ((r.pde(55 downto 12) and not finalmask) or (r.addr(55 downto 12) and finalmask))
562 # & r.pde(11 downto 0);
563
564 # -- update registers
565 # rin <= v;
566
567 # -- drive outputs
568 # if tlbie_req = '1' then
569 # addr := r.addr;
570 # tlb_data := (others => '0');
571 # elsif tlb_load = '1' then
572 # addr := r.addr(63 downto 12) & x"000";
573 # tlb_data := pte;
574 # elsif prtbl_rd = '1' then
575 # addr := prtable_addr;
576 # tlb_data := (others => '0');
577 # else
578 # addr := pgtable_addr;
579 # tlb_data := (others => '0');
580 # end if;
581
582 # l_out.done <= r.done;
583 # l_out.err <= r.err;
584 # l_out.invalid <= r.invalid;
585 # l_out.badtree <= r.badtree;
586 # l_out.segerr <= r.segerror;
587 # l_out.perm_error <= r.perm_err;
588 # l_out.rc_error <= r.rc_error;
589
590 # d_out.valid <= dcreq;
591 # d_out.tlbie <= tlbie_req;
592 # d_out.doall <= r.inval_all;
593 # d_out.tlbld <= tlb_load;
594 # d_out.addr <= addr;
595 # d_out.pte <= tlb_data;
596
597 # i_out.tlbld <= itlb_load;
598 # i_out.tlbie <= tlbie_req;
599 # i_out.doall <= r.inval_all;
600 # i_out.addr <= addr;
601 # i_out.pte <= tlb_data;
602
603 # end process;
604 # end;