icache: Snoop writes to memory by other agents
authorPaul Mackerras <paulus@ozlabs.org>
Tue, 11 May 2021 04:04:08 +0000 (14:04 +1000)
committerPaul Mackerras <paulus@ozlabs.org>
Tue, 11 May 2021 07:20:22 +0000 (17:20 +1000)
This makes the icache snoop writes to memory in the same way that the
dcache does, thus making DMA cache-coherent for the icache as well as
the dcache.

This also simplifies the logic for the WAIT_ACK state by removing the
stbs_done variable, since is_last_row(r.store_row, r.end_row_ix) can
only be true when stbs_done is true.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
core.vhdl
icache.vhdl

index cd2bb07b1128e5f000070ce57aad490340e468b1..c36f9cf6919fd2a18de287b1834a94b2f222d16a 100644 (file)
--- a/core.vhdl
+++ b/core.vhdl
@@ -243,6 +243,7 @@ begin
            stall_out => icache_stall_out,
             wishbone_out => wishbone_insn_out,
             wishbone_in => wishbone_insn_in,
+            wb_snoop_in => wb_snoop_in,
             log_out => log_data(96 downto 43)
             );
 
index a658783c84ea32351e67a9c5c17cef91deec34a5..4d3b12e4ee22a2d73f9a56b52b1167ecc372facb 100644 (file)
@@ -68,6 +68,8 @@ entity icache is
         wishbone_out : out wishbone_master_out;
         wishbone_in  : in wishbone_slave_out;
 
+        wb_snoop_in  : in wishbone_master_out := wishbone_master_out_init;
+
         log_out      : out std_ulogic_vector(53 downto 0)
         );
 end entity icache;
@@ -220,8 +222,13 @@ architecture rtl of icache is
     signal plru_victim : plru_out_t;
     signal replace_way : way_t;
 
+    -- Memory write snoop signals
+    signal snoop_valid : std_ulogic;
+    signal snoop_index : index_t;
+    signal snoop_hits  : cache_way_valids_t;
+
     -- Return the cache line index (tag index) for an address
-    function get_index(addr: std_ulogic_vector(63 downto 0)) return index_t is
+    function get_index(addr: std_ulogic_vector) return index_t is
     begin
         return to_integer(unsigned(addr(SET_SIZE_BITS - 1 downto LINE_OFF_BITS)));
     end;
@@ -614,7 +621,10 @@ begin
     -- Cache miss/reload synchronous machine
     icache_miss : process(clk)
        variable tagset    : cache_tags_set_t;
-       variable stbs_done : boolean;
+        variable tag       : cache_tag_t;
+        variable snoop_addr : std_ulogic_vector(REAL_ADDR_BITS - 1 downto 0);
+        variable snoop_tag : cache_tag_t;
+        variable snoop_cache_tags : cache_tags_set_t;
     begin
         if rising_edge(clk) then
            -- On reset, clear all valid bits to force misses
@@ -633,13 +643,43 @@ begin
 
                -- Not useful normally but helps avoiding tons of sim warnings
                r.wb.adr <= (others => '0');
+
+                snoop_valid <= '0';
+                snoop_index <= 0;
+                snoop_hits <= (others => '0');
             else
+                -- Detect snooped writes and decode address into index and tag
+                -- Since we never write, any write should be snooped
+                snoop_valid <= wb_snoop_in.cyc and wb_snoop_in.stb and wb_snoop_in.we;
+                snoop_addr := (others => '0');
+                snoop_addr(wb_snoop_in.adr'left downto 0) := wb_snoop_in.adr;
+                snoop_index <= get_index(snoop_addr);
+                snoop_cache_tags := cache_tags(get_index(snoop_addr));
+                snoop_tag := get_tag(snoop_addr, '0');
+                snoop_hits <= (others => '0');
+                for i in way_t loop
+                    tag := read_tag(i, snoop_cache_tags);
+                    -- Ignore endian bit in comparison
+                    tag(TAG_BITS - 1) := '0';
+                    if tag = snoop_tag then
+                        snoop_hits(i) <= '1';
+                    end if;
+                end loop;
+
                 -- Process cache invalidations
                 if inval_in = '1' then
                     for i in index_t loop
                         cache_valids(i) <= (others => '0');
                     end loop;
                     r.store_valid <= '0';
+                else
+                    -- Do invalidations from snooped stores to memory, one
+                    -- cycle after the address appears on wb_snoop_in.
+                    for i in way_t loop
+                        if snoop_valid = '1' and snoop_hits(i) = '1' then
+                            cache_valids(snoop_index)(i) <= '0';
+                        end if;
+                    end loop;
                 end if;
 
                -- Main state machine
@@ -697,18 +737,13 @@ begin
 
                         r.state <= WAIT_ACK;
                     end if;
-                   -- Requests are all sent if stb is 0
-                   stbs_done := r.wb.stb = '0';
 
                    -- If we are still sending requests, was one accepted ?
-                   if wishbone_in.stall = '0' and not stbs_done then
-                       -- That was the last word ? We are done sending. Clear
-                       -- stb and set stbs_done so we can handle an eventual last
-                       -- ack on the same cycle.
+                   if wishbone_in.stall = '0' and r.wb.stb = '1' then
+                       -- That was the last word ? We are done sending. Clear stb.
                        --
                        if is_last_row_addr(r.wb.adr, r.end_row_ix) then
                            r.wb.stb <= '0';
-                           stbs_done := true;
                        end if;
 
                        -- Calculate the next row address
@@ -719,7 +754,7 @@ begin
                    if wishbone_in.ack = '1' then
                         r.rows_valid(r.store_row mod ROW_PER_LINE) <= '1';
                        -- Check for completion
-                       if stbs_done and is_last_row(r.store_row, r.end_row_ix) then
+                       if is_last_row(r.store_row, r.end_row_ix) then
                            -- Complete wishbone cycle
                            r.wb.cyc <= '0';