-------------------------------------------------------------------------- -- 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 . -- -- 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 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; ctrl : in cache_ctrl_t; 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; 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; 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, invalidate, flush, mem_request, mem_access, mem_data, rd_cache, upd_cache); signal s, sn : cache_state_t; signal cache_req : std_logic; signal cache_ack : std_logic; signal cache_busy : std_logic; signal cache_miss : std_logic; signal tag_match : std_logic; signal cache_miss_inv : std_logic; signal tag_match_inv : std_logic; signal word_index_reg : unsigned(word_index_width-1 downto 0); signal addr_windex_reg : unsigned(word_index_width-1 downto 0); signal cache_index_reg : unsigned(cache_index_width-1 downto 0); signal cache_index_inv : unsigned(cache_index_width-1 downto 0); signal tag_reg : unsigned(tag_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 : icache_entry_t; signal cache_entry_out : icache_entry_t; signal cache_entry_out_inv : 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 tag_ram_addr_rd : unsigned(cache_index_width-1 downto 0); signal tag_ram_data_rd : tag_ram_data_t; signal tag_ram_data_rd_inv : 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_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 ram_read_en : 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; begin ram_read_en <= cpu_en or was_miss; 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_index_reg: process(CLK_I) begin if rising_edge(CLK_I) then if RST_I = '1' then cache_index_reg <= (others => '0'); tag_reg <= (others => '0'); elsif fill_count_en = '1' then if ACK_I = '1' then word_index_reg <= word_index_reg + 1; end if; elsif cache_busy = '0' then word_index_reg <= cpu_word_index; cache_index_reg <= cpu_cache_index; tag_reg <= cpu_tag; end if; end if; end process; addr_windex_register: process(CLK_I) begin if rising_edge(CLK_I) then if request_count_en = '1'then if SRDY_I = '1' then addr_windex_reg <= addr_windex_reg + 1; end if; elsif cache_busy = '0' then addr_windex_reg <= cpu_word_index; end if; end if; end process; cpu_request_register: process(CLK_I) begin if rising_edge(CLK_I) then if RST_I = '1' then cache_req <= '0'; elsif cpu_en = '1' then if cache_busy = '0' then cache_req <= '1'; end if; elsif cache_ack = '1' then cache_req <= '0'; end if; end if; end process; 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; inst_tag_ram : dpram_2w2r 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 => ram_read_en, we_a => tag_ram_we, we_b => '0', addr_a => tag_ram_addr_wr, addr_b => tag_ram_addr_rd, din_a => tag_ram_data_wr, din_b => tag_ram_data_wr, dout_a => tag_ram_data_rd_inv, 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 => ram_read_en, 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; ADDR_O <= tag_reg & cache_index_reg & addr_windex_reg & "00"; cpu_busy <= cache_busy; cpu_dout <= data_ram_data_rd; cache_entry_out <= to_icache_entry(tag_ram_data_rd); tag_match <= '1' when tag_reg = cache_entry_out.tag else '0'; cache_miss <= not (tag_match and cache_entry_out.valid); cache_entry_out_inv <= to_icache_entry(tag_ram_data_rd_inv); tag_match_inv <= '1' when tag_reg_inv = cache_entry_out_inv.tag else '0'; cache_miss_inv <= not (tag_match_inv and cache_entry_out_inv.valid); tag_ram_data_wr <= to_tag_ram_data(cache_entry_in); tag_ram_addr_wr <= to_unsigned(flush_count, cache_index_width) when invalidate_en = '1' else cache_index_reg; tag_ram_addr_rd <= cpu_cache_index when was_miss = '0' else cache_index_reg; data_ram_addr_rd <= (cpu_cache_index & cpu_word_index) when was_miss = '0' else (cache_index_reg & word_index_reg); data_ram_addr_wr <= cache_index_reg & word_index_reg; data_ram_data_wr <= DAT_I; data_ram_we <= fill_count_en and ACK_I; cache_state: process(s, cache_req, cache_miss, cache_miss_inv, flush_count_rdy, fill_count_rdy, request_count_rdy, tag_reg, SRDY_I, 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 <= tag_reg; 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'; elsif cache_req = '1' then if cache_miss = '1' then sn <= mem_request; CYC_O <= '1'; else cache_busy <= '0'; cache_ack <= '1'; 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_miss_inv = '0' 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 => request_count_en <= '1'; fill_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;