Add Tercel PHY reset synchronization
[microwatt.git] / mmu.vhdl
index 0eefbabe0285b9ba77a6fb2a17af1165903c703a..623c2c9313077c2ffa8a44fbefef56ac4e3eae1b 100644 (file)
--- 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;