Committed on the Free edition of March Hare Software CVSNT Server. Upgrade to CVS Suite for more features and support: http://march-hare.com/cvsnt/ git-svn-id: http://moon:8086/svn/vhdl/trunk@942 cc03376c-175c-47c8-b038-4cd826a8556b
514 lines
14 KiB
VHDL
514 lines
14 KiB
VHDL
--------------------------------------------------------------------------
|
|
-- Project: JIPS, a portable 32-bit RISC CPU written in VHDL
|
|
-- This file: JIPS top file
|
|
--
|
|
-- Copyright (C) 2008 J. Ahrensfeld
|
|
--
|
|
-- This program is free software: you can redistribute it and/or modify
|
|
-- it under the terms of the GNU General Public License as published by
|
|
-- the Free Software Foundation, either version 3 of the License, or
|
|
-- (at your option) any later version.
|
|
--
|
|
-- This program is distributed in the hope that it will be useful,
|
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
-- GNU General Public License for more details.
|
|
--
|
|
-- You should have received a copy of the GNU General Public License
|
|
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
--
|
|
-- For questions and ideas, please contact the author at jens@jayfield.org
|
|
--
|
|
--------------------------------------------------------------------------
|
|
|
|
LIBRARY IEEE;
|
|
USE IEEE.STD_LOGIC_1164.ALL;
|
|
USE IEEE.NUMERIC_STD.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;
|
|
MRDY_O : out STD_LOGIC;
|
|
ADDR_O : out word_t;
|
|
DAT_I : in word_t;
|
|
STB_O : out STD_LOGIC;
|
|
CYC_O : out STD_LOGIC;
|
|
ctrl : in cache_ctrl_t;
|
|
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_2w2r2c_ra 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;
|
|
|
|
constant addr_width : natural := 32;
|
|
constant word_index_width : natural := lg2(line_size);
|
|
constant cache_index_width : natural := lg2(cache_size) - word_index_width;
|
|
constant tag_width : natural := addr_width - 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 dcache_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;
|
|
|
|
function to_dcache_entry(x : tag_ram_data_t) return dcache_entry_t is
|
|
variable result : dcache_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_dcache_entry;
|
|
|
|
function to_tag_ram_data(x : dcache_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, invalidate, flush, mem_request, mem_access, mem_data, rd_cache);
|
|
signal s, sn : cache_state_t;
|
|
|
|
signal cache_req : std_logic;
|
|
signal cache_ack : std_logic;
|
|
signal cache_busy : std_logic;
|
|
signal cache_hit : std_logic;
|
|
signal tag_match : std_logic;
|
|
signal cache_hit_inv : std_logic;
|
|
signal tag_match_inv : std_logic;
|
|
|
|
signal request_addr : unsigned(addr_width-1 downto 0);
|
|
signal fill_addr : unsigned(addr_width-1 downto 0);
|
|
|
|
signal cache_index_inv : unsigned(cache_index_width-1 downto 0);
|
|
signal tag_inv : unsigned(tag_width-1 downto 0);
|
|
signal tag_reg_inv : unsigned(tag_width-1 downto 0);
|
|
|
|
signal cache_entry_in : dcache_entry_t;
|
|
signal cache_entry_out : dcache_entry_t;
|
|
signal cache_entry_out_inv : dcache_entry_t;
|
|
signal cpu_data_ram_addr : unsigned(lg2(cache_size)-1 downto 0);
|
|
signal cpu_data_ram_dout : word_t;
|
|
signal cpu_data_reg : word_t;
|
|
signal cpu_be_reg : unsigned(3 downto 0);
|
|
signal cpu_we_reg : std_logic;
|
|
signal ctrl_data_ram_addr : unsigned(lg2(cache_size)-1 downto 0);
|
|
signal ctrl_data_ram_we : unsigned(3 downto 0);
|
|
signal cpu_data_ram_we : unsigned(3 downto 0);
|
|
signal cpu_we2 : std_logic;
|
|
|
|
signal tag_ram_addr_rd : unsigned(cache_index_width-1 downto 0);
|
|
signal tag_ram_dout : tag_ram_data_t;
|
|
signal tag_ram_dout_inv : tag_ram_data_t;
|
|
signal tag_ram_addr_wr : unsigned(cache_index_width-1 downto 0);
|
|
signal tag_ram_din : tag_ram_data_t;
|
|
signal tag_ram_we : std_logic;
|
|
|
|
signal fill_count : natural range 0 to 2**word_index_width-1;
|
|
signal fill_count_en : std_logic;
|
|
signal fill_count_rdy : std_logic;
|
|
signal flush_count : natural range 0 to 2**cache_index_width-1;
|
|
signal flush_count_rst : std_logic;
|
|
signal flush_count_en : std_logic;
|
|
signal flush_count_rdy : std_logic;
|
|
signal request_count : natural range 0 to 2**word_index_width-1;
|
|
signal request_count_en : std_logic;
|
|
signal request_count_rdy : std_logic;
|
|
signal was_miss : std_logic;
|
|
signal invalidate_all : std_logic;
|
|
signal invalidate_ack : std_logic;
|
|
signal invalidate_en : std_logic;
|
|
signal invalidate_req : std_logic;
|
|
signal cpu_hit_we : std_logic;
|
|
signal instant_raw : std_logic;
|
|
signal hit_cache_index : unsigned(cache_index_width-1 downto 0);
|
|
|
|
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);
|
|
|
|
alias fill_word_index is fill_addr(word_index_width+1 downto 2);
|
|
alias fill_cache_index is fill_addr(cache_index_width+word_index_width+1 downto word_index_width+2);
|
|
alias fill_tag is fill_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2);
|
|
|
|
alias req_word_index is request_addr(word_index_width+1 downto 2);
|
|
alias req_cache_index is request_addr(cache_index_width+word_index_width+1 downto word_index_width+2);
|
|
alias req_tag is request_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2);
|
|
|
|
begin
|
|
|
|
cache_index_inv <= ctrl.inv_addr(cache_index_width+word_index_width+1 downto word_index_width+2);
|
|
tag_inv <= ctrl.inv_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2);
|
|
|
|
cpu_hit_we <= cpu_we2 and cache_hit;
|
|
|
|
fill_address_register:
|
|
process(CLK_I)
|
|
begin
|
|
if rising_edge(CLK_I) then
|
|
if fill_count_en = '1' then
|
|
if ACK_I = '1' then
|
|
fill_word_index <= fill_word_index + 1;
|
|
end if;
|
|
elsif cache_busy = '0' then
|
|
fill_addr <= cpu_addr(addr_width-1 downto 2) & "00";
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
request_address_register:
|
|
process(CLK_I)
|
|
begin
|
|
if rising_edge(CLK_I) then
|
|
if request_count_en = '1'then
|
|
if SRDY_I = '1' then
|
|
req_word_index <= req_word_index + 1;
|
|
end if;
|
|
elsif cache_busy = '0' then
|
|
request_addr <= cpu_addr(addr_width-1 downto 2) & "00";
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
cpu_request_register:
|
|
process(CLK_I)
|
|
begin
|
|
if rising_edge(CLK_I) then
|
|
if RST_I = '1' then
|
|
cpu_we_reg <= '0';
|
|
cache_req <= '0';
|
|
elsif cpu_en = '1' then
|
|
if cache_busy = '0' then
|
|
cpu_we2 <= cpu_we;
|
|
cache_req <= '1';
|
|
cpu_data_reg <= cpu_din;
|
|
cpu_be_reg <= cpu_be;
|
|
cpu_we_reg <= cpu_we;
|
|
if cpu_we = '1' then
|
|
hit_cache_index <= cpu_cache_index;
|
|
end if;
|
|
end if;
|
|
elsif cache_ack = '1' then
|
|
cache_req <= '0';
|
|
cpu_we2 <= '0';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
instant_raw_logic:
|
|
process(CLK_I)
|
|
begin
|
|
if rising_edge(CLK_I) then
|
|
instant_raw <= '0';
|
|
if cpu_word_index = fill_word_index and cpu_cache_index = hit_cache_index then
|
|
instant_raw <= cpu_hit_we and cpu_en and not cpu_we;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
inst_tag_ram : dpram_2w2r2c_ra
|
|
GENERIC MAP
|
|
(
|
|
addr_width => tag_ram_addr_width,
|
|
data_width => tag_ram_data_width
|
|
)
|
|
PORT MAP
|
|
(
|
|
clk_a => CLK_I,
|
|
clk_b => CLK_I,
|
|
en_a => '1',
|
|
en_b => '1',
|
|
we_a => tag_ram_we,
|
|
we_b => '0',
|
|
addr_a => tag_ram_addr_wr,
|
|
addr_b => tag_ram_addr_rd,
|
|
din_a => tag_ram_din,
|
|
din_b => tag_ram_din,
|
|
dout_a => tag_ram_dout_inv,
|
|
dout_b => tag_ram_dout
|
|
);
|
|
|
|
gen_data_ram:
|
|
for i in 0 to 3 generate
|
|
begin
|
|
|
|
inst_data_ram : dpram_2w2r2c_ra
|
|
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 => '1',
|
|
en_b => '1',
|
|
we_a => cpu_data_ram_we(i),
|
|
we_b => ctrl_data_ram_we(i),
|
|
addr_a => ctrl_data_ram_addr,
|
|
addr_b => cpu_data_ram_addr,
|
|
din_a => cpu_data_reg(8*(i+1)-1 downto 8*i),
|
|
din_b => DAT_I(8*(i+1)-1 downto 8*i),
|
|
dout_a => open,
|
|
dout_b => cpu_data_ram_dout(8*(i+1)-1 downto 8*i)
|
|
);
|
|
end generate;
|
|
|
|
|
|
|
|
cache_invalidate_request:
|
|
process(CLK_I)
|
|
begin
|
|
if rising_edge(CLK_I) then
|
|
if RST_I = '1' or ctrl.inv_all = '1' or ctrl.inv_at = '1' then
|
|
invalidate_req <= '1';
|
|
invalidate_all <= ctrl.inv_all or RST_I;
|
|
tag_reg_inv <= tag_inv;
|
|
elsif invalidate_ack = '1' then
|
|
invalidate_req <= '0';
|
|
invalidate_all <= '0';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
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;
|
|
|
|
MRDY_O <= fill_count_en;
|
|
ADDR_O <= request_addr;
|
|
|
|
cpu_busy <= cache_busy;
|
|
cpu_dout <= cpu_data_ram_dout;
|
|
|
|
tag_match <= '1' when fill_tag = cache_entry_out.tag else '0';
|
|
cache_hit <= tag_match and cache_entry_out.valid;
|
|
tag_match_inv <= '1' when tag_reg_inv = cache_entry_out_inv.tag else '0';
|
|
cache_hit_inv <= tag_match_inv and cache_entry_out_inv.valid;
|
|
tag_ram_din <= to_tag_ram_data(cache_entry_in);
|
|
tag_ram_addr_wr <= to_unsigned(flush_count, cache_index_width) when invalidate_en = '1' else fill_cache_index;
|
|
tag_ram_addr_rd <= cpu_cache_index when (was_miss = '0' and instant_raw = '0') else fill_cache_index;
|
|
|
|
cache_entry_out <= to_dcache_entry(tag_ram_dout);
|
|
cache_entry_out_inv <= to_dcache_entry(tag_ram_dout_inv);
|
|
|
|
cpu_data_ram_addr <= (cpu_cache_index & cpu_word_index) when (was_miss = '0' and instant_raw = '0' and fill_count_en = '0') else (fill_cache_index & fill_word_index);
|
|
ctrl_data_ram_addr <= fill_cache_index & fill_word_index;
|
|
ctrl_data_ram_we <= (others => fill_count_en and ACK_I);
|
|
cpu_data_ram_we <= cpu_be_reg when (cpu_hit_we = '1') else (others => '0');
|
|
|
|
cache_state:
|
|
process(s, cache_req, instant_raw, cache_hit, cache_hit_inv, flush_count_rdy, fill_count_rdy, request_count_rdy, fill_tag, SRDY_I, cpu_we_reg, invalidate_req, invalidate_all)
|
|
begin
|
|
cache_busy <= cache_req;
|
|
cache_ack <= '0';
|
|
tag_ram_we <= '0';
|
|
flush_count_en <= '0';
|
|
flush_count_rst <= '0';
|
|
invalidate_en <= '0';
|
|
request_count_en <= '0';
|
|
fill_count_en <= '0';
|
|
CYC_O <= '0';
|
|
STB_O <= '0';
|
|
was_miss <= '0';
|
|
invalidate_ack <= '0';
|
|
|
|
cache_entry_in.tv_p <= (others => '0');
|
|
cache_entry_in.tag <= fill_tag;
|
|
cache_entry_in.valid <= '0';
|
|
sn <= s;
|
|
|
|
case s is
|
|
when init =>
|
|
sn <= ready;
|
|
when ready =>
|
|
if invalidate_req = '1' then
|
|
sn <= invalidate;
|
|
invalidate_en <= '1';
|
|
else
|
|
if cache_req = '1' then
|
|
if cache_hit = '0' and cpu_we_reg = '0' then
|
|
sn <= mem_request;
|
|
CYC_O <= '1';
|
|
else
|
|
cache_busy <= instant_raw;
|
|
cache_ack <= not instant_raw;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
when invalidate =>
|
|
sn <= rd_cache;
|
|
invalidate_en <= '1';
|
|
invalidate_ack <= '1';
|
|
if invalidate_all = '1' then
|
|
sn <= flush;
|
|
flush_count_rst <= '1';
|
|
elsif cache_hit_inv = '1' then
|
|
tag_ram_we <= '1';
|
|
cache_entry_in.valid <= '0';
|
|
cache_entry_in.tag <= (others => '0');
|
|
sn <= rd_cache;
|
|
end if;
|
|
|
|
when flush =>
|
|
flush_count_en <= '1';
|
|
invalidate_en <= '1';
|
|
tag_ram_we <= '1';
|
|
cache_entry_in.valid <= '0';
|
|
cache_entry_in.tag <= (others => '0');
|
|
if flush_count_rdy = '1' then
|
|
tag_ram_we <= '0';
|
|
sn <= rd_cache;
|
|
end if;
|
|
when mem_request =>
|
|
CYC_O <= '1';
|
|
if SRDY_I = '1' then
|
|
sn <= mem_access;
|
|
end if;
|
|
when mem_access =>
|
|
fill_count_en <= '1';
|
|
request_count_en <= '1';
|
|
CYC_O <= '1';
|
|
STB_O <= '1';
|
|
if request_count_rdy = '1' then
|
|
STB_O <= '0';
|
|
sn <= mem_data;
|
|
end if;
|
|
when mem_data =>
|
|
CYC_O <= '1';
|
|
fill_count_en <= '1';
|
|
if fill_count_rdy = '1' then
|
|
tag_ram_we <= '1';
|
|
cache_entry_in.valid <= '1';
|
|
sn <= rd_cache;
|
|
end if;
|
|
when rd_cache =>
|
|
was_miss <= '1';
|
|
sn <= ready;
|
|
|
|
when others =>
|
|
sn <= ready;
|
|
end case;
|
|
|
|
end process;
|
|
|
|
flush_counter:
|
|
process(CLK_I)
|
|
begin
|
|
if rising_edge(CLK_I) then
|
|
if ctrl.inv_at = '1' then
|
|
flush_count <= to_integer(cache_index_inv);
|
|
elsif flush_count_rst = '1' then
|
|
flush_count_rdy <= '0';
|
|
flush_count <= 2**cache_index_width-1;
|
|
elsif flush_count_en = '1' then
|
|
if flush_count /= 0 then
|
|
flush_count <= flush_count - 1;
|
|
else
|
|
flush_count_rdy <= '1';
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
request_counter:
|
|
process(CLK_I)
|
|
begin
|
|
if rising_edge(CLK_I) then
|
|
if request_count_en = '0' then
|
|
request_count_rdy <= '0';
|
|
request_count <= 2**word_index_width-1;
|
|
else
|
|
if SRDY_I = '1' then
|
|
if request_count /= 0 then
|
|
request_count <= request_count - 1;
|
|
else
|
|
request_count_rdy <= '1';
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
fill_counter:
|
|
process(CLK_I)
|
|
begin
|
|
if rising_edge(CLK_I) then
|
|
if fill_count_en = '0' then
|
|
fill_count_rdy <= '0';
|
|
fill_count <= 2**word_index_width-1;
|
|
else
|
|
if ACK_I = '1' then
|
|
if fill_count /= 0 then
|
|
fill_count <= fill_count - 1;
|
|
else
|
|
fill_count_rdy <= '1';
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
end behavior;
|