X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=mmu.vhdl;h=623c2c9313077c2ffa8a44fbefef56ac4e3eae1b;hb=5cc5d8f030d303a82ed38f3b359921a748b746ba;hp=0eefbabe0285b9ba77a6fb2a17af1165903c703a;hpb=2843c99a71ad4b88d8d722bb7bae7d4979b6083c;p=microwatt.git diff --git a/mmu.vhdl b/mmu.vhdl index 0eefbab..623c2c9 100644 --- a/mmu.vhdl +++ b/mmu.vhdl @@ -27,6 +27,7 @@ end mmu; architecture behave of mmu is type state_t is (IDLE, + DO_TLBIE, TLB_WAIT, PROC_TBL_READ, PROC_TBL_WAIT, @@ -34,7 +35,7 @@ architecture behave of mmu is RADIX_LOOKUP, RADIX_READ_WAIT, RADIX_LOAD_TLB, - RADIX_ERROR + RADIX_FINISH ); type reg_stage_t is record @@ -44,11 +45,14 @@ architecture behave of mmu is store : std_ulogic; priv : std_ulogic; addr : std_ulogic_vector(63 downto 0); + inval_all : std_ulogic; -- config SPRs prtbl : std_ulogic_vector(63 downto 0); pid : std_ulogic_vector(31 downto 0); -- internal state state : state_t; + done : std_ulogic; + err : std_ulogic; pgtbl0 : std_ulogic_vector(63 downto 0); pt0_valid : std_ulogic; pgtbl3 : std_ulogic_vector(63 downto 0); @@ -84,12 +88,16 @@ begin r.pt0_valid <= '0'; r.pt3_valid <= '0'; r.prtbl <= (others => '0'); + r.pid <= (others => '0'); else if rin.valid = '1' then report "MMU got tlb miss for " & to_hstring(rin.addr); end if; if l_out.done = '1' then - report "MMU completing op with invalid=" & std_ulogic'image(l_out.invalid) & + report "MMU completing op without error"; + end if; + if l_out.err = '1' then + report "MMU completing op with err invalid=" & std_ulogic'image(l_out.invalid) & " badtree=" & std_ulogic'image(l_out.badtree); end if; if rin.state = RADIX_LOOKUP then @@ -174,11 +182,9 @@ begin mmu_1: process(all) variable v : reg_stage_t; variable dcreq : std_ulogic; - variable done : std_ulogic; variable tlb_load : std_ulogic; variable itlb_load : std_ulogic; variable tlbie_req : std_ulogic; - variable inval_all : std_ulogic; variable prtbl_rd : std_ulogic; variable pt_valid : std_ulogic; variable effpid : std_ulogic_vector(31 downto 0); @@ -198,7 +204,8 @@ begin v := r; v.valid := '0'; dcreq := '0'; - done := '0'; + v.done := '0'; + v.err := '0'; v.invalid := '0'; v.badtree := '0'; v.segerror := '0'; @@ -207,7 +214,7 @@ begin tlb_load := '0'; itlb_load := '0'; tlbie_req := '0'; - inval_all := '0'; + v.inval_all := '0'; prtbl_rd := '0'; -- Radix tree data structures in memory are big-endian, @@ -240,19 +247,17 @@ begin v.store := not (l_in.load or l_in.iside); v.priv := l_in.priv; if l_in.tlbie = '1' then - dcreq := '1'; - tlbie_req := '1'; -- Invalidate all iTLB/dTLB entries for tlbie with -- RB[IS] != 0 or RB[AP] != 0, or for slbia - inval_all := l_in.slbia or l_in.addr(11) or l_in.addr(10) or - l_in.addr(7) or l_in.addr(6) or l_in.addr(5); + v.inval_all := l_in.slbia or l_in.addr(11) or l_in.addr(10) or + l_in.addr(7) or l_in.addr(6) or l_in.addr(5); -- The RIC field of the tlbie instruction comes across on the -- sprn bus as bits 2--3. RIC=2 flushes process table caches. if l_in.sprn(3) = '1' then v.pt0_valid := '0'; v.pt3_valid := '0'; end if; - v.state := TLB_WAIT; + v.state := DO_TLBIE; else v.valid := '1'; if pt_valid = '0' then @@ -263,7 +268,7 @@ begin v.state := PROC_TBL_READ; elsif mbits = 0 then -- Use RPDS = 0 to disable radix tree walks - v.state := RADIX_ERROR; + v.state := RADIX_FINISH; v.invalid := '1'; else v.state := SEGMENT_CHECK; @@ -281,16 +286,18 @@ begin v.pt3_valid := '0'; end if; v.pt0_valid := '0'; - dcreq := '1'; - tlbie_req := '1'; - inval_all := '1'; - v.state := TLB_WAIT; + v.inval_all := '1'; + v.state := DO_TLBIE; end if; + when DO_TLBIE => + dcreq := '1'; + tlbie_req := '1'; + v.state := TLB_WAIT; + when TLB_WAIT => if d_in.done = '1' then - done := '1'; - v.state := IDLE; + v.state := RADIX_FINISH; end if; when PROC_TBL_READ => @@ -300,43 +307,42 @@ begin when PROC_TBL_WAIT => if d_in.done = '1' then - if d_in.err = '0' then - if r.addr(63) = '1' then - v.pgtbl3 := data; - v.pt3_valid := '1'; - else - v.pgtbl0 := data; - v.pt0_valid := '1'; - end if; - -- rts == radix tree size, # address bits being translated - rts := unsigned('0' & data(62 downto 61) & data(7 downto 5)); - -- mbits == # address bits to index top level of tree - mbits := unsigned('0' & data(4 downto 0)); - -- set v.shift to rts so that we can use finalmask for the segment check - v.shift := rts; - v.mask_size := mbits(4 downto 0); - v.pgbase := data(55 downto 8) & x"00"; - if mbits = 0 then - v.state := RADIX_ERROR; - v.invalid := '1'; - else - v.state := SEGMENT_CHECK; - end if; + if r.addr(63) = '1' then + v.pgtbl3 := data; + v.pt3_valid := '1'; + else + v.pgtbl0 := data; + v.pt0_valid := '1'; + end if; + -- rts == radix tree size, # address bits being translated + rts := unsigned('0' & data(62 downto 61) & data(7 downto 5)); + -- mbits == # address bits to index top level of tree + mbits := unsigned('0' & data(4 downto 0)); + -- set v.shift to rts so that we can use finalmask for the segment check + v.shift := rts; + v.mask_size := mbits(4 downto 0); + v.pgbase := data(55 downto 8) & x"00"; + if mbits = 0 then + v.state := RADIX_FINISH; + v.invalid := '1'; else - v.state := RADIX_ERROR; - v.badtree := '1'; + v.state := SEGMENT_CHECK; end if; end if; + if d_in.err = '1' then + v.state := RADIX_FINISH; + v.badtree := '1'; + end if; when SEGMENT_CHECK => mbits := '0' & r.mask_size; v.shift := r.shift + (31 - 12) - mbits; nonzero := or(r.addr(61 downto 31) and not finalmask(30 downto 0)); if r.addr(63) /= r.addr(62) or nonzero = '1' then - v.state := RADIX_ERROR; + v.state := RADIX_FINISH; v.segerror := '1'; elsif mbits < 5 or mbits > 16 or mbits > (r.shift + (31 - 12)) then - v.state := RADIX_ERROR; + v.state := RADIX_FINISH; v.badtree := '1'; else v.state := RADIX_LOOKUP; @@ -348,54 +354,53 @@ begin when RADIX_READ_WAIT => if d_in.done = '1' then - if d_in.err = '0' then - v.pde := data; - -- test valid bit - if data(63) = '1' then - -- test leaf bit - if data(62) = '1' then - -- check permissions and RC bits - perm_ok := '0'; - if r.priv = '1' or data(3) = '0' then - if r.iside = '0' then - perm_ok := data(1) or (data(2) and not r.store); - else - -- no IAMR, so no KUEP support for now - -- deny execute permission if cache inhibited - perm_ok := data(0) and not data(5); - end if; - end if; - rc_ok := data(8) and (data(7) or not r.store); - if perm_ok = '1' and rc_ok = '1' then - v.state := RADIX_LOAD_TLB; + v.pde := data; + -- test valid bit + if data(63) = '1' then + -- test leaf bit + if data(62) = '1' then + -- check permissions and RC bits + perm_ok := '0'; + if r.priv = '1' or data(3) = '0' then + if r.iside = '0' then + perm_ok := data(1) or (data(2) and not r.store); else - v.state := RADIX_ERROR; - v.perm_err := not perm_ok; - -- permission error takes precedence over RC error - v.rc_error := perm_ok; + -- no IAMR, so no KUEP support for now + -- deny execute permission if cache inhibited + perm_ok := data(0) and not data(5); end if; + end if; + rc_ok := data(8) and (data(7) or not r.store); + if perm_ok = '1' and rc_ok = '1' then + v.state := RADIX_LOAD_TLB; else - mbits := unsigned('0' & data(4 downto 0)); - if mbits < 5 or mbits > 16 or mbits > r.shift then - v.state := RADIX_ERROR; - v.badtree := '1'; - else - v.shift := v.shift - mbits; - v.mask_size := mbits(4 downto 0); - v.pgbase := data(55 downto 8) & x"00"; - v.state := RADIX_LOOKUP; - end if; + v.state := RADIX_FINISH; + v.perm_err := not perm_ok; + -- permission error takes precedence over RC error + v.rc_error := perm_ok; end if; else - -- non-present PTE, generate a DSI - v.state := RADIX_ERROR; - v.invalid := '1'; + mbits := unsigned('0' & data(4 downto 0)); + if mbits < 5 or mbits > 16 or mbits > r.shift then + v.state := RADIX_FINISH; + v.badtree := '1'; + else + v.shift := v.shift - mbits; + v.mask_size := mbits(4 downto 0); + v.pgbase := data(55 downto 8) & x"00"; + v.state := RADIX_LOOKUP; + end if; end if; else - v.state := RADIX_ERROR; - v.badtree := '1'; + -- non-present PTE, generate a DSI + v.state := RADIX_FINISH; + v.invalid := '1'; end if; end if; + if d_in.err = '1' then + v.state := RADIX_FINISH; + v.badtree := '1'; + end if; when RADIX_LOAD_TLB => tlb_load := '1'; @@ -404,16 +409,19 @@ begin v.state := TLB_WAIT; else itlb_load := '1'; - done := '1'; v.state := IDLE; end if; - when RADIX_ERROR => - done := '1'; + when RADIX_FINISH => v.state := IDLE; end case; + if v.state = RADIX_FINISH or (v.state = RADIX_LOAD_TLB and r.iside = '1') then + v.err := v.invalid or v.badtree or v.segerror or v.perm_err or v.rc_error; + v.done := not v.err; + end if; + if r.addr(63) = '1' then effpid := x"00000000"; else @@ -436,8 +444,8 @@ begin -- drive outputs if tlbie_req = '1' then - addr := l_in.addr; - tlb_data := l_in.rs; + addr := r.addr; + tlb_data := (others => '0'); elsif tlb_load = '1' then addr := r.addr(63 downto 12) & x"000"; tlb_data := pte; @@ -449,7 +457,8 @@ begin tlb_data := (others => '0'); end if; - l_out.done <= done; + l_out.done <= r.done; + l_out.err <= r.err; l_out.invalid <= r.invalid; l_out.badtree <= r.badtree; l_out.segerr <= r.segerror; @@ -458,14 +467,14 @@ begin d_out.valid <= dcreq; d_out.tlbie <= tlbie_req; - d_out.doall <= inval_all; + d_out.doall <= r.inval_all; d_out.tlbld <= tlb_load; d_out.addr <= addr; d_out.pte <= tlb_data; i_out.tlbld <= itlb_load; i_out.tlbie <= tlbie_req; - i_out.doall <= inval_all; + i_out.doall <= r.inval_all; i_out.addr <= addr; i_out.pte <= tlb_data;