357 lines
9.4 KiB
VHDL
357 lines
9.4 KiB
VHDL
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 icache 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_addr : in word_t;
|
|
cpu_dout : out word_t;
|
|
cpu_busy : out STD_LOGIC
|
|
);
|
|
END icache;
|
|
|
|
ARCHITECTURE behavior OF icache 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;
|
|
|
|
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 tag_ram_data_width : natural := 1 + tag_parity_width + tag_width;
|
|
constant tag_ram_addr_width : natural := cache_index_width;
|
|
|
|
subtype tag_ram_data_t is unsigned (tag_ram_data_width-1 downto 0);
|
|
|
|
type icache_entry_t is record
|
|
valid : 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_icache_entry(x : tag_ram_data_t) return icache_entry_t is
|
|
variable result : icache_entry_t;
|
|
begin
|
|
result.valid := x(0);
|
|
result.tv_p := x(3 downto 1);
|
|
result.tag := x(tag_width+3 downto 4);
|
|
|
|
return result;
|
|
end to_icache_entry;
|
|
|
|
function to_tag_ram_data(x : icache_entry_t) return tag_ram_data_t is
|
|
variable result : tag_ram_data_t;
|
|
begin
|
|
result(0) := x.valid;
|
|
result(3 downto 1) := x.tv_p;
|
|
result(tag_width+3 downto 4) := x.tag;
|
|
|
|
return result;
|
|
end to_tag_ram_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_miss : 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 : icache_entry_t;
|
|
signal cache_entry_out : icache_entry_t;
|
|
signal data_ram_addr_rd : unsigned(lg2(cache_size)-1 downto 0);
|
|
signal data_ram_data_rd : word_t;
|
|
signal data_ram_addr_wr : unsigned(lg2(cache_size)-1 downto 0);
|
|
signal data_ram_data_wr : word_t;
|
|
signal data_ram_we : std_logic;
|
|
signal data_ram_re : std_logic;
|
|
|
|
signal tag_ram_addr_rd : unsigned(cache_index_width-1 downto 0);
|
|
signal tag_ram_data_rd : tag_ram_data_t;
|
|
signal tag_ram_addr_wr : unsigned(cache_index_width-1 downto 0);
|
|
signal tag_ram_data_wr : tag_ram_data_t;
|
|
signal tag_ram_re : std_logic;
|
|
signal tag_ram_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;
|
|
|
|
begin
|
|
|
|
|
|
cpu_index_reg:
|
|
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' then
|
|
word_index_reg <= cpu_word_index;
|
|
cache_index_reg <= cpu_cache_index;
|
|
tag_index_reg <= cpu_tag;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
inst_tag_ram : dpram_1w1r
|
|
GENERIC MAP (
|
|
addr_width => tag_ram_addr_width,
|
|
data_width => tag_ram_data_width
|
|
)
|
|
PORT MAP (
|
|
clka => CLK_I,
|
|
clkb => CLK_I,
|
|
en_a => '1',
|
|
en_b => tag_ram_re,
|
|
we_a => tag_ram_we,
|
|
addr_a => tag_ram_addr_wr,
|
|
addr_b => tag_ram_addr_rd,
|
|
din_a => tag_ram_data_wr,
|
|
dout_b => tag_ram_data_rd
|
|
);
|
|
|
|
inst_data_ram : dpram_1w1r
|
|
GENERIC MAP (
|
|
addr_width => lg2(cache_size),
|
|
data_width => word_t'length
|
|
)
|
|
PORT MAP (
|
|
clka => CLK_I,
|
|
clkb => CLK_I,
|
|
en_a => '1',
|
|
en_b => data_ram_re,
|
|
we_a => data_ram_we,
|
|
addr_a => data_ram_addr_wr,
|
|
addr_b => data_ram_addr_rd,
|
|
din_a => data_ram_data_wr,
|
|
dout_b => data_ram_data_rd
|
|
);
|
|
|
|
|
|
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;
|
|
cpu_dout <= data_ram_data_rd;
|
|
|
|
tag_match <= '1' when tag_index_reg = cache_entry_out.tag else '0';
|
|
cache_miss <= not (tag_match and cache_entry_out.valid);
|
|
tag_ram_data_wr <= to_tag_ram_data(cache_entry_in);
|
|
tag_ram_addr_rd <= cpu_cache_index when was_miss = '0' else cache_index_reg;
|
|
cache_entry_out <= to_icache_entry(tag_ram_data_rd);
|
|
data_ram_addr_rd <= (cpu_cache_index & cpu_word_index) when was_miss = '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";
|
|
data_ram_addr_wr <= cache_index_reg & to_unsigned(ram_index_count, word_index_width);
|
|
data_ram_data_wr <= DAT_I;
|
|
data_ram_we <= data_write and ACK_I;
|
|
|
|
cache_state:
|
|
process(s, cache_miss, cache_index_count, ram_index_count, mem_index_count, cache_index_reg, ACK_I, tag_index_reg, cpu_en, SRDY_I, en)
|
|
begin
|
|
cpu_reg_en <= '0';
|
|
cache_busy <= '1';
|
|
tag_ram_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';
|
|
data_ram_re <= '0';
|
|
tag_ram_re <= '0';
|
|
was_miss <= '0';
|
|
data_write <= '0';
|
|
tag_ram_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';
|
|
sn <= s;
|
|
|
|
case s is
|
|
when init =>
|
|
sn <= flush;
|
|
when ready =>
|
|
if en = '1' then
|
|
cache_busy <= '0';
|
|
if cache_miss = '1' then
|
|
sn <= mem_request;
|
|
cpu_reg_en <= '0';
|
|
cache_busy <= '1';
|
|
CYC_O <= '1';
|
|
elsif cpu_en = '1' then
|
|
cpu_reg_en <= '1';
|
|
data_ram_re <= '1';
|
|
tag_ram_re <= '1';
|
|
end if;
|
|
end if;
|
|
when flush =>
|
|
cache_index_count_en <= '1';
|
|
tag_ram_addr_wr <= to_unsigned(cache_index_count, cache_index_width);
|
|
tag_ram_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';
|
|
data_ram_re <= '1';
|
|
tag_ram_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 =>
|
|
mem_index_count_en <= '1';
|
|
data_write <= '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';
|
|
tag_ram_addr_wr <= cache_index_reg;
|
|
tag_ram_we <= '1';
|
|
cache_entry_in.valid <= '1';
|
|
sn <= rd_cache;
|
|
|
|
when rd_cache =>
|
|
-- CYC_O <= '1';
|
|
tag_ram_re <= '1';
|
|
data_ram_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;
|