Implement data storage interrupts
authorPaul Mackerras <paulus@ozlabs.org>
Tue, 7 Apr 2020 06:17:37 +0000 (16:17 +1000)
committerPaul Mackerras <paulus@ozlabs.org>
Fri, 8 May 2020 02:12:01 +0000 (12:12 +1000)
This adds a path from loadstore1 back to execute1 for reporting
errors, and machinery in execute1 for generating data storage
interrupts at vector 0x300.

If dcache is given two requests in successive cycles and the
first encounters an error (e.g. a TLB miss), it will now cancel
the second request.

Loadstore1 now responds to errors reported by dcache by sending
an exception signal to execute1 and returning to the idle state.
Execute1 then writes SRR0 and SRR1 and jumps to the 0x300 Data
Storage Interrupt vector.  DAR and DSISR are held in loadstore1.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
common.vhdl
core.vhdl
dcache.vhdl
execute1.vhdl
loadstore1.vhdl

index 6741044f35f65e80b74dabe6d847b2197e601c19..59b374421d76df1ce1bbb680e5ad0e2b93ec6b63 100644 (file)
@@ -238,6 +238,10 @@ package common is
                                                                      reserve => '0', rc => '0', virt_mode => '0',
                                                                      spr_num => 0, others => (others => '0'));
 
+    type Loadstore1ToExecute1Type is record
+        exception : std_ulogic;
+    end record;
+
     type Loadstore1ToDcacheType is record
        valid : std_ulogic;
        load : std_ulogic;                              -- is this a load
index acb37cc83bf0786d9741da82263e711c4cb927fc..0cb2ecdff5a6a2809f0a7ff1e8121a78cdba02d9 100644 (file)
--- a/core.vhdl
+++ b/core.vhdl
@@ -63,6 +63,7 @@ architecture behave of core is
 
     -- load store signals
     signal execute1_to_loadstore1: Execute1ToLoadstore1Type;
+    signal loadstore1_to_execute1: Loadstore1ToExecute1Type;
     signal loadstore1_to_writeback: Loadstore1ToWritebackType;
 
     -- dcache signals
@@ -251,6 +252,7 @@ begin
            stall_out => ex1_stall_out,
             e_in => decode2_to_execute1,
             i_in => xics_in,
+            l_in => loadstore1_to_execute1,
             l_out => execute1_to_loadstore1,
             f_out => execute1_to_fetch1,
             e_out => execute1_to_writeback,
@@ -264,6 +266,7 @@ begin
             clk => clk,
             rst => core_rst,
             l_in => execute1_to_loadstore1,
+            e_out => loadstore1_to_execute1,
             l_out => loadstore1_to_writeback,
             d_out => loadstore1_to_dcache,
             d_in => dcache_to_loadstore1,
index 3464c0dd0c98fd5c5a19531579436bc82d9e84c6..7895877893b134cb9e1bfe44088b4714beffcf2a 100644 (file)
@@ -147,6 +147,7 @@ architecture rtl of dcache is
     attribute ram_style of dtlb_ptes : signal is "distributed";
 
     signal r0 : Loadstore1ToDcacheType;
+    signal r0_valid : std_ulogic;
 
     -- Type of operation on a "valid" input
     type op_t is (OP_NONE,
@@ -406,6 +407,10 @@ begin
         end if;
     end process;
 
+    -- Hold off the request in r0 when stalling,
+    -- and cancel it if we get an error in a previous request.
+    r0_valid <= r0.valid and not stall_out and not r1.error_done;
+
     -- TLB
     -- Operates in the second cycle on the request latched in r0.
     -- TLB updates write the entry at the end of the second cycle.
@@ -478,7 +483,7 @@ begin
                 hit := '1';
             end if;
         end loop;
-        tlb_hit <= hit and r0.valid;
+        tlb_hit <= hit and r0_valid;
         tlb_hit_way <= hitway;
         pte <= read_tlb_pte(hitway, tlb_pte_way);
         valid_ra <= tlb_hit or not r0.virt_mode;
@@ -503,7 +508,7 @@ begin
             tlbie := '0';
             tlbia := '0';
             tlbwe := '0';
-            if r0.valid = '1' and stall_out = '0' and r0.tlbie = '1' then
+            if r0_valid = '1' and r0.tlbie = '1' then
                 if r0.addr(11 downto 10) /= "00" then
                     tlbia := '1';
                 elsif r0.addr(9) = '1' then
@@ -596,7 +601,7 @@ begin
         req_tag <= get_tag(ra);
 
         -- Only do anything if not being stalled by stage 1
-        go := r0.valid and not stall_out and not r0.tlbie;
+        go := r0_valid and not r0.tlbie;
 
         -- Calculate address of beginning of cache line, will be
         -- used for cache miss processing if needed
@@ -697,7 +702,7 @@ begin
         cancel_store <= '0';
         set_rsrv <= '0';
         clear_rsrv <= '0';
-        if stall_out = '0' and r0.valid = '1' and r0.reserve = '1' then
+        if r0_valid = '1' and r0.reserve = '1' then
             -- XXX generate alignment interrupt if address is not aligned
             -- XXX or if r0.nc = '1'
             if r0.load = '1' then
@@ -920,7 +925,7 @@ begin
             end if;
 
             -- complete tlbies in the third cycle
-            r1.tlbie_done <= r0.valid and r0.tlbie and not stall_out;
+            r1.tlbie_done <= r0_valid and r0.tlbie;
        end if;
     end process;
 
index 98b95dcca52f8687650047b91d0675a4d2816903..e2cb6510aef8ee81d941050ce935d164f993d473 100644 (file)
@@ -23,6 +23,7 @@ entity execute1 is
        stall_out : out std_ulogic;
 
        e_in  : in Decode2ToExecute1Type;
+        l_in  : in Loadstore1ToExecute1Type;
 
        i_in : in XicsToExecute1Type;
 
@@ -51,6 +52,7 @@ architecture behaviour of execute1 is
        slow_op_rc : std_ulogic;
        slow_op_oe : std_ulogic;
        slow_op_xerc : xer_common_t;
+        ldst_nia : std_ulogic_vector(63 downto 0);
     end record;
     constant reg_type_init : reg_type :=
         (e => Execute1ToWritebackInit, lr_update => '0',
@@ -446,9 +448,9 @@ begin
         v.e.exc_write_reg := fast_spr_num(SPR_SRR0);
         v.e.exc_write_data := e_in.nia;
 
-       if ctrl.irq_state = WRITE_SRR1 then
-           v.e.exc_write_reg := fast_spr_num(SPR_SRR1);
-           v.e.exc_write_data := ctrl.srr1;
+       if ctrl.irq_state = WRITE_SRR1 then
+           v.e.exc_write_reg := fast_spr_num(SPR_SRR1);
+           v.e.exc_write_data := ctrl.srr1;
             v.e.exc_write_enable := '1';
             ctrl_tmp.msr(MSR_SF) <= '1';
             ctrl_tmp.msr(MSR_EE) <= '0';
@@ -899,6 +901,7 @@ begin
 
         elsif e_in.valid = '1' then
             -- instruction for other units, i.e. LDST
+            v.ldst_nia := e_in.nia;
             v.e.valid := '0';
             if e_in.unit = LDST then
                 lv.valid := '1';
@@ -969,6 +972,17 @@ begin
        v.e.write_data := result;
        v.e.write_enable := result_en;
 
+        -- generate DSI for load/store exceptions
+        if l_in.exception = '1' then
+            ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#300#, 64));
+            ctrl_tmp.srr1 <= msr_copy(ctrl.msr);
+            v.e.exc_write_enable := '1';
+            v.e.exc_write_reg := fast_spr_num(SPR_SRR0);
+            v.e.exc_write_data := r.ldst_nia;
+            ctrl_tmp.irq_state <= WRITE_SRR1;
+            v.e.valid := '1';   -- complete the original load or store
+        end if;
+
         -- Outputs to loadstore1 (async)
         lv.op := e_in.insn_type;
         lv.addr1 := a_in;
index d5a59e8b5ffd2f827f14c44a16d36ca5beed9476..6ab18f5f693102245e36db3cbaba0eda981a8d34 100644 (file)
@@ -16,6 +16,7 @@ entity loadstore1 is
         rst   : in std_ulogic;
 
         l_in  : in Execute1ToLoadstore1Type;
+        e_out : out Loadstore1ToExecute1Type;
         l_out : out Loadstore1ToWritebackType;
 
         d_out : out Loadstore1ToDcacheType;
@@ -142,6 +143,9 @@ begin
         variable mfspr : std_ulogic;
         variable sprn : std_ulogic_vector(9 downto 0);
         variable sprval : std_ulogic_vector(63 downto 0);
+        variable exception : std_ulogic;
+        variable next_addr : std_ulogic_vector(63 downto 0);
+        variable dsisr : std_ulogic_vector(31 downto 0);
     begin
         v := r;
         req := '0';
@@ -151,6 +155,8 @@ begin
         addr := lsu_sum;
         mfspr := '0';
         sprval := (others => '0');      -- avoid inferred latches
+        exception := '0';
+        dsisr := (others => '0');
 
         write_enable := '0';
         do_update := '0';
@@ -204,6 +210,9 @@ begin
             end case;
         end loop;
 
+        -- compute (addr + 8) & ~7 for the second doubleword when unaligned
+        next_addr := std_ulogic_vector(unsigned(r.addr(63 downto 3)) + 1) & "000";
+
         case r.state is
         when IDLE =>
             if l_in.valid = '1' then
@@ -301,8 +310,7 @@ begin
             end if;
 
         when SECOND_REQ =>
-            -- compute (addr + 8) & ~7 for the second doubleword when unaligned
-            addr := std_ulogic_vector(unsigned(r.addr(63 downto 3)) + 1) & "000";
+            addr := next_addr;
             byte_sel := r.second_bytes;
             req := '1';
             stall := '1';
@@ -311,25 +319,43 @@ begin
         when FIRST_ACK_WAIT =>
             stall := '1';
             if d_in.valid = '1' then
-                v.state := LAST_ACK_WAIT;
-                if r.load = '1' then
-                    v.load_data := data_permuted;
+                if d_in.error = '1' then
+                    -- dcache will discard the second request
+                    exception := '1';
+                    dsisr(30) := d_in.tlb_miss;
+                    v.state := IDLE;
+                else
+                    v.state := LAST_ACK_WAIT;
+                    if r.load = '1' then
+                        v.load_data := data_permuted;
+                    end if;
                 end if;
             end if;
 
         when LAST_ACK_WAIT =>
             stall := '1';
             if d_in.valid = '1' then
-                write_enable := r.load;
-                if r.load = '1' and r.update = '1' then
-                    -- loads with rA update need an extra cycle
-                    v.state := LD_UPDATE;
-                else
-                    -- stores write back rA update in this cycle
-                    do_update := r.update;
-                    stall := '0';
-                    done := '1';
+                if d_in.error = '1' then
+                    if two_dwords = '1' then
+                        addr := next_addr;
+                    else
+                        addr := r.addr;
+                    end if;
+                    exception := '1';
+                    dsisr(30) := d_in.tlb_miss;
                     v.state := IDLE;
+                else
+                    write_enable := r.load;
+                    if r.load = '1' and r.update = '1' then
+                        -- loads with rA update need an extra cycle
+                        v.state := LD_UPDATE;
+                    else
+                        -- stores write back rA update in this cycle
+                        do_update := r.update;
+                        stall := '0';
+                        done := '1';
+                        v.state := IDLE;
+                    end if;
                 end if;
             end if;
 
@@ -372,6 +398,13 @@ begin
         l_out.rc <= r.rc and done;
         l_out.store_done <= d_in.store_done;
 
+        -- update exception info back to execute1
+        e_out.exception <= exception;
+        if exception = '1' then
+            v.dar := addr;
+            v.dsisr := dsisr;
+        end if;
+
         stall_out <= stall;
 
         -- Update registers