LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.NUMERIC_STD.ALL; use IEEE.MATH_REAL.ALL; library work; use work.mips_types.all; ENTITY dcache IS Generic ( cache_size : natural := 2048; -- words line_size : natural := 8 -- words ); Port ( RST_I : in STD_LOGIC; CLK_I : in STD_LOGIC; ACK_I : in STD_LOGIC; SRDY_I : in STD_LOGIC; ADDR_O : out word_t; DAT_I : in word_t; STB_O : out STD_LOGIC; CYC_O : out STD_LOGIC; en : in STD_LOGIC; cpu_en : in STD_LOGIC; cpu_we : in STD_LOGIC; cpu_be : in unsigned(3 downto 0); cpu_addr : in word_t; cpu_din : in word_t; cpu_dout : out word_t; cpu_busy : out STD_LOGIC ); END dcache; ARCHITECTURE behavior OF dcache IS COMPONENT dpram_1w1r GENERIC ( addr_width : integer := 3; data_width : integer := 8 ); PORT ( clka : in STD_LOGIC; clkb : in STD_LOGIC; en_a : in STD_LOGIC; en_b : in STD_LOGIC; we_a : in STD_LOGIC; addr_a : in unsigned (addr_width-1 downto 0); addr_b : in unsigned (addr_width-1 downto 0); din_a : in unsigned (data_width-1 downto 0); dout_b : out unsigned (data_width-1 downto 0) ); END COMPONENT; COMPONENT dpram_2w2r is GENERIC ( addr_width : integer := 3; data_width : integer := 8 ); PORT ( clk_a : in STD_LOGIC; clk_b : in STD_LOGIC; en_a : in STD_LOGIC; en_b : in STD_LOGIC; we_a : in STD_LOGIC; we_b : in STD_LOGIC; addr_a : in unsigned (addr_width-1 downto 0); addr_b : in unsigned (addr_width-1 downto 0); din_a : in unsigned (data_width-1 downto 0); din_b : in unsigned (data_width-1 downto 0); dout_a : out unsigned (data_width-1 downto 0); dout_b : out unsigned (data_width-1 downto 0) ); END COMPONENT; function lg2(x : natural) return natural is begin return natural(ceil(log2(real(x)))); end lg2; function po2(x : natural) return natural is begin return 2**lg2(x); end po2; constant word_index_width : natural := lg2(line_size); constant cache_index_width : natural := lg2(cache_size) - word_index_width; constant tag_width : natural := 32 - word_index_width - cache_index_width - 2; constant tag_parity_width : natural := 3; constant tram_data_width : natural := 2 + tag_parity_width + tag_width; constant tram_addr_width : natural := cache_index_width; subtype tram_data_t is unsigned (tram_data_width-1 downto 0); type dcache_entry_t is record valid : std_logic; dirty : std_logic; tv_p : unsigned(tag_parity_width-1 downto 0); tag : unsigned(tag_width-1 downto 0); end record; alias cpu_word_index is cpu_addr(word_index_width+1 downto 2); alias cpu_cache_index is cpu_addr(cache_index_width+word_index_width+1 downto word_index_width+2); alias cpu_tag is cpu_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); function to_dcache_entry(x : tram_data_t) return dcache_entry_t is variable result : dcache_entry_t; begin result.valid := x(0); result.dirty := x(1); result.tv_p := x(4 downto 2); result.tag := x(tag_width+4 downto 5); return result; end to_dcache_entry; function to_tram_data(x : dcache_entry_t) return tram_data_t is variable result : tram_data_t; begin result(0) := x.valid; result(1) := x.dirty; result(4 downto 2) := x.tv_p; result(tag_width+4 downto 5) := x.tag; return result; end to_tram_data; type cache_state_t is (init, ready, flush, mem_request, mem_access, mem_wait, mem_data, upd_cache, rd_cache); signal s, sn : cache_state_t; signal cache_busy : std_logic; signal cache_hit : std_logic; signal tag_match : std_logic; signal word_index_reg : unsigned(word_index_width-1 downto 0); signal cache_index_reg : unsigned(cache_index_width-1 downto 0); signal tag_index_reg : unsigned(tag_width-1 downto 0); signal cache_entry_in : dcache_entry_t; signal cache_entry_out : dcache_entry_t; signal cpu_dram_addr : unsigned(lg2(cache_size)-1 downto 0); signal cpu_dram_dout : word_t; signal cpu_dram_din : word_t; signal cpu_data_reg : word_t; signal cpu_be_reg : unsigned(3 downto 0); signal cpu_we_reg : std_logic; signal ctrl_dram_en : std_logic; signal ctrl_dram_addr : unsigned(lg2(cache_size)-1 downto 0); signal ctrl_dram_din : word_t; signal ctrl_dram_we : unsigned(3 downto 0); signal cpu_dram_we : unsigned(3 downto 0); signal cpu_dram_en : std_logic; signal cpu_en2 : std_logic; signal cpu_we2 : std_logic; signal tram_addr_rd : unsigned(cache_index_width-1 downto 0); signal tram_dout : tram_data_t; signal tram_addr_wr : unsigned(cache_index_width-1 downto 0); signal tram_din : tram_data_t; signal tram_re : std_logic; signal tram_we : std_logic; signal ram_index_count : natural range 0 to 2**word_index_width-1; signal ram_index_count_rst : std_logic; signal cache_index_count : natural range 0 to 2**cache_index_width-1; signal cache_index_count_en : std_logic; signal mem_index_count : natural range 0 to 2**word_index_width-1; signal mem_index_count_en : std_logic; signal mem_index_count_rst : std_logic; signal cpu_reg_en : std_logic; signal was_miss : std_logic; signal data_write : std_logic; signal cpu_hit_we : std_logic; signal instant_raw : std_logic; begin cpu_hit_we <= cpu_we2 and cache_hit; cpu_index_register: process(CLK_I) begin if rising_edge(CLK_I) then if RST_I = '1' then cache_index_reg <= (others => '0'); tag_index_reg <= (others => '0'); elsif cpu_reg_en = '1' and en = '1' and instant_raw = '0' then word_index_reg <= cpu_word_index; cache_index_reg <= cpu_cache_index; tag_index_reg <= cpu_tag; end if; end if; end process; cpu_data_register: process(CLK_I) begin if rising_edge(CLK_I) then if RST_I = '1' then cpu_we_reg <= '0'; elsif cpu_reg_en = '1' and en = '1' then cpu_data_reg <= cpu_din; cpu_be_reg <= cpu_be; cpu_we_reg <= cpu_we; end if; end if; end process; cpu_was_wr_register: process(CLK_I) begin if rising_edge(CLK_I) then cpu_en2 <= '0'; cpu_we2 <= '0'; if cpu_en = '1' and en = '1' then cpu_we2 <= cpu_we; cpu_en2 <= '1'; cpu_dram_din <= cpu_din; end if; end if; end process; instant_raw_logic: process(CLK_I) begin if rising_edge(CLK_I) then instant_raw <= cpu_hit_we and cpu_en and en and not cpu_we; end if; end process; inst_tag_ram : dpram_1w1r GENERIC MAP ( addr_width => tram_addr_width, data_width => tram_data_width ) PORT MAP ( clka => CLK_I, clkb => CLK_I, en_a => '1', en_b => tram_re, we_a => tram_we, addr_a => tram_addr_wr, addr_b => tram_addr_rd, din_a => tram_din, dout_b => tram_dout ); gen_data_ram: for i in 0 to 3 generate begin inst_data_ram : dpram_2w2r GENERIC MAP ( addr_width => lg2(cache_size), data_width => word_t'length/4 ) PORT MAP ( clk_a => CLK_I, clk_b => CLK_I, en_a => ctrl_dram_en, en_b => '1', we_a => ctrl_dram_we(i), we_b => cpu_dram_we(i), addr_a => ctrl_dram_addr, addr_b => cpu_dram_addr, din_a => ctrl_dram_din(8*(i+1)-1 downto 8*i), din_b => cpu_dram_din(8*(i+1)-1 downto 8*i), dout_a => open, dout_b => cpu_dram_dout(8*(i+1)-1 downto 8*i) ); end generate; cache_state_next: process(CLK_I) begin if rising_edge(CLK_I) then if RST_I = '1' then s <= init; else s <= sn; end if; end if; end process; cpu_busy <= cache_busy or instant_raw; cpu_dout <= cpu_dram_dout; tag_match <= '1' when tag_index_reg = cache_entry_out.tag else '0'; cache_hit <= tag_match and cache_entry_out.valid; tram_din <= to_tram_data(cache_entry_in); tram_addr_rd <= cpu_cache_index when was_miss = '0' else cache_index_reg; cache_entry_out <= to_dcache_entry(tram_dout); cpu_dram_addr <= (cpu_cache_index & cpu_word_index) when (was_miss = '0' and cpu_hit_we = '0' and instant_raw = '0') else (cache_index_reg & word_index_reg); ADDR_O <= tag_index_reg & cache_index_reg & to_unsigned(mem_index_count, word_index_width) & "00"; ctrl_dram_addr <= cache_index_reg & to_unsigned(ram_index_count, word_index_width); ctrl_dram_din <= DAT_I; ctrl_dram_we <= (others => '1'); ctrl_dram_en <= data_write and ACK_I; cpu_dram_we <= cpu_be_reg when (cpu_hit_we = '1') else (others => '0'); cache_state: process(s, instant_raw, cache_hit, cache_index_count, ram_index_count, mem_index_count, cache_index_reg, ACK_I, tag_index_reg, cpu_en, SRDY_I, cpu_en2, cpu_we_reg) begin cpu_reg_en <= '0'; cache_busy <= '1'; tram_we <= '0'; cache_index_count_en <= '0'; ram_index_count_rst <= '0'; mem_index_count_en <= '0'; mem_index_count_rst <= '0'; CYC_O <= '0'; STB_O <= '0'; tram_re <= '0'; was_miss <= '0'; data_write <= '0'; tram_addr_wr <= to_unsigned(cache_index_count, cache_index_width); cache_entry_in.tv_p <= (others => '0'); cache_entry_in.tag <= tag_index_reg; cache_entry_in.valid <= '0'; cache_entry_in.dirty <= '0'; sn <= s; case s is when init => sn <= flush; when ready => cache_busy <= '0'; cpu_reg_en <= '1'; tram_re <= cpu_en; if cpu_en2 = '1' then if cache_hit = '0' and cpu_we_reg = '0' then sn <= mem_request; cpu_reg_en <= '0'; cache_busy <= '1'; CYC_O <= '1'; end if; end if; when flush => cache_index_count_en <= '1'; tram_addr_wr <= to_unsigned(cache_index_count, cache_index_width); tram_we <= '1'; cache_entry_in.valid <= '0'; cache_entry_in.tag <= (others => '0'); if cache_index_count = 0 then sn <= ready; if cpu_en = '1' then cpu_reg_en <= '1'; tram_re <= '1'; end if; end if; when mem_request => ram_index_count_rst <= '1'; mem_index_count_rst <= '1'; CYC_O <= '1'; if SRDY_I = '1' then sn <= mem_access; end if; when mem_access => data_write <= '1'; mem_index_count_en <= '1'; CYC_O <= '1'; STB_O <= '1'; if mem_index_count = 2**word_index_width-1 then if SRDY_I = '1' then sn <= mem_data; end if; end if; when mem_data => CYC_O <= '1'; data_write <= '1'; if ram_index_count = 2**word_index_width-1 then if ACK_I = '1' then sn <= upd_cache; end if; end if; when upd_cache => CYC_O <= '1'; tram_addr_wr <= cache_index_reg; tram_we <= '1'; cache_entry_in.valid <= '1'; sn <= rd_cache; when rd_cache => tram_re <= '1'; was_miss <= '1'; sn <= ready; when others => sn <= ready; end case; end process; cache_index_counter: process(CLK_I) begin if rising_edge(CLK_I) then if cache_index_count_en = '0' then cache_index_count <= 2**cache_index_width-1; elsif cache_index_count /= 0 then cache_index_count <= cache_index_count - 1; end if; end if; end process; ram_index_counter: process(CLK_I) begin if rising_edge(CLK_I) then if ram_index_count_rst = '1' then ram_index_count <= 0; elsif data_write = '1' and ACK_I = '1' then if ram_index_count /= 2**word_index_width-1 then ram_index_count <= ram_index_count + 1; end if; end if; end if; end process; mem_index_counter: process(CLK_I) begin if rising_edge(CLK_I) then if mem_index_count_rst = '1' then mem_index_count <= 0; elsif mem_index_count_en = '1' and SRDY_I = '1' then if mem_index_count /= 2**word_index_width-1 then mem_index_count <= mem_index_count + 1; end if; end if; end if; end process; end behavior;