From c78f82dd0566441d3078f891c1b8cea3cf2d14e6 Mon Sep 17 00:00:00 2001 From: Jens Ahrensfeld Date: Wed, 27 Jul 2016 06:26:54 +0000 Subject: [PATCH] [MIPS] - added MMU (Zwischenstand) git-svn-id: http://moon:8086/svn/vhdl/trunk@1336 cc03376c-175c-47c8-b038-4cd826a8556b --- lib/CPUs/MIPS/src/core/MMU/TLB/mips_biu.vhd | 434 +++++++++ lib/CPUs/MIPS/src/core/MMU/TLB/mips_cam.vhd | 319 +++++++ lib/CPUs/MIPS/src/core/MMU/TLB/mips_cam2.vhd | 329 +++++++ lib/CPUs/MIPS/src/core/MMU/TLB/mips_cop.vhd | 867 +++++++++++++++++ .../MIPS/src/core/MMU/TLB/mips_dcache.vhd | 780 ++++++++++++++++ .../MIPS/src/core/MMU/TLB/mips_func_pkg.vhd | 276 ++++++ .../MIPS/src/core/MMU/TLB/mips_icache.vhd | 663 +++++++++++++ .../MIPS/src/core/MMU/TLB/mips_pipeline.vhd | 877 ++++++++++++++++++ lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb.vhd | 340 +++++++ .../src/core/MMU/TLB/mips_tlb_20130811.vhd | 204 ++++ .../MIPS/src/core/MMU/TLB/mips_tlb_block.vhd | 85 ++ .../src/core/MMU/TLB/mips_tlb_with_cam.vhd | 350 +++++++ .../src/core/MMU/TLB/mips_tlb_with_comp.vhd | 343 +++++++ lib/CPUs/MIPS/src/core/MMU/TLB/mips_top.vhd | 396 ++++++++ lib/CPUs/MIPS/src/core/MMU/TLB/mips_types.vhd | 807 ++++++++++++++++ .../MIPS/src/core/MMU/TLB/mips_util_pkg.vhd | 68 ++ .../MIPS/src/core/MMU/TLB/old/mips_dcache.vhd | 696 ++++++++++++++ .../core/MMU/TLB/old/mips_dcache_20130728.vhd | 743 +++++++++++++++ .../core/MMU/TLB/old/mips_dcache_20130729.vhd | 754 +++++++++++++++ .../TLB/old/mips_dcache_20130729_works.vhd | 754 +++++++++++++++ .../core/MMU/TLB/old/mips_dcache_20130730.vhd | 696 ++++++++++++++ .../src/core/MMU/TLB/old/mips_dcache_new.vhd | 782 ++++++++++++++++ .../MIPS/src/core/MMU/TLB/old/mips_icache.vhd | 583 ++++++++++++ .../core/MMU/TLB/old/mips_icache_20130728.vhd | 637 +++++++++++++ .../core/MMU/TLB/old/mips_icache_20130730.vhd | 583 ++++++++++++ .../TLB/old/mips_icache_20130801_works.vhd | 640 +++++++++++++ .../src/core/MMU/TLB/old/mips_icache_new.vhd | 671 ++++++++++++++ .../src/core/MMU/TLB/old/mips_icache_orig.vhd | 513 ++++++++++ .../src/core/MMU/TLB/old/mips_tlb_types.vhd | 83 ++ lib/CPUs/MIPS/src/core/MMU/TLB/test.vhd | 73 ++ lib/CPUs/MIPS/src/core/MMU/TLB/todo_txt.txt | 9 + lib/CPUs/MIPS/src/core/MMU/mips_biu.vhd | 552 +++++++++++ lib/CPUs/MIPS/src/core/MMU/mips_dcache.vhd | 513 ++++++++++ lib/CPUs/MIPS/src/core/MMU/mips_icache.vhd | 488 ++++++++++ .../MIPS/src/core/MMU/mips_pipeline_nofwd.vhd | 867 +++++++++++++++++ 35 files changed, 17775 insertions(+) create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/mips_biu.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/mips_cam.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/mips_cam2.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/mips_cop.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/mips_dcache.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/mips_func_pkg.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/mips_icache.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/mips_pipeline.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb_20130811.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb_block.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb_with_cam.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb_with_comp.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/mips_top.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/mips_types.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/mips_util_pkg.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_20130728.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_20130729.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_20130729_works.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_20130730.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_new.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_20130728.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_20130730.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_20130801_works.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_new.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_orig.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_tlb_types.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/test.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/TLB/todo_txt.txt create mode 100644 lib/CPUs/MIPS/src/core/MMU/mips_biu.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/mips_dcache.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/mips_icache.vhd create mode 100644 lib/CPUs/MIPS/src/core/MMU/mips_pipeline_nofwd.vhd diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/mips_biu.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_biu.vhd new file mode 100644 index 0000000..e54e35d --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_biu.vhd @@ -0,0 +1,434 @@ +-------------------------------------------------------------------------- +-- 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; +use work.mips_util_pkg.all; +use work.busmaster_types.all; + +entity biu is + Generic + ( + ICACHE_SIZE : natural := 2048; -- words + ICACHE_LINE : natural := 8; -- words + DCACHE_SIZE : natural := 2048; -- words + DCACHE_LINE : natural := 8; -- words + WRITE_FIFO_SIZE : natural := 4; -- words + WITH_TLB : boolean + ); + Port + ( + clk : in STD_LOGIC; + busy : out STD_LOGIC; + + imem_err : out STD_LOGIC; + imem_rdy : out STD_LOGIC; + imem_en : in STD_LOGIC; + imem_addr : in word_t; + imem_dout : out word_t; + dmem_err : out STD_LOGIC; + dmem_rdy : out STD_LOGIC; + dmem_en : in STD_LOGIC; + dmem_we : in STD_LOGIC; + dmem_be : in unsigned(3 downto 0); + dmem_din : in word_t; + dmem_dout : out word_t; + dmem_addr : in word_t; + + RST_I : in STD_LOGIC; + CLK_I : in STD_LOGIC; + ACK_I : in STD_LOGIC; + SRDY_I : in STD_LOGIC; + ADDR_O : out unsigned(31 downto 0); + MDAT_I : in unsigned(31 downto 0); + MDAT_O : out unsigned(31 downto 0); + WE_O : out STD_LOGIC; + SEL_O : out unsigned(3 downto 0); + CYC_O : out STD_LOGIC; + STB_O : out STD_LOGIC; + MRDY_O : out STD_LOGIC; + + ctrl_in : in biu_ctrl_in_t; + ctrl_out : out biu_ctrl_out_t + + ); +end biu; + +architecture behavior of biu is + + type bus_state_t is (init, ready, icache_bus_access, dcache_bus_access, write_bus, read_bus, read_finish); + + signal s, sn : bus_state_t; + signal bus_idle : std_logic; + + type timeout_cnt_t is range 0 to 1E5-1; + signal bus_timeout_cnt : timeout_cnt_t; + signal bus_timeout : std_logic; + + ------------------------------------------------- + signal RST : std_logic; + signal CPU_CLK : std_logic; + + -- busmaster signals + signal bus_din : word_t; + signal bus_din_rdy : std_logic; + signal bus_din_we : std_logic; + + signal bus_dout : word_t; + signal bus_dout_vld : std_logic; + signal bus_dout_re : std_logic; + + signal bus_cmd_rdy : std_logic; + signal bus_cmd_we : std_logic; + signal bus_cmd_cycle_en : std_logic; + signal bus_cmd : bm_cmd_t; + signal bus_cyc_complete : std_logic; + + signal i_bus_din_rdy : std_logic; + signal i_bus_cmd_rdy : std_logic; + signal i_bus_cmd_we : std_logic; + signal i_bus_cmd_cycle_en : std_logic; + signal i_bus_cmd : bm_cmd_t; + + signal i_tlb_ctrl_in : tlb_ctrl_in_t; + signal i_tlb_ctrl_out : tlb_ctrl_out_t; + signal i_tlb_qry_in : tlb_query_in_t; + signal i_tlb_qry_out : tlb_query_out_t; + signal i_tlb_rdy : std_logic; + signal i_cache_rdy : std_logic; + + signal d_bus_din_rdy : std_logic; + signal d_bus_cmd_rdy : std_logic; + signal d_bus_cmd_we : std_logic; + signal d_bus_cmd_cycle_en : std_logic; + signal d_bus_cmd : bm_cmd_t; + + signal d_tlb_ctrl_in : tlb_ctrl_in_t; + signal d_tlb_ctrl_out : tlb_ctrl_out_t; + signal d_tlb_qry_in : tlb_query_in_t; + signal d_tlb_qry_out : tlb_query_out_t; + signal d_tlb_rdy : std_logic; + signal d_cache_rdy : std_logic; + + signal icache_bus_gnt : std_logic; + signal dcache_bus_gnt : std_logic; + + +begin + + CPU_CLK <= clk; + RST <= RST_I; + imem_rdy <= i_cache_rdy; + dmem_rdy <= d_cache_rdy; + + ctrl_out.itlb <= i_tlb_ctrl_out; + ctrl_out.dtlb <= d_tlb_ctrl_out; + + +biu_busy_state: + process(clk) + begin + if rising_edge(clk) then + if RST_I = '1' then + busy <= '1'; + elsif (i_tlb_rdy and i_cache_rdy and d_cache_rdy and d_tlb_rdy) = '1' then + busy <= '0'; + end if; + end if; + end process; + +inst_busmaster : entity work.busmaster_async + GENERIC MAP + ( + DATA_WIDTH => 32, + FIFO_DEPTH => 16 + ) + PORT MAP + ( + -- System signals + rst => RST, + clk => CPU_CLK, + + cmd_cycle_finished => bus_cyc_complete, + cmd_cycle_en => bus_cmd_cycle_en, + + cmd_rdy => bus_cmd_rdy, + cmd_we => bus_cmd_we, + cmd_in => bus_cmd, + + din_rdy => bus_din_rdy, + din_we => bus_din_we, + din => bus_din, + + dout => bus_dout, + dout_vld => bus_dout_vld, + dout_re => bus_dout_re, + + -- JBUS Master signals + RST_I => RST_I, + CLK_I => CLK_I, + CYC_O => CYC_O, + STB_O => STB_O, + WE_O => WE_O, + SEL_O => SEL_O, + ACK_I => ACK_I, + SRDY_I => SRDY_I, + MRDY_O => MRDY_O, + ADDR_O => ADDR_O, + MDAT_I => MDAT_I, + MDAT_O => MDAT_O + ); + +inst_i_tlb : entity work.tlb + GENERIC MAP + ( + NUM_ENTRIES => TLB_NUM_ENTRIES, + CACHE_KSEG1 => false, + USE_TLB => WITH_TLB + ) + PORT MAP + ( + -- System signals + rst => RST, + clk => CPU_CLK, + ce => '1', + + rdy => i_tlb_rdy, + + ctrl_in => i_tlb_ctrl_in, + ctrl_out => i_tlb_ctrl_out, + + query_in => i_tlb_qry_in, + query_out => i_tlb_qry_out + + ); + + i_tlb_ctrl_in <= ctrl_in.itlb; + +inst_icache: entity work.icache + GENERIC MAP + ( + CACHE_SIZE => ICACHE_SIZE, + LINE_SIZE => ICACHE_LINE + ) + PORT MAP + ( + rst => RST, + clk => CPU_CLK, + + -- CPU control/data + en => imem_en, + addr => imem_addr, + dout => imem_dout, + rdy => i_cache_rdy, + + -- Cache control + ctrl_in => ctrl_in.icache, + tlb_query_in => i_tlb_qry_out, + tlb_query_out => i_tlb_qry_in, + + -- busmaster data + bus_din => bus_dout, + bus_din_rdy => i_bus_din_rdy, + bus_din_vld => bus_dout_vld, + + -- busmaster command + bus_cmd_rdy => i_bus_cmd_rdy, + bus_cmd_we => i_bus_cmd_we, + bus_cmd_cycle_en => i_bus_cmd_cycle_en, + bus_cmd_out => i_bus_cmd, + bus_cyc_complete => bus_cyc_complete + ); + + +inst_d_tlb : entity work.tlb + GENERIC MAP + ( + NUM_ENTRIES => TLB_NUM_ENTRIES, + CACHE_KSEG1 => false, + USE_TLB => WITH_TLB + ) + PORT MAP + ( + -- System signals + rst => RST, + clk => CPU_CLK, + ce => '1', + + rdy => d_tlb_rdy, + + ctrl_in => d_tlb_ctrl_in, + ctrl_out => d_tlb_ctrl_out, + + query_in => d_tlb_qry_in, + query_out => d_tlb_qry_out + + ); + + d_tlb_ctrl_in <= ctrl_in.dtlb; + +inst_dcache: entity work.dcache + GENERIC MAP + ( + CACHE_SIZE => DCACHE_SIZE, + LINE_SIZE => DCACHE_LINE, + WRITE_FIFO_SIZE => WRITE_FIFO_SIZE + ) + PORT MAP + ( + rst => RST, + clk => CPU_CLK, + + -- CPU control/data + en => dmem_en, + we => dmem_we, + addr => dmem_addr, + be => dmem_be, + din => dmem_din, + dout => dmem_dout, + rdy => d_cache_rdy, + + -- Cache control + ctrl_in => ctrl_in.dcache, + tlb_query_in => d_tlb_qry_out, + tlb_query_out => d_tlb_qry_in, + + -- busmaster data + bus_din => bus_dout, + bus_din_rdy => d_bus_din_rdy, + bus_din_vld => bus_dout_vld, + + bus_dout => bus_din, + bus_dout_vld => bus_din_we, + bus_dout_rdy => bus_din_rdy, + + -- busmaster command + bus_cmd_rdy => d_bus_cmd_rdy, + bus_cmd_we => d_bus_cmd_we, + bus_cmd_cycle_en => d_bus_cmd_cycle_en, + bus_cmd_out => d_bus_cmd, + bus_cyc_complete => bus_cyc_complete + ); + +bus_state_next: + process(clk) + begin + if rising_edge(clk) then + if RST_I = '1' then + s <= init; + else + s <= sn; + end if; + end if; + end process; + +bus_state: + process(s, i_bus_cmd_cycle_en, d_bus_cmd_cycle_en, i_bus_cmd_we, i_bus_cmd, d_bus_cmd_we, d_bus_cmd, bus_cyc_complete, i_bus_din_rdy, d_bus_din_rdy, bus_cmd_rdy) + begin + + icache_bus_gnt <= '0'; + dcache_bus_gnt <= '0'; + + i_bus_cmd_rdy <= '0'; + d_bus_cmd_rdy <= '0'; + + bus_cmd_cycle_en <= '0'; + bus_cmd_we <= '0'; + bus_cmd <= i_bus_cmd; + bus_dout_re <= '0'; + bus_idle <= '0'; + + sn <= s; + case s is + when init => + sn <= ready; + when ready => + bus_idle <= '1'; + if i_bus_cmd_cycle_en = '1' then + sn <= icache_bus_access; + elsif d_bus_cmd_cycle_en = '1' then + sn <= dcache_bus_access; + end if; + when icache_bus_access => + icache_bus_gnt <= '1'; + i_bus_cmd_rdy <= bus_cmd_rdy; + bus_cmd_cycle_en <= i_bus_cmd_cycle_en; + bus_cmd_we <= i_bus_cmd_we; + bus_dout_re <= i_bus_din_rdy; + if i_bus_cmd_cycle_en = '0' then + sn <= ready; + end if; + when dcache_bus_access => + dcache_bus_gnt <= '1'; + d_bus_cmd_rdy <= bus_cmd_rdy; + bus_cmd_cycle_en <= d_bus_cmd_cycle_en; + bus_cmd <= d_bus_cmd; + bus_cmd_we <= d_bus_cmd_we; + bus_dout_re <= d_bus_din_rdy; + if d_bus_cmd_cycle_en = '0' then + sn <= ready; + end if; + when others => + sn <= ready; + end case; + + end process; + +bus_timeout_counter: + process(clk) + begin + if rising_edge(clk) then + if bus_idle = '0' then + if bus_timeout_cnt /= 0 then + bus_timeout_cnt <= bus_timeout_cnt - 1; + else + bus_timeout <= '1'; + end if; + else + bus_timeout_cnt <= timeout_cnt_t'high; + bus_timeout <= '0'; + end if; + end if; + end process; + +bus_err: + process(clk) + begin + if rising_edge(clk) then + if RST_I = '1' then + imem_err <= '0'; + dmem_err <= '0'; + elsif bus_timeout = '1' then + imem_err <= icache_bus_gnt; + dmem_err <= dcache_bus_gnt; + end if; + end if; + end process; + +------------------------------------------------------------------- + +end behavior; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/mips_cam.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_cam.vhd new file mode 100644 index 0000000..0ae8736 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_cam.vhd @@ -0,0 +1,319 @@ +LIBRARY IEEE; +USE IEEE.MATH_REAL.ALL; +USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; + +library work; +use work.mips_types.all; +use work.mips_util_pkg.all; + +ENTITY cam IS + Generic + ( + NUM_ENTRIES : natural := 32; + DATA_WIDTH : natural := 20; + CAM_RAM_MAX_WIDTH : natural := 15 + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + clk_rd : in STD_LOGIC; + + rdy : out STD_LOGIC; + + re : in STD_LOGIC; + we : in STD_LOGIC; + vld : in STD_LOGIC; + addr : in unsigned (lg2(NUM_ENTRIES)-1 downto 0); + tag_wr : in unsigned (DATA_WIDTH-1 downto 0); + + tag_rd : in unsigned (DATA_WIDTH-1 downto 0); + hit : out unsigned (NUM_ENTRIES-1 downto 0); + hit_vld : out STD_LOGIC + + ); +END cam; + +ARCHITECTURE behavior OF cam IS + + type debug_t is record + num_cam_rams : integer; + cam_ram_data_width : integer; + cam_ram_addr_width : integer; + end record; + + signal debug : debug_t; + + constant NUM_BLOCKS : integer := integer(real(DATA_WIDTH)/real(CAM_RAM_MAX_WIDTH-lg2(NUM_ENTRIES))); + constant BLOCK_ADDR_WIDTH : integer := CAM_RAM_MAX_WIDTH - lg2(NUM_ENTRIES); + + type state_t is (init, ready, clear); + signal state : state_t; + signal state_next : state_t; + + type pipe_stage_t is record + re : std_logic; + we : std_logic; + vld : std_logic; + addr : unsigned (lg2(NUM_ENTRIES)-1 downto 0); + data : unsigned (DATA_WIDTH-1 downto 0); + vld_cam : unsigned (NUM_ENTRIES-1 downto 0); + end record; + + signal pipe0 : pipe_stage_t; + signal pipe1 : pipe_stage_t; + signal pipe2 : pipe_stage_t; + signal pipe3 : pipe_stage_t; + signal pipe0_rd : pipe_stage_t; + signal pipe1_rd : pipe_stage_t; + signal pipe2_rd : pipe_stage_t; + signal pipe3_rd : pipe_stage_t; + signal busy : std_logic; + signal vld_cam : unsigned (NUM_ENTRIES-1 downto 0); + + signal cam_ram_re : std_logic; + signal cam_ram_we : std_logic; + + type cam_ram_array_t is array (0 to NUM_BLOCKS-1) of unsigned (NUM_ENTRIES-1 downto 0); + signal cam_ram_din : cam_ram_array_t; + signal cam_ram_dout : cam_ram_array_t; + signal cam_ram_result : unsigned (NUM_ENTRIES-1 downto 0); + + signal cam_ram_addr_wr : unsigned (DATA_WIDTH-1 downto 0); + signal cam_ram_addr_rd : unsigned (DATA_WIDTH-1 downto 0); + + signal track_ram_re : std_logic; + signal track_ram_we : std_logic; + signal track_ram_addr_wr : unsigned (lg2(NUM_ENTRIES)-1 downto 0); + signal track_ram_addr_rd : unsigned (lg2(NUM_ENTRIES)-1 downto 0); + signal track_ram_din : unsigned (DATA_WIDTH downto 0); + signal track_ram_dout : unsigned (DATA_WIDTH downto 0); + + signal clear_data : unsigned (DATA_WIDTH-1 downto 0); + signal clear_addr : unsigned (BLOCK_ADDR_WIDTH-1 downto 0); + signal clear_count_en : std_logic; + signal clear_count_rdy : std_logic; + signal over_write : std_logic; + + +begin + + over_write <= track_ram_dout(DATA_WIDTH) and pipe1.we; + rdy <= not busy; + hit_vld <= pipe1.re; + + debug.num_cam_rams <= NUM_BLOCKS; + debug.cam_ram_addr_width <= BLOCK_ADDR_WIDTH; + debug.cam_ram_data_width <= NUM_ENTRIES; + + track_ram_re <= '1'; + track_ram_we <= clear_count_en or pipe1.we; + track_ram_addr_rd <= pipe0.addr; + track_ram_addr_wr <= clear_data(lg2(NUM_ENTRIES)-1 downto 0) when clear_count_en = '1' else pipe1.addr; + track_ram_din <= (others => '0') when clear_count_en = '1' else (pipe1.vld & pipe1.data); + + cam_ram_re <= re when pipe1_rd.we = '0' else not busy; + cam_ram_we <= clear_count_en or (pipe2.we and pipe2.vld) or over_write; + cam_ram_addr_rd <= tag_rd when pipe1_rd.we = '0' else pipe1_rd.data; -- clk_rd same edge of clk +-- cam_ram_addr_rd <= tag_rd when pipe2_rd.we = '0' else pipe2_rd.data; -- clk_rd opposite edge of clk + cam_ram_addr_wr <= clear_data when clear_count_en = '1' else track_ram_dout(DATA_WIDTH-1 downto 0) when over_write = '1' else pipe2.data; + +combine_cam_ram_din: + process(clear_count_en, over_write, pipe2, cam_ram_dout) + begin + for i in 0 to NUM_BLOCKS-1 loop + if clear_count_en = '1' or over_write = '1' then + cam_ram_din(i) <= (others => '0'); + else + cam_ram_din(i) <= (((NUM_ENTRIES-1 downto 0 => pipe2.vld) and pipe2.vld_cam) xor cam_ram_dout(i)); + end if; + end loop; + end process; + +combine_clear_addr: + process(clear_addr) + begin + for i in 0 to NUM_BLOCKS-1 loop + clear_data((i+1)*BLOCK_ADDR_WIDTH-1 downto i*BLOCK_ADDR_WIDTH) <= clear_addr; + end loop; + end process; + +combine_cam_ram_result: + process(cam_ram_dout) + variable result : unsigned (NUM_ENTRIES-1 downto 0); + begin + result := (others => '0'); + + for i in 0 to NUM_BLOCKS-1 loop + result := result or cam_ram_dout(i); + end loop; + + cam_ram_result <= result; + end process; + + pipe0.re <= re and not busy; + pipe0.vld <= vld; + pipe0.we <= we and not busy; + pipe0.addr <= addr; + pipe0.data <= tag_wr; + pipe0.vld_cam <= vld_cam; + +pipe_line_wr: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + pipe3.we <= '0'; + pipe2.we <= '0'; + pipe1.we <= '0'; + else + pipe3 <= pipe2; + pipe2 <= pipe1; + pipe1 <= pipe0; + end if; + end if; + end process; + + pipe0_rd <= pipe0; +pipe_line_rd: + process(clk_rd) + begin + if rising_edge(clk_rd) then + if rst = '1' then + pipe3_rd.we <= '0'; + pipe2_rd.we <= '0'; + pipe1_rd.we <= '0'; + else + pipe3_rd <= pipe2_rd; + pipe2_rd <= pipe1_rd; + pipe1_rd <= pipe0_rd; + end if; + end if; + end process; + +combine_cam_hit: + process(cam_ram_dout, clear_count_en) + variable result : unsigned (NUM_ENTRIES-1 downto 0); + begin + result := (others => not clear_count_en); + + for i in 0 to NUM_BLOCKS-1 loop + result := result and cam_ram_dout(i); + end loop; + + hit <= result; + end process; + +vld_decode: +process(vld, addr) + begin + vld_cam <= (others => '0'); + vld_cam(to_integer(addr)) <= vld; + end process; + + +inst_track_ram: entity work.dpram_1w1r2c_ra + GENERIC MAP + ( + addr_width => lg2(NUM_ENTRIES), + data_width => DATA_WIDTH+1 + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + en_a => '1', + en_b => '1', + we_a => track_ram_we, + addr_a => track_ram_addr_wr, + addr_b => track_ram_addr_rd, + din_a => track_ram_din, + dout_b => track_ram_dout + ); + +gen_cam_ram: +for i in 0 to NUM_BLOCKS-1 generate + + inst_cam_ram: entity work.dpram_1w1r2c_ra + GENERIC MAP + ( + addr_width => BLOCK_ADDR_WIDTH, + data_width => NUM_ENTRIES + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk_rd, + en_a => '1', + en_b => cam_ram_re, + we_a => cam_ram_we, + addr_a => cam_ram_addr_wr((i+1)*BLOCK_ADDR_WIDTH-1 downto i*BLOCK_ADDR_WIDTH), + addr_b => cam_ram_addr_rd((i+1)*BLOCK_ADDR_WIDTH-1 downto i*BLOCK_ADDR_WIDTH), + din_a => cam_ram_din(i), + dout_b => cam_ram_dout(i) + ); + +end generate; + +cam_state_next: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + state <= init; + else + state <= state_next; + end if; + end if; + end process; + +cam_state: + process(state, clear_count_rdy, over_write) + begin + busy <= over_write; + clear_count_en <= '0'; + + state_next <= state; + + case state is + + when init => + busy <= '1'; + state_next <= clear; + + when clear => + busy <= '1'; + clear_count_en <= '1'; + if clear_count_rdy = '1' then + state_next <= ready; + end if; + + when ready => + + when others => + state_next <= ready; + + end case; + + end process; + +clear_counter: + process(clk) + begin + if rising_edge(clk) then + if clear_count_en = '0' then + clear_count_rdy <= '0'; + clear_addr <= (BLOCK_ADDR_WIDTH-1 downto 0 => '1'); + else + if clear_addr /= (BLOCK_ADDR_WIDTH-1 downto 0 => '0') then + clear_addr <= clear_addr - 1; + else + clear_count_rdy <= '1'; + end if; + end if; + end if; + end process; + +------------------------------------------------------------------ +end behavior; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/mips_cam2.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_cam2.vhd new file mode 100644 index 0000000..9173513 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_cam2.vhd @@ -0,0 +1,329 @@ +LIBRARY IEEE; +USE IEEE.MATH_REAL.ALL; +USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; + +library work; +use work.mips_types.all; +use work.mips_util_pkg.all; + +ENTITY cam2 IS + Generic + ( + NUM_ENTRIES : natural := 32; + ENTRY_WIDTH : natural := 32; + DATA_WIDTH : natural := 20; + REGISTERED_RTAG : boolean := true; + REGISTERED_RESULT : boolean := false; + CAM_RAM_MAX_WIDTH : natural := 8 + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + + rdy : out STD_LOGIC; + + re : in STD_LOGIC; + we : in STD_LOGIC; + vld : in STD_LOGIC; + addr : in unsigned (lg2(NUM_ENTRIES)-1 downto 0); + entry_din : in unsigned (ENTRY_WIDTH-1 downto 0); + entry_dout : out unsigned (ENTRY_WIDTH-1 downto 0); + + tag_wr : in unsigned (DATA_WIDTH-1 downto 0); + tag_rd : in unsigned (DATA_WIDTH-1 downto 0); + + hit_vld : out STD_LOGIC + + ); +END cam2; + +ARCHITECTURE behavior OF cam2 IS + + type debug_t is record + num_cam_rams : integer; + cam_ram_data_width : integer; + cam_ram_addr_width : integer; + end record; + + signal debug : debug_t; + + constant NUM_BLOCKS : integer := integer(real(2*DATA_WIDTH)/real(CAM_RAM_MAX_WIDTH))-1; + constant BLOCK_ADDR_WIDTH : integer := CAM_RAM_MAX_WIDTH; + + type state_t is (init, ready, clear); + signal state : state_t; + signal state_next : state_t; + + type pipe_stage_t is record + re : std_logic; + we : std_logic; + vld : std_logic; + addr : unsigned (lg2(NUM_ENTRIES)-1 downto 0); + data : unsigned (DATA_WIDTH-1 downto 0); + entry_data : unsigned (ENTRY_WIDTH downto 0); + end record; + + signal read_tag : unsigned (DATA_WIDTH-1 downto 0); + signal read_en : std_logic; + + signal pipe0 : pipe_stage_t; + signal pipe1 : pipe_stage_t; + signal pipe2 : pipe_stage_t; + signal pipe3 : pipe_stage_t; + signal pipe0_rd : pipe_stage_t; + signal pipe1_rd : pipe_stage_t; + signal pipe2_rd : pipe_stage_t; + signal pipe3_rd : pipe_stage_t; + signal busy : std_logic; + + signal cam_ram_re : std_logic; + signal cam_ram_we : std_logic; + signal cam_ram_addr_wr : unsigned (DATA_WIDTH-1 downto 0); + signal cam_ram_addr_rd : unsigned (DATA_WIDTH-1 downto 0); + + signal track_ram_re : std_logic; + signal track_ram_we : std_logic; + signal track_ram_addr_wr : unsigned (lg2(NUM_ENTRIES)-1 downto 0); + signal track_ram_addr_rd : unsigned (lg2(NUM_ENTRIES)-1 downto 0); + signal track_ram_din : unsigned (DATA_WIDTH downto 0); + signal track_ram_dout : unsigned (DATA_WIDTH downto 0); + + signal clear_addr : unsigned (BLOCK_ADDR_WIDTH-1 downto 0); + signal clear_count_en : std_logic; + signal clear_count_rdy : std_logic; + signal over_write : std_logic; + + type cam_stage_addr_t is array(0 to NUM_BLOCKS-1) of unsigned (CAM_RAM_MAX_WIDTH-1 downto 0); + signal cam_stage_raddr : cam_stage_addr_t; + signal cam_stage_waddr : cam_stage_addr_t; + + type cam_stage_data_t is array(0 to NUM_BLOCKS-1) of unsigned (CAM_RAM_MAX_WIDTH/2-1 downto 0); + signal cam_stage_din : cam_stage_data_t; + signal cam_stage_dout : cam_stage_data_t; + + signal final_stage_we : std_logic; + signal final_stage_din : unsigned (ENTRY_WIDTH downto 0); + signal final_stage_dout : unsigned (ENTRY_WIDTH downto 0); + signal final_stage_wptr : unsigned (CAM_RAM_MAX_WIDTH-1 downto 0); + signal final_stage_rptr : unsigned (CAM_RAM_MAX_WIDTH-1 downto 0); + +begin + + + + rdy <= not busy; + over_write <= track_ram_dout(DATA_WIDTH) and pipe1.we; + + -------------------------------------------------------- + final_stage_we <= clear_count_en or (pipe2.we and pipe2.vld) or over_write; + final_stage_wptr <= clear_addr when clear_count_en = '1' else cam_stage_waddr(NUM_BLOCKS-1); + final_stage_rptr <= cam_stage_raddr(NUM_BLOCKS-1); + final_stage_din <= (others => '0') when (clear_count_en or over_write) = '1' else pipe2.entry_data; + + cam_ram_we <= clear_count_en or (pipe2.we and pipe2.vld) or over_write; + cam_ram_re <= not busy; + cam_ram_addr_rd <= read_tag when pipe1_rd.we = '0' else pipe1_rd.data; -- clk_rd same edge of clk + cam_ram_addr_wr <= track_ram_dout(DATA_WIDTH-1 downto 0) when over_write = '1' else pipe2.data; + + track_ram_re <= '1'; + track_ram_we <= clear_count_en or pipe1.we; + track_ram_addr_rd <= pipe0.addr; + track_ram_addr_wr <= clear_addr(lg2(NUM_ENTRIES)-1 downto 0) when clear_count_en = '1' else pipe1.addr; + track_ram_din <= (others => '0') when clear_count_en = '1' else (pipe1.vld & pipe1.data); + + pipe0.re <= read_en and not busy; + pipe0.vld <= vld; + pipe0.we <= we and not busy; + pipe0.addr <= addr; + pipe0.data <= tag_wr; + pipe0.entry_data <= vld & entry_din; + + +gen_registered_rtag: +if REGISTERED_RTAG = true generate +registered_rtag: + process(clk) + begin + if rising_edge(clk) then + read_en <= re; + if re = '1' then + read_tag <= tag_rd; + end if; + end if; + end process; + +end generate; +gen_non_registered_rtag: +if REGISTERED_RTAG = false generate + read_en <= re; + read_tag <= read_tag; +end generate; + +gen_registered_output: +if REGISTERED_RESULT = true generate +registered_output: + process(clk) + begin + if rising_edge(clk) then + if read_en = '1' then + entry_dout <= final_stage_dout(ENTRY_WIDTH-1 downto 0); + hit_vld <= final_stage_dout(ENTRY_WIDTH); + end if; + end if; + end process; +end generate; +gen_non_registered_output: +if REGISTERED_RESULT = false generate + entry_dout <= final_stage_dout(ENTRY_WIDTH-1 downto 0); + hit_vld <= final_stage_dout(ENTRY_WIDTH); +end generate; + +pipe_line_wr: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + pipe3.we <= '0'; + pipe2.we <= '0'; + pipe1.we <= '0'; + else + pipe3 <= pipe2; + pipe2 <= pipe1; + pipe1 <= pipe0; + end if; + end if; + end process; + +inst_track_ram: entity work.dpram_1w1r2c_ra + GENERIC MAP + ( + addr_width => lg2(NUM_ENTRIES), + data_width => DATA_WIDTH+1 + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + en_a => '1', + en_b => '1', + we_a => track_ram_we, + addr_a => track_ram_addr_wr, + addr_b => track_ram_addr_rd, + din_a => track_ram_din, + dout_b => track_ram_dout + ); + + -------------------------------------------------------- + cam_stage_raddr(0) <= read_tag(DATA_WIDTH-1 downto DATA_WIDTH-CAM_RAM_MAX_WIDTH); + cam_stage_waddr(0) <= cam_ram_addr_wr(DATA_WIDTH-1 downto DATA_WIDTH-CAM_RAM_MAX_WIDTH); + cam_stage_din(0) <= cam_ram_addr_wr(DATA_WIDTH-CAM_RAM_MAX_WIDTH/2-1 downto DATA_WIDTH-CAM_RAM_MAX_WIDTH); + +gen_cam_stage_addresses: +for i in 1 to NUM_BLOCKS-1 generate + cam_stage_raddr(i) <= to_mips_01(cam_stage_dout(i-1)) & read_tag(DATA_WIDTH-(i+1)*CAM_RAM_MAX_WIDTH/2-1 downto DATA_WIDTH-(i+2)*CAM_RAM_MAX_WIDTH/2); + cam_stage_waddr(i) <= cam_ram_addr_wr(DATA_WIDTH-i*CAM_RAM_MAX_WIDTH/2-1 downto DATA_WIDTH-(i+2)*CAM_RAM_MAX_WIDTH/2); + cam_stage_din(i) <= cam_ram_addr_wr(DATA_WIDTH-(i+1)*CAM_RAM_MAX_WIDTH/2-1 downto DATA_WIDTH-(i+2)*CAM_RAM_MAX_WIDTH/2); +end generate; + +gen_cam_stages: +for i in 0 to NUM_BLOCKS-2 generate + +inst_cam_stage: entity work.dpram_1w1r1wc + GENERIC MAP + ( + addr_width => CAM_RAM_MAX_WIDTH, + data_width => CAM_RAM_MAX_WIDTH/2 + ) + PORT MAP + ( + clk_w => clk, + we => cam_ram_we, + wptr => cam_stage_waddr(i), + rptr => cam_stage_raddr(i), + din => cam_stage_din(i), + dout => cam_stage_dout(i) + ); + +end generate; + +inst_cam_final_stage: entity work.dpram_1w1r1wc + GENERIC MAP + ( + addr_width => CAM_RAM_MAX_WIDTH, + data_width => 1+ENTRY_WIDTH + ) + PORT MAP + ( + clk_w => clk, + we => final_stage_we, + wptr => final_stage_wptr, + rptr => final_stage_rptr, + din => final_stage_din, + dout => final_stage_dout + ); + +------------------------------------------------------------------ +cam_state_next: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + state <= init; + else + state <= state_next; + end if; + end if; + end process; + +cam_state: + process(state, clear_count_rdy, over_write) + begin + busy <= over_write; + clear_count_en <= '0'; + + state_next <= state; + + case state is + + when init => + busy <= '1'; + state_next <= clear; + + when clear => + busy <= '1'; + clear_count_en <= '1'; + if clear_count_rdy = '1' then + state_next <= ready; + end if; + + when ready => + + when others => + state_next <= ready; + + end case; + + end process; + +------------------------------------------------------------------ +clear_counter: + process(clk) + begin + if rising_edge(clk) then + if clear_count_en = '0' then + clear_count_rdy <= '0'; + clear_addr <= (BLOCK_ADDR_WIDTH-1 downto 0 => '1'); + else + if clear_addr /= (BLOCK_ADDR_WIDTH-1 downto 0 => '0') then + clear_addr <= clear_addr - 1; + else + clear_count_rdy <= '1'; + end if; + end if; + end if; + end process; + +------------------------------------------------------------------ +end behavior; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/mips_cop.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_cop.vhd new file mode 100644 index 0000000..9352e8c --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_cop.vhd @@ -0,0 +1,867 @@ +-------------------------------------------------------------------------- +-- Project: JIPS, a portable 32-bit RISC CPU written in VHDL +-- This file: The arithmetic logic unit +-- +-- 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; + +use work.mips_types.all; +use work.mips_util_pkg.all; +use work.mips_instr.all; + +entity cop is + Generic + ( + icache_size : natural := 2048; -- words + icache_line : natural := 8; -- words + dcache_size : natural := 2048; -- words + dcache_line : natural := 8 -- words + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + eb : in STD_LOGIC; + int : in unsigned(5 downto 0); + nmi : in STD_LOGIC; + ir_en : in STD_LOGIC; + ir : in word_t; + ctrl_in : in cop0_ctrl_in_t; + ctrl_out : out cop0_ctrl_out_t; + din : in word_t; + dout : out word_t + ); +end cop; + +architecture Behavioral of cop is + + type cp0_regs_t is record + index : word_t; + random : word_t; + entry_lo : word_t; + config : word_t; + context : word_t; + pagemask : word_t; + wired : word_t; + badvaddr : word_t; + entry_hi : word_t; + status : word_t; + cause : word_t; + epc : word_t; + prid : word_t; + test : word_t; + end record; + + signal regs : cp0_regs_t; + constant COP0_INDEX : natural := 0; + constant COP0_RANDOM : natural := 1; + constant COP0_ENTRY_LO : natural := 2; + constant COP0_CONFIG : natural := 3; + constant COP0_CONTEXT : natural := 4; + constant COP0_PAGEMASK : natural := 5; + constant COP0_WIRED : natural := 6; + constant COP0_07 : natural := 7; + constant COP0_BADVADDR : natural := 8; + constant COP0_09 : natural := 9; + constant COP0_ENTRY_HI : natural := 10; + constant COP0_0B : natural := 11; + constant COP0_STATUS : natural := 12; + constant COP0_CAUSE : natural := 13; + constant COP0_EPC : natural := 14; + constant COP0_PRID : natural := 15; + constant COP0_10 : natural := 16; + constant COP0_11 : natural := 17; + constant COP0_12 : natural := 18; + constant COP0_13 : natural := 19; + constant COP0_14 : natural := 20; + constant COP0_15 : natural := 21; + constant COP0_16 : natural := 22; + constant COP0_17 : natural := 23; + constant COP0_18 : natural := 24; + constant COP0_19 : natural := 25; + constant COP0_1A : natural := 26; + constant COP0_1B : natural := 27; + constant COP0_1C : natural := 28; + constant COP0_1D : natural := 29; + constant COP0_1E : natural := 30; + constant COP0_1F : natural := 31; + + signal EX_reg_we : unsigned (31 downto 0); + signal index : unsigned (lg2(TLB_NUM_ENTRIES)-1 downto 0); + signal random : unsigned (lg2(TLB_NUM_ENTRIES)-1 downto 0); + signal PTEBase : unsigned (10 downto 0); + signal itlb_exc : STD_LOGIC; + signal dtlb_exc : STD_LOGIC; + signal itlb_entry_hi : tlb_entry_hi_t; + signal itlb_entry_lo : tlb_entry_lo_t; + signal dtlb_entry_hi : tlb_entry_hi_t; + signal dtlb_entry_lo : tlb_entry_lo_t; + signal entry_hi : tlb_entry_hi_t; + signal entry_lo : tlb_entry_lo_t; + signal exc_code : unsigned(4 downto 0); + signal tlb_sel : STD_LOGIC; + signal epc_reg_we : STD_LOGIC; + signal code_reg_we : STD_LOGIC; + signal bd : STD_LOGIC; + signal sw_int : unsigned(1 downto 0); + signal ip : unsigned(7 downto 0); + signal im : unsigned(7 downto 0); + signal status_save : STD_LOGIC; + signal status_rest : STD_LOGIC; + signal eflags : exc_flags_t; + signal EB_reg : STD_LOGIC; + signal icache_info : unsigned(7 downto 0); + signal dcache_info : unsigned(7 downto 0); + signal reg_rptr : reg_ptr_t; + signal reg_wptr : reg_ptr_t; + signal inject_exc : STD_LOGIC; + signal latch_vect_en : STD_LOGIC; + signal int_nmi : STD_LOGIC; + signal nmi_asserted : std_logic; + + type cop_pipe_t is record + opc : opcode_t; + RFE : std_logic; + CO : std_logic; + reg_ptr : reg_ptr_t; + rs : reg_ptr_t; + rd : reg_ptr_t; + rt : reg_ptr_t; + func : func_t; + we : std_logic; + re : std_logic; + cs : std_logic; + TLBP : std_logic; + TLBR : std_logic; + TLBWI : std_logic; + TLBWR : std_logic; + end record; + + signal cop_pipe_ID : cop_pipe_t; + signal cop_pipe_EX : cop_pipe_t; + + function eval_int(ip : unsigned) return STD_LOGIC is + variable result : STD_LOGIC; + begin + result := '0'; + for i in ip'range loop + result := result or ip(i); + end loop; + + return result; + end eval_int; + + type exc_state_t is (exc_idle, exc_pipe_flush, exc_commit, exc_inject, exc_enter, exc_leave, exc_left); + signal exc_state, exc_staten : exc_state_t; + + function to_tlb_entry_lo(x : word_t) return tlb_entry_lo_t is + variable result : tlb_entry_lo_t; + begin + result.PFN := x(31 downto lg2(TLB_PAGE_SIZE)); + result.N := x(11); + result.D := x(10); + result.V := x(9); + result.G := x(8); + + return result; + + end to_tlb_entry_lo; + + function to_tlb_entry_hi(x : word_t) return tlb_entry_hi_t is + variable result : tlb_entry_hi_t; + begin + result.VPN := x(31 downto lg2(TLB_PAGE_SIZE)); + result.ASID := x(lg2(TLB_PAGE_SIZE)-1 downto lg2(TLB_PAGE_SIZE)-lg2(TLB_NUM_ASIDS)); + + return result; + + end to_tlb_entry_hi; + + function to_tlb_entry_hi(x : tlb_entry_hi_t; va : word_t) return tlb_entry_hi_t is + variable result : tlb_entry_hi_t; + begin + result.VPN := va(31 downto lg2(TLB_PAGE_SIZE)); + result.ASID := x.ASID; + + return result; + + end to_tlb_entry_hi; + + function to_reg(x : tlb_entry_lo_t) return word_t is + variable result : word_t; + begin + result(31 downto lg2(TLB_PAGE_SIZE)) := x.PFN; + result(11) := x.N; + result(10) := x.D; + result(9) := x.V; + result(8) := x.G; + result(7 downto 0) := (others => '0'); + + return result; + + end to_reg; + + function to_reg(x : tlb_entry_hi_t) return word_t is + variable result : word_t; + begin + result(31 downto lg2(TLB_PAGE_SIZE)) := x.VPN; + result(lg2(TLB_PAGE_SIZE)-1 downto lg2(TLB_PAGE_SIZE)-lg2(TLB_NUM_ASIDS)) := x.ASID; + result(lg2(TLB_PAGE_SIZE)-lg2(TLB_NUM_ASIDS)-1 downto 0) := (others => '0'); + + return result; + + end to_reg; + +-------------------------------------------------------------------------- +begin + + -- Pass to pipeline TLB status + ctrl_out.pipe.ITLB <= ctrl_in.itlb.stat; + ctrl_out.pipe.DTLB <= ctrl_in.dtlb.stat; + + -- I-TLB stuff + ctrl_out.itlb.entry_re <= '1'; -- cop_pipe_ID.TLBR and not tlb_sel; -- on tlbr + ctrl_out.itlb.entry_we <= (cop_pipe_EX.TLBWI or cop_pipe_EX.TLBWR) and not tlb_sel; -- on tlbwi or tlbwr + ctrl_out.itlb.entry_rd_idx <= index; + ctrl_out.itlb.entry_wr_idx <= random when cop_pipe_EX.TLBWR = '1' else index; + ctrl_out.itlb.entry_lo <= itlb_entry_lo; + ctrl_out.itlb.entry_hi <= itlb_entry_hi; + + -- D-TLB stuff + ctrl_out.dtlb.entry_re <= '1'; -- cop_pipe_ID.TLBR and tlb_sel; -- on tlbr + ctrl_out.dtlb.entry_we <= (cop_pipe_EX.TLBWI or cop_pipe_EX.TLBWR) and tlb_sel; -- on tlbwi or tlbwr + ctrl_out.dtlb.entry_rd_idx <= index; + ctrl_out.dtlb.entry_wr_idx <= random when cop_pipe_EX.TLBWR = '1' else index; + ctrl_out.dtlb.entry_lo <= dtlb_entry_lo; + ctrl_out.dtlb.entry_hi <= dtlb_entry_hi; + + cop_pipe_ID.cs <= ir_en; + cop_pipe_ID.opc <= extract_opc(ir); + cop_pipe_ID.rs <= extract_rs(ir); + cop_pipe_ID.rd <= extract_rd(ir); + cop_pipe_ID.rt <= extract_rt(ir); + cop_pipe_ID.func <= extract_func(ir); + cop_pipe_ID.CO <= ir(25); + cop_pipe_ID.re <= cop_pipe_ID.cs when (cop_pipe_ID.opc = "111000" or (cop_pipe_ID.opc = "010000" and cop_pipe_ID.rs = "00000")) else '0'; + cop_pipe_ID.we <= cop_pipe_ID.cs when (cop_pipe_ID.opc = "110000" or (cop_pipe_ID.opc = "010000" and cop_pipe_ID.rs = "00100")) else '0'; + cop_pipe_ID.RFE <= cop_pipe_ID.cs when (cop_pipe_ID.CO = '1' and cop_pipe_ID.func = "010000") else '0'; + cop_pipe_ID.TLBP <= cop_pipe_ID.cs when (cop_pipe_ID.CO = '1' and cop_pipe_ID.func = "001000") else '0'; + cop_pipe_ID.TLBR <= cop_pipe_ID.cs when (cop_pipe_ID.CO = '1' and cop_pipe_ID.func = "000001") else '0'; + cop_pipe_ID.TLBWI <= cop_pipe_ID.cs when (cop_pipe_ID.CO = '1' and cop_pipe_ID.func = "000010") else '0'; + cop_pipe_ID.TLBWR <= cop_pipe_ID.cs when (cop_pipe_ID.CO = '1' and cop_pipe_ID.func = "000110") else '0'; + ctrl_out.pipe.EB <= EB_reg xor (regs.status(25) and not regs.status(1)); + ctrl_out.pipe.user_mode <= not regs.status(1); + ctrl_out.pipe.exc_inject <= inject_exc or rst; + cop_pipe_ID.reg_ptr <= cop_pipe_ID.rd when cop_pipe_ID.opc = "010000" else cop_pipe_ID.rt; + + eflags.Ov <= ctrl_in.pipe.events.alu_ovf or ctrl_in.pipe.events.alu_uvf; + eflags.DAdEL <= ctrl_in.pipe.events.data_load_err; + eflags.DAdES <= ctrl_in.pipe.events.data_store_err; + eflags.IAdEL <= ctrl_in.pipe.events.inst_load_err; + eflags.IAdEK <= ctrl_in.pipe.events.inst_priv_addr and not regs.status(1); + eflags.Sys <= ctrl_in.pipe.events.syscall; + eflags.Bp <= ctrl_in.pipe.events.break; + eflags.RI <= ctrl_in.pipe.events.illegal; + eflags.Int <= ctrl_in.pipe.events.int; + eflags.ITLB_MOD <= ctrl_in.pipe.events.ITLB_MOD; + eflags.ITLB_LOAD <= ctrl_in.pipe.events.ITLB_LOAD; + eflags.ITLB_STORE <= ctrl_in.pipe.events.ITLB_STORE; + eflags.DTLB_MOD <= ctrl_in.pipe.events.DTLB_MOD; + eflags.DTLB_LOAD <= ctrl_in.pipe.events.DTLB_LOAD; + eflags.DTLB_STORE <= ctrl_in.pipe.events.DTLB_STORE; + + im <= regs.status(15 downto 8); + entry_lo <= itlb_entry_lo when tlb_sel = '0' else dtlb_entry_lo; + entry_hi <= itlb_entry_hi when tlb_sel = '0' else dtlb_entry_hi; + icache_info <= "00" & to_unsigned(lg2(icache_line), 3) & to_unsigned(lg2(icache_size)-8, 3); + dcache_info <= "00" & to_unsigned(lg2(dcache_line), 3) & to_unsigned(lg2(dcache_size)-8, 3); + + regs.index <= '0' & '0' & (29 downto 8+lg2(TLB_NUM_ENTRIES) => '0') & index & (7 downto 0 => '0'); + regs.random <= (31 downto 8+lg2(TLB_NUM_ENTRIES) => '0') & random & (7 downto 0 => '0'); + regs.entry_lo <= to_reg(entry_lo); + regs.context <= PTEBase & regs.badvaddr(31 downto 13) & "00"; + regs.entry_hi <= to_reg(entry_hi); + regs.cause <= bd & (30 downto 16 => '0') & ip & '0' & exc_code & "00"; + regs.prid <= dcache_info & icache_info & X"02" & to_unsigned(REVISION ,8); + + tlb_sel <= regs.config(0); + +-- COP Register read +cop_register_read: + process(reg_rptr, regs) + variable reg : word_t; + variable reg_num : natural; + begin + + reg_num := to_integer(reg_rptr); + + case reg_num is + + when COP0_INDEX => -- 00: index + reg := regs.index; + + when COP0_RANDOM => -- 01: random + reg := regs.random; + + when COP0_ENTRY_LO => -- 02: entry_lo + reg := regs.entry_lo; + + when COP0_CONFIG => -- 03: config + reg := regs.config; + + when COP0_CONTEXT => -- 04: Context + reg := regs.context; + + -- 05: PageMask + + when COP0_WIRED => -- 06: wired + reg := regs.wired; + + -- 07: Reserved + + when COP0_BADVADDR => -- 08: BadVAddr + reg := regs.badvaddr; + + -- 09: Reserved + + when COP0_ENTRY_HI => -- 0A: entry_hi + reg := regs.entry_hi; + + -- 0B: Reserved + + when COP0_STATUS => -- 0C: Status + reg := regs.status; + + when COP0_CAUSE => -- 0D: Cause + reg := regs.cause; + + when COP0_EPC => -- 0E: EPC (Exception Program Counter) + reg := regs.epc; + + when COP0_PRID => -- 0F: PRId (Processor Revision Register) + reg := regs.prid; + + -- 10 .. 1E : reserved + + when COP0_1F => -- 1F: test + reg := regs.test; + + when others => + reg := (others => '-'); + + end case; + + dout <= reg after 2 ns; + + end process; + +-- Cop pipeline +cop_pipe: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + cop_pipe_EX.cs <= '0'; + cop_pipe_EX.we <= '0'; + cop_pipe_EX.re <= '0'; + elsif ctrl_in.pipe.sdu.stall_all = '0' then + cop_pipe_EX <= cop_pipe_ID; + if cop_pipe_ID.we = '1' then + reg_wptr <= cop_pipe_ID.reg_ptr; + end if; + if cop_pipe_ID.re = '1' then + reg_rptr <= cop_pipe_ID.reg_ptr; + end if; + end if; + end if; + end process; + +-- COP Register write strobe generation +cop_we_gen: + process(clk) + begin + if rising_edge(clk) then +-- if ctrl_in.pipe.sdu.stall_all = '0' then + EX_reg_we <= (others => '0'); + if cop_pipe_ID.we = '1' then + EX_reg_we(to_integer(cop_pipe_ID.reg_ptr)) <= '1'; + end if; +-- end if; + end if; + end process; + + +-- NMI/RST transition detector +proc_nmi_transition: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + int_nmi <= '0'; + nmi_asserted <= '0'; + elsif latch_vect_en = '1' then + int_nmi <= '0'; + elsif nmi_asserted = '1' then + if nmi = '0' then + nmi_asserted <= '0'; + int_nmi <= '1'; + end if; + elsif nmi = '1' then + nmi_asserted <= '1'; + end if; + end if; + end process; + +-- Endian switch sampled at hard reset +sample_endian: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + EB_reg <= eb; + end if; + end if; + end process; + +-- Main exception FSM next +proc_exc_state_next: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + exc_state <= exc_idle; + else + exc_state <= exc_staten; + end if; + end if; + end process; + +-- Main exception FSM +proc_exc_state: + process(exc_state, ctrl_in, cop_pipe_ID) + begin + + ctrl_out.pipe.exc_commit <= '0'; + ctrl_out.pipe.exc_pending <= '0'; + ctrl_out.pipe.exc_exit <= '0'; + epc_reg_we <= '0'; + code_reg_we <= '0'; + status_save <= '0'; + status_rest <= '0'; + inject_exc <= '0'; + latch_vect_en <= '0'; + + exc_staten <= exc_state; + + case exc_state is + + when exc_idle => + if cop_pipe_ID.RFE = '1' then + exc_staten <= exc_leave; + ctrl_out.pipe.exc_exit <= '1'; + ctrl_out.pipe.exc_commit <= '1'; + elsif ctrl_in.pipe.exc_req = '1' then + exc_staten <= exc_pipe_flush; + end if; + + when exc_pipe_flush => + ctrl_out.pipe.exc_pending <= '1'; + ctrl_out.pipe.exc_commit <= '1'; + if ctrl_in.pipe.exc_ack = '1' then + exc_staten <= exc_commit; + end if; + + when exc_commit => + latch_vect_en <= '1'; + ctrl_out.pipe.exc_pending <= '1'; + ctrl_out.pipe.exc_commit <= '1'; + if ctrl_in.pipe.sdu.ID_stall = '0' then + exc_staten <= exc_inject; + end if; + + when exc_inject => + ctrl_out.pipe.exc_pending <= '1'; + ctrl_out.pipe.exc_commit <= '1'; + code_reg_we <= '1'; + status_save <= '1'; + epc_reg_we <= '1'; + inject_exc <= '1'; + exc_staten <= exc_enter; + + when exc_enter => + ctrl_out.pipe.exc_pending <= '1'; + exc_staten <= exc_idle; + + when exc_leave => + exc_staten <= exc_left; + status_rest <= '1'; + ctrl_out.pipe.exc_exit <= '1'; + ctrl_out.pipe.exc_commit <= '1'; + + when exc_left => + exc_staten <= exc_idle; + + when others => + exc_staten <= exc_idle; + + end case; + end process; + +-- EPC reg and BD-bit write +cop_exception_epc_write: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + bd <= '0'; + regs.epc <= (others => '0'); + elsif epc_reg_we = '1' then + bd <= ctrl_in.pipe.bd_wb; + regs.epc <= ctrl_in.pipe.epc_wb; + end if; + end if; + end process; + +-- BadVaddr and exception code write +cop_exception_map: + process(clk) + begin + if rising_edge(clk) then + itlb_exc <= '0'; + dtlb_exc <= '0'; + if rst = '1' then + exc_code <= (others => '0'); + regs.badvaddr <= (others => '0'); + elsif code_reg_we = '1' then + if eflags.Ov = '1' then + exc_code <= "01100"; + regs.badvaddr <= ctrl_in.pipe.imem_addr; + elsif eflags.Sys = '1' then + exc_code <= "01000"; + regs.badvaddr <= ctrl_in.pipe.imem_addr; + elsif eflags.Bp = '1' then + exc_code <= "01001"; + regs.badvaddr <= ctrl_in.pipe.imem_addr; + elsif eflags.RI = '1' then + exc_code <= "01010"; + regs.badvaddr <= ctrl_in.pipe.imem_addr; + elsif eflags.IAdEL = '1' then + exc_code <= "00100"; + regs.badvaddr <= ctrl_in.pipe.imem_addr; + elsif eflags.IAdEK = '1' then + exc_code <= "00100"; + regs.badvaddr <= ctrl_in.pipe.imem_addr; + elsif eflags.DAdEL = '1' then + exc_code <= "00100"; + regs.badvaddr <= ctrl_in.pipe.dmem_addr; + elsif eflags.DAdES = '1' then + exc_code <= "00101"; + regs.badvaddr <= ctrl_in.pipe.dmem_addr; + elsif eflags.Int = '1' then + exc_code <= "00000"; + regs.badvaddr <= ctrl_in.pipe.imem_addr; + elsif eflags.ITLB_MOD = '1' then + exc_code <= "00001"; + regs.badvaddr <= ctrl_in.pipe.imem_addr; + itlb_exc <= '1'; + elsif eflags.ITLB_LOAD = '1' then + exc_code <= "00010"; + regs.badvaddr <= ctrl_in.pipe.imem_addr; + itlb_exc <= '1'; + elsif eflags.ITLB_STORE = '1' then + exc_code <= "00011"; + regs.badvaddr <= ctrl_in.pipe.imem_addr; + itlb_exc <= '1'; + elsif eflags.DTLB_MOD = '1' then + exc_code <= "00001"; + regs.badvaddr <= ctrl_in.pipe.dmem_addr; + dtlb_exc <= '1'; + elsif eflags.DTLB_LOAD = '1' then + exc_code <= "00010"; + regs.badvaddr <= ctrl_in.pipe.dmem_addr; + dtlb_exc <= '1'; + elsif eflags.DTLB_STORE = '1' then + exc_code <= "00011"; + regs.badvaddr <= ctrl_in.pipe.dmem_addr; + dtlb_exc <= '1'; + end if; + end if; + end if; + end process; + +-- Exception vector dispatch +cop_exc_vector: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + ctrl_out.pipe.exc_vec <= X"BFC00000"; + elsif latch_vect_en = '1' then + if ctrl_in.pipe.events.nmi = '1' then + ctrl_out.pipe.exc_vec <= X"BFC00000"; + else + ctrl_out.pipe.exc_vec <= X"80000080"; + if regs.status(22) = '1' then + ctrl_out.pipe.exc_vec <= X"BFC00180"; + end if; + end if; + end if; + end if; + end process; + + ctrl_out.icache.inv_addr <= regs.test; + ctrl_out.dcache.inv_addr <= regs.test; + +-- Custom I/D-Cache operations: invalidate all/line +cop_cache_op: + process(clk) + begin + if rising_edge(clk) then + ctrl_out.icache.inv_at <= '0'; + ctrl_out.icache.inv_all <= '0'; + ctrl_out.dcache.inv_at <= '0'; + ctrl_out.dcache.inv_all <= '0'; + if cop_pipe_ID.cs = '1' and cop_pipe_ID.CO = '1' then + case cop_pipe_ID.func is + when "100000" => + ctrl_out.icache.inv_all <= '1'; + when "100001" => + ctrl_out.icache.inv_at <= '1'; + when "100010" => + ctrl_out.dcache.inv_all <= '1'; + when "100011" => + ctrl_out.dcache.inv_at <= '1'; + + when others => null; + end case; + end if; + end if; + end process; + +-- CAUSE: IP register write and signalling Int to pipeline +cop_ip_register: + process(clk) + variable ip_v : unsigned(7 downto 0); + variable ipm_v : unsigned(7 downto 0); + begin + if rising_edge(clk) then + if rst = '1' then + sw_int <= (others => '0'); + elsif ctrl_in.pipe.sdu.WB_nop = '0' then + if EX_reg_we(COP0_CAUSE) = '1' then + sw_int <= din(9 downto 8); + end if; + end if; + ip_v := (int & sw_int); + ipm_v := ip_v and im; + ip <= ip_v; + ctrl_out.pipe.int <= (eval_int(ipm_v) and regs.status(0)); + ctrl_out.pipe.NMI <= int_nmi; + end if; + end process; + + +-- WIRED: Wired Register (RO) +cop_wired_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + regs.wired <= X"0000_0008"; + end if; + end if; + end process; + +-- RANDOM: Random Register (RO) +cop_random_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' or EX_reg_we(COP0_WIRED) = '1' then + random <= to_unsigned(TLB_NUM_ENTRIES-1, lg2(TLB_NUM_ENTRIES)); + elsif random > regs.wired(lg2(TLB_NUM_ENTRIES)-1 downto 0) then + random <= random - 1; + else + random <= to_unsigned(TLB_NUM_ENTRIES-1, lg2(TLB_NUM_ENTRIES)); + end if; + end if; + end process; + +-- INDEX: Index Register (RW) +cop_index_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + index <= to_unsigned(0, lg2(TLB_NUM_ENTRIES)); + elsif EX_reg_we(COP0_INDEX) = '1' then + index <= din(8+lg2(TLB_NUM_ENTRIES)-1 downto 8); + end if; + end if; + end process; + +-- CONTEXT: PTEBase Register (RW) +cop_context_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + PTEBase <= to_unsigned(0, PTEBase'length); + elsif EX_reg_we(COP0_CONTEXT) = '1' then + PTEBase <= din(31 downto 21); + end if; + end if; + end process; + +-- ITLB EntryLO +itlb_entrylo_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + itlb_entry_lo <= to_tlb_entry_lo(to_unsigned(0, 32)); + elsif cop_pipe_EX.TLBR = '1' then + itlb_entry_lo <= ctrl_in.itlb.entry_lo; + elsif EX_reg_we(COP0_ENTRY_LO) = '1' and tlb_sel = '0' then + itlb_entry_lo <= to_tlb_entry_lo(din); + end if; + end if; + end process; + +-- ITLB EntryHI +itlb_entryhi_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + itlb_entry_hi <= to_tlb_entry_hi(to_unsigned(0, 32)); + elsif itlb_exc = '1' then + itlb_entry_hi <= to_tlb_entry_hi(itlb_entry_hi, regs.badvaddr); + elsif cop_pipe_EX.TLBR = '1' then + itlb_entry_hi <= ctrl_in.itlb.entry_hi; + elsif EX_reg_we(COP0_ENTRY_HI) = '1' and tlb_sel = '0' then + itlb_entry_hi <= to_tlb_entry_hi(din); + end if; + end if; + end process; + +-- DTLB +dtlb_entrylo_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + dtlb_entry_lo <= to_tlb_entry_lo(to_unsigned(0, 32)); + elsif cop_pipe_EX.TLBR = '1' then + dtlb_entry_lo <= ctrl_in.dtlb.entry_lo; + elsif EX_reg_we(COP0_ENTRY_LO) = '1' and tlb_sel = '1' then + dtlb_entry_lo <= to_tlb_entry_lo(din); + end if; + end if; + end process; + +-- DTLB EntryHI +dtlb_entryhi_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + dtlb_entry_hi <= to_tlb_entry_hi(to_unsigned(0, 32)); + elsif dtlb_exc = '1' then + dtlb_entry_hi <= to_tlb_entry_hi(dtlb_entry_hi, regs.badvaddr); + elsif cop_pipe_EX.TLBR = '1' then + dtlb_entry_hi <= ctrl_in.dtlb.entry_hi; + elsif EX_reg_we(COP0_ENTRY_HI) = '1' and tlb_sel = '1' then + dtlb_entry_hi <= to_tlb_entry_hi(din); + end if; + end if; + end process; + +-- TEST: COP Test Register +cop_test_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + regs.test <= (others => '0'); + elsif ctrl_in.pipe.sdu.WB_nop = '0' then + if EX_reg_we(COP0_1F) = '1' then + regs.test <= din; + end if; + end if; + end if; + end process; + +-- CONFIG: COP Config Register +cop_config_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + regs.config <= (others => '0'); + elsif ctrl_in.pipe.sdu.WB_nop = '0' then + if EX_reg_we(COP0_CONFIG) = '1' then + regs.config <= din; + end if; + end if; + end if; + end process; + +-- STATUS: COP Status Register +cop_status_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + regs.status <= X"00600002"; -- at reset: set kernel mode enabled and interrupts disabled + elsif status_save = '1' then + if ctrl_in.pipe.events.nmi = '0' then + regs.status(5 downto 4) <= regs.status(3 downto 2); + regs.status(3 downto 2) <= regs.status(1 downto 0); + else + regs.status(22) <= '1'; + end if; + regs.status(1 downto 0) <= "10"; -- at exception: set kernel mode enabled and interrupts disabled + elsif status_rest = '1' then + regs.status(1 downto 0) <= regs.status(3 downto 2); + regs.status(3 downto 2) <= regs.status(5 downto 4); + elsif ctrl_in.pipe.sdu.WB_nop = '0' then + if EX_reg_we(COP0_STATUS) = '1' then + if regs.status(1) = '1' then -- accessible only in kernel mode + regs.status(0) <= din(0); -- IEc + regs.status(1) <= din(1); -- KUc + regs.status(2) <= din(2); -- IEp + regs.status(3) <= din(3); -- KUp + regs.status(4) <= din(4); -- IEo + regs.status(5) <= din(5); -- KUo + end if; + regs.status(15 downto 8) <= din(15 downto 8); -- IM + regs.status(22) <= din(22); -- BEV + regs.status(25) <= din(25); -- RE + end if; + end if; + end if; + end process; + +-------------------------------------------------------------------------- +end Behavioral; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/mips_dcache.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_dcache.vhd new file mode 100644 index 0000000..6f8d7e2 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_dcache.vhd @@ -0,0 +1,780 @@ +-------------------------------------------------------------------------- +-- 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_util_pkg.all; +use work.mips_types.all; +use work.busmaster_types.all; + +ENTITY dcache IS + Generic + ( + CACHE_SIZE : natural := 1024; -- words + LINE_SIZE : natural := 8; -- words + WRITE_FIFO_SIZE : natural := 4 + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + + en : in STD_LOGIC; + we : in STD_LOGIC; + be : in unsigned(3 downto 0); + addr : in word_t; + din : in word_t; + dout : out word_t; + rdy : out STD_LOGIC; + + -- Cache control + ctrl_in : in cache_ctrl_t; + + tlb_query_out : out tlb_query_in_t; + tlb_query_in : in tlb_query_out_t; + + -- busmaster data + bus_din : in word_t; + bus_din_rdy : out std_logic; + bus_din_vld : in std_logic; + + bus_dout : out word_t; + bus_dout_vld : out std_logic; + bus_dout_rdy : in std_logic; + + -- busmaster command + bus_cmd_rdy : in std_logic; + bus_cmd_we : out std_logic; + bus_cmd_cycle_en : out std_logic; + bus_cmd_out : out bm_cmd_t; + bus_cyc_complete : in 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 - lg2(CACHE_SIZE) - 2; + constant tag_ram_data_width : natural := 1 + tag_width + lg2(TLB_NUM_ENTRIES)+1; + 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; + tag : unsigned(tag_width-1 downto 0); + idx : unsigned(lg2(TLB_NUM_ENTRIES) 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.tag := x(tag_width downto 1); + result.idx := x(lg2(TLB_NUM_ENTRIES)+tag_width+1 downto tag_width+1); + + 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(tag_width downto 1) := x.tag; + result(lg2(TLB_NUM_ENTRIES)+tag_width+1 downto tag_width+1) := x.idx; + + return result; + end to_tag_ram_data; + + function to_word_index(x : word_t) return unsigned is + variable result : unsigned(word_index_width-1 downto 0); + begin + result := x(word_index_width+1 downto 2); + + return result; + + end to_word_index; + + + function to_cache_index(x : word_t) return unsigned is + variable result : unsigned(cache_index_width-1 downto 0); + begin + result := x(cache_index_width+word_index_width+1 downto word_index_width+2); + + return result; + + end to_cache_index; + + function to_tag(x : word_t) return unsigned is + variable result : unsigned(tag_width-1 downto 0); + begin + result := x(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + return result; + + end to_tag; + + + type cache_busy_state_t is (init, ready, busy); + signal cache_busy_state, cache_busy_state_next : cache_busy_state_t; + + type cache_ctrl_state_t is (init, ready, invalidate, invalidate_post, flush, bus_request, mem_read, mem_read_single, mem_read_data, mem_read_data_single, rd_cache, upd_cache, wait_write_thru); + signal cache_ctrl_state, cache_ctrl_state_next : cache_ctrl_state_t; + + type debug_t is record + ready : std_logic; + req_strobe : std_logic; + req_read : std_logic; + req_write : std_logic; + req_va : word_t; + req_pa : word_t; + dout_reg : word_t; + din_reg : word_t; + end record; + + signal debug : debug_t; + + signal req_strobe : std_logic; + signal cache_req : std_logic; + signal cache_req_rd : std_logic; + signal cache_req_wr : std_logic; + signal cache_rdy : std_logic; + signal cache_hit : std_logic; + signal read_rdy : std_logic; + signal write_rdy : std_logic; + signal request_va : word_t; + signal request_pa : word_t; + + signal tag_match : std_logic; + signal cache_hit_inv : std_logic; + signal tag_match_inv : std_logic; + + signal mem_fetch_req : std_logic; + signal mem_fetch_ack : std_logic; + signal single_read_en : std_logic; + signal single_read_set : std_logic; + signal single_read_reg_vld : std_logic; + + 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 data_ram_addr : unsigned(lg2(CACHE_SIZE)-1 downto 0); + signal data_ram_dout : word_t; + signal din_reg : word_t; + signal be_reg : unsigned(3 downto 0); + signal ctrl_data_ram_addr : unsigned(lg2(CACHE_SIZE)-1 downto 0); + signal ctrl_data_ram_we : unsigned(3 downto 0); + signal data_ram_we : unsigned(3 downto 0); + signal single_read_reg : word_t; + signal cache_dout : word_t; + + 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 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; + signal write_hit : std_logic; + signal instant_raw : std_logic; + signal ram_write_en : std_logic; + signal fill_word_index : unsigned(word_index_width-1 downto 0); + signal req_word_index : unsigned(word_index_width-1 downto 0); + signal hit_cache_index : unsigned(cache_index_width-1 downto 0); + + signal write_fifo_din : unsigned(67 downto 0); + signal write_fifo_dout : unsigned(67 downto 0); + signal write_fifo_re : std_logic; + signal write_fifo_we : std_logic; + signal write_fifo_full : std_logic; + signal write_fifo_empty : std_logic; + signal write_data_avail : std_logic; + signal read_write_buffer : std_logic; + + alias write_fifo_addr_in is write_fifo_din(31 downto 0); + alias write_fifo_data_in is write_fifo_din(63 downto 32); + alias write_fifo_sel_in is write_fifo_din(67 downto 64); + alias write_fifo_addr_out is write_fifo_dout(31 downto 0); + alias write_fifo_data_out is write_fifo_dout(63 downto 32); + alias write_fifo_sel_out is write_fifo_dout(67 downto 64); + + signal write_through_en : std_logic; + +begin + + -- Print info + assert false report "addr_width = " & natural'image(addr_width) & "." severity note; + assert false report "tag_width = " & natural'image(tag_width) & "." severity note; + assert false report "word_index_width = " & natural'image(word_index_width) & "." severity note; + assert false report "cache_index_width = " & natural'image(cache_index_width) & "." severity note; + + cache_index_inv <= ctrl_in.inv_addr(cache_index_width+word_index_width+1 downto word_index_width+2); + tag_inv <= ctrl_in.inv_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + bus_din_rdy <= fill_count_en or single_read_en; + bus_cmd_out.addr <= write_fifo_addr_out when read_write_buffer = '1' else to_tag(request_pa) & to_cache_index(request_pa) & req_word_index & "00"; + bus_cmd_out.rw <= read_write_buffer; + bus_cmd_out.sel <= write_fifo_sel_out when read_write_buffer = '1' else (others => '1'); + bus_dout <= write_fifo_data_out; + bus_dout_vld <= read_write_buffer and write_data_avail; + +-- tlb_query_out.vld <= cache_req; +-- tlb_query_out.vaddr <= request_va; +-- tlb_query_out.write <= cache_req_wr; + tlb_query_out.vld <= req_strobe; + tlb_query_out.vaddr <= addr; + tlb_query_out.idx <= cache_entry_out.idx; + tlb_query_out.write <= we; + + request_pa <= tlb_query_in.paddr; + + write_hit <= cache_req_wr and cache_hit and not tlb_query_in.exc; + + debug.req_strobe <= req_strobe; + debug.req_read <= cache_req_rd; + debug.req_write <= cache_req_wr; + debug.req_va <= request_va; + debug.req_pa <= request_pa; + debug.din_reg <= din_reg; + debug.dout_reg <= cache_dout; + debug.ready <= cache_rdy; + +------------------------------------------------- +-- ctrl +------------------------------------------------- +req_strobe <= cache_rdy and en; + +request_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + cache_req_rd <= '0'; + cache_req_wr <= '0'; + elsif cache_rdy = '1' then + cache_req_rd <= en and not we; + cache_req_wr <= en and we; + cache_req <= en; + if we = '1' then + hit_cache_index <= to_cache_index(addr); + end if; + end if; + end if; + end process; + +request_address_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + request_va <= (others => '0'); + elsif req_strobe = '1' then + din_reg <= din; + be_reg <= be; + request_va <= addr; + end if; + end if; + end process; + +instant_raw_logic: + process(clk) + begin + if rising_edge(clk) then + instant_raw <= '0'; + if to_word_index(addr) = fill_word_index and to_cache_index(addr) = hit_cache_index then + instant_raw <= write_hit and en and not we; + end if; + end if; + end process; + +------------------------------------------------- +-- Instantiate synchronous FIFO +inst_write_fifo: entity work.fifo_sync + GENERIC MAP + ( + addr_width => lg2(WRITE_FIFO_SIZE), + data_width => 68, + do_last_read_update => true + ) + PORT MAP + ( + rst => rst, + clk => clk, + we => write_fifo_we, + re => write_fifo_re, + fifo_full => write_fifo_full, + fifo_empty => write_fifo_empty, + fifo_afull => open, + fifo_aempty => open, + data_w => write_fifo_din, + data_r => write_fifo_dout + ); + + write_fifo_data_in <= din_reg; + write_fifo_addr_in <= request_pa; + write_fifo_sel_in <= be_reg; + write_fifo_re <= read_write_buffer and bus_dout_rdy and bus_cmd_rdy; + write_fifo_we <= write_through_en; + write_data_avail <= not write_fifo_empty; + +fill_address_register: + process(clk) + begin + if rising_edge(clk) then + if req_strobe = '1' then + fill_word_index <= to_word_index(addr); + elsif bus_din_vld = '1' and fill_count_en = '1' then + fill_word_index <= fill_word_index + 1; + end if; + end if; + end process; + +bus_request_address_register: + process(clk) + begin + if rising_edge(clk) then + if req_strobe = '1' then + req_word_index <= to_word_index(addr); + elsif request_count_en = '1' and bus_cmd_rdy = '1' then + req_word_index <= req_word_index + 1; + end if; + end if; + end process; + +single_read_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + single_read_en <= '0'; + single_read_reg_vld <= '0'; + elsif single_read_en = '0' then + if single_read_set = '1' then + single_read_en <= '1'; + end if; + elsif single_read_reg_vld = '1' then + if en = '1' then + single_read_en <= '0'; + single_read_reg_vld <= '0'; + end if; + elsif bus_din_vld = '1' and single_read_en = '1' then + single_read_reg <= bus_din; + single_read_reg_vld <= '1'; + 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, + clk_b => clk, + 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_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, + clk_b => clk, + en_a => '1', + en_b => ram_read_en, + we_a => data_ram_we(i), + we_b => ctrl_data_ram_we(i), + addr_a => ctrl_data_ram_addr, + addr_b => data_ram_addr, + din_a => din_reg(8*(i+1)-1 downto 8*i), + din_b => bus_din(8*(i+1)-1 downto 8*i), + dout_a => open, + dout_b => data_ram_dout(8*(i+1)-1 downto 8*i) + ); + end generate; + + + +cache_invalidate_request: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' or ctrl_in.inv_all = '1' or ctrl_in.inv_at = '1' then + invalidate_req <= '1'; + invalidate_all <= ctrl_in.inv_all or rst; + 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) + begin + if rising_edge(clk) then + if rst = '1' then + cache_ctrl_state <= init; + cache_busy_state <= init; + else + cache_ctrl_state <= cache_ctrl_state_next; + cache_busy_state <= cache_busy_state_next; + end if; + end if; + end process; + + cache_rdy <= read_rdy and not instant_raw and write_rdy; + write_rdy <= not (write_fifo_full and cache_req_wr); + rdy <= cache_rdy; + + cache_dout <= single_read_reg when single_read_en = '1' else to_mips_01(data_ram_dout); + dout <= cache_dout; + +-- tag_match <= '1' when to_tag(request_pa) = cache_entry_out.tag else '0'; + tag_match <= '1' when ((to_tag(request_pa) xor cache_entry_out.tag) = (tag_width-1 downto 0 => '0')) 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 to_cache_index(request_va); + tag_ram_addr_rd <= to_cache_index(addr) when (was_miss = '0' and instant_raw = '0') else to_cache_index(request_va); + + cache_entry_out <= to_dcache_entry(to_mips_01(tag_ram_dout)); + cache_entry_out_inv <= to_dcache_entry(to_mips_01(tag_ram_dout_inv)); + + ram_read_en <= en or was_miss or fill_count_en; + data_ram_addr <= (to_cache_index(addr) & to_word_index(addr)) when (was_miss = '0' and instant_raw = '0' and fill_count_en = '0') else (to_cache_index(request_va) & fill_word_index); + ctrl_data_ram_addr <= to_cache_index(request_va) & fill_word_index; + ctrl_data_ram_we <= (others => ram_write_en and bus_din_vld); + data_ram_we <= be_reg when (write_hit = '1') else (others => '0'); + + write_through_en <= cache_rdy and cache_req_wr; -- and tlb_query_in.hit and not tlb_query_in.flags.D; + +cache_busy_state_fsm: + process(cache_busy_state, tlb_query_in, cache_req_rd, cache_hit, mem_fetch_ack, single_read_en, single_read_reg_vld) + begin + + read_rdy <= '0'; + mem_fetch_req <= '0'; + single_read_set <= '0'; + cache_busy_state_next <= cache_busy_state; + + case cache_busy_state is + + when init => + cache_busy_state_next <= ready; + + when ready => + if single_read_en = '1' then + read_rdy <= single_read_reg_vld; + else + read_rdy <= '1'; + if cache_req_rd = '1' then + if tlb_query_in.exc = '0' then + if tlb_query_in.flags.N = '1' or cache_hit = '0' then +-- mem_fetch_req <= '1'; + read_rdy <= '0'; + cache_busy_state_next <= busy; + end if; + end if; + end if; + end if; + + when busy => + mem_fetch_req <= '1'; + if mem_fetch_ack = '1' then + single_read_set <= tlb_query_in.flags.N; + cache_busy_state_next <= ready; + end if; + + when others => + cache_busy_state_next <= ready; + + end case; + + end process; + +cache_ctrl_state_fsm: + process(cache_ctrl_state, mem_fetch_req, tlb_query_in, flush_count_rdy, fill_count_rdy, request_count_rdy, request_pa, bus_cmd_rdy, bus_din_vld, invalidate_req, invalidate_all, write_through_en, write_data_avail) + begin + tag_ram_we <= '0'; + flush_count_en <= '0'; + flush_count_rst <= '0'; + invalidate_en <= '0'; + request_count_en <= '0'; + fill_count_en <= '0'; + ram_write_en <= '0'; + bus_cmd_cycle_en <= '0'; + bus_cmd_we <= '0'; + was_miss <= '0'; + invalidate_ack <= '0'; + mem_fetch_ack <= '0'; + read_write_buffer <= '0'; + + cache_entry_in.idx <= tlb_query_in.idx; + cache_entry_in.tag <= to_tag(request_pa); + cache_entry_in.valid <= '0'; + cache_ctrl_state_next <= cache_ctrl_state; + + case cache_ctrl_state is + + when init => + cache_ctrl_state_next <= ready; + + when ready => + if invalidate_req = '1' then + cache_ctrl_state_next <= invalidate; + invalidate_en <= '1'; + elsif mem_fetch_req = '1' or write_data_avail = '1' then + cache_ctrl_state_next <= bus_request; +-- bus_cmd_cycle_en <= '1'; + elsif write_through_en = '1' then + cache_ctrl_state_next <= wait_write_thru; +-- bus_cmd_cycle_en <= '1'; + end if; + + when invalidate => + cache_ctrl_state_next <= rd_cache; + invalidate_en <= '1'; + invalidate_ack <= '1'; + if invalidate_all = '1' then + cache_ctrl_state_next <= 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'); + cache_ctrl_state_next <= invalidate_post; + 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'; + cache_ctrl_state_next <= invalidate_post; + end if; + + when invalidate_post => + was_miss <= '1'; + cache_ctrl_state_next <= ready; + + when wait_write_thru => + cache_ctrl_state_next <= bus_request; + bus_cmd_cycle_en <= '1'; + + when bus_request => + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + if write_data_avail = '1' then + read_write_buffer <= '1'; + bus_cmd_we <= '1'; + elsif mem_fetch_req = '1' then + if tlb_query_in.flags.N = '1' then + cache_ctrl_state_next <= mem_read_single; + else + cache_ctrl_state_next <= mem_read; + end if; + else + cache_ctrl_state_next <= ready; + end if; + end if; + + when mem_read => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + request_count_en <= '1'; + ram_write_en <= '1'; + fill_count_en <= '1'; + if request_count_rdy = '1' then + bus_cmd_we <= '0'; + cache_ctrl_state_next <= mem_read_data; + end if; + + when mem_read_single => + mem_fetch_ack <= '1'; + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_ctrl_state_next <= mem_read_data_single; + + when mem_read_data => + bus_cmd_cycle_en <= '1'; + fill_count_en <= '1'; + ram_write_en <= '1'; + if fill_count_rdy = '1' then + tag_ram_we <= '1'; + cache_entry_in.valid <= '1'; + cache_ctrl_state_next <= rd_cache; + end if; + + when mem_read_data_single => + bus_cmd_cycle_en <= '1'; + if bus_din_vld = '1' then + cache_ctrl_state_next <= ready; + end if; + + when rd_cache => + mem_fetch_ack <= '1'; + was_miss <= '1'; + cache_ctrl_state_next <= ready; + + when others => + cache_ctrl_state_next <= ready; + end case; + + end process; + +flush_counter: + process(clk) + begin + if rising_edge(clk) then + if ctrl_in.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) + begin + if rising_edge(clk) then + if request_count_en = '0' then + request_count_rdy <= '0'; + request_count <= 2**word_index_width-1; + else + if bus_cmd_rdy = '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) + begin + if rising_edge(clk) then + if fill_count_en = '0' then + fill_count_rdy <= '0'; + fill_count <= 2**word_index_width-1; + else + if bus_din_vld = '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; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/mips_func_pkg.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_func_pkg.vhd new file mode 100644 index 0000000..59f6f31 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_func_pkg.vhd @@ -0,0 +1,276 @@ +-------------------------------------------------------------------------- +-- Project: JIPS, a portable 32-bit RISC CPU written in VHDL +-- This file: Types, constants and functions for JIPS +-- +-- 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; +use IEEE.MATH_REAL.ALL; + +library work; +use work.mips_util_pkg.all; +use work.mips_types.all; + +-------------------------------------------------------------------------- +package mips_func_pkg is + + function store_shift(x : word_t; va, shift_off : unsigned(1 downto 0); bypass : std_logic) return word_t; + function store_be(va : unsigned(1 downto 0); be_src, word2_en, word4_en, align_left, bypass : std_logic) return unsigned; + function load_shift(x : word_t; va, shift_off : unsigned(1 downto 0); bypass : std_logic) return word_t; + function load_be(va : unsigned(1 downto 0); align_left, bypass : std_logic) return unsigned; + function load_sign_ext(x : word_t; bypass, signed, word2_en, word4_en : std_logic) return word_t; + function events_clr return event_t; + function event_is_active(e : event_t) return std_logic; + function "or" (a, b : event_t) return event_t; + +end mips_func_pkg; + +-------------------------------------------------------------------------- +package body mips_func_pkg is + + function store_shift(x : word_t; va, shift_off : unsigned(1 downto 0); bypass : std_logic) return word_t is + variable stage1 : word_t; + variable result : word_t; + variable sa : unsigned(1 downto 0); + begin + + if bypass = '1' then + sa := "00"; + else + sa := va + shift_off; + end if; + + stage1 := x; + if sa(0) = '1' then + stage1 := x(23 downto 0) & x(31 downto 24); + end if; + result := stage1; + if sa(1) = '1' then + result := stage1(15 downto 0) & stage1(31 downto 16); + end if; + + return result; + end store_shift; + +-------------------------------------------------------------------------- + function store_be(va : unsigned(1 downto 0); be_src, word2_en, word4_en, align_left, bypass : std_logic) return unsigned is + variable result : unsigned(3 downto 0); + begin + result := (3 downto 0 => be_src); + if bypass = '0' then + if word2_en = '1' then + case va is + when "00" => + result := "00" & be_src & be_src; + when "10" => + result := be_src & be_src & "00"; + when others => null; + end case; + elsif word4_en = '1' then + case va is + when "00" => + result := "000" & be_src; + when "01" => + result := "00" & be_src & '0'; + when "10" => + result := '0' & be_src & "00"; + when "11" => + result := be_src & "000"; + when others => null; + end case; + elsif align_left = '1' then + case va is + when "00" => + result := be_src & be_src & be_src & be_src; + when "01" => + result := be_src & be_src & be_src & '0'; + when "10" => + result := be_src & be_src & "00"; + when "11" => + result := be_src & "000"; + when others => null; + end case; + else + case va is + when "00" => + result := "000" & be_src; + when "01" => + result := "00" & be_src & be_src; + when "10" => + result := '0' & be_src & be_src & be_src; + when "11" => + result := be_src & be_src & be_src & be_src; + when others => null; + end case; + end if; + end if; + + return result; + + end store_be; + +-------------------------------------------------------------------------- + function load_shift(x : word_t; va, shift_off : unsigned(1 downto 0); bypass : std_logic) return word_t is + variable stage1 : word_t; + variable result : word_t; + variable sa : unsigned(1 downto 0); + + begin + if bypass = '1' then + sa := "00"; + else + sa := va + shift_off; + end if; + + stage1 := x; + if sa(0) = '1' then + stage1 := x(7 downto 0) & x(31 downto 8); + end if; + result := stage1; + if sa(1) = '1' then + result := stage1(15 downto 0) & stage1(31 downto 16); + end if; + + return result; + end load_shift; + +-------------------------------------------------------------------------- + function load_be(va : unsigned(1 downto 0); align_left, bypass : std_logic) return unsigned is + variable result : unsigned(3 downto 0); + begin + result := (3 downto 0 => '1'); + if bypass = '0' then + if align_left = '1' then + case va is + when "00" => + result := "1000"; + when "01" => + result := "1100"; + when "10" => + result := "1110"; + when "11" => + result := "1111"; + when others => null; + end case; + else + case va is + when "00" => + result := "1111"; + when "01" => + result := "0111"; + when "10" => + result := "0011"; + when "11" => + result := "0001"; + when others => null; + end case; + end if; + end if; + + return result; + + end load_be; + +-------------------------------------------------------------------------- + function load_sign_ext(x : word_t; bypass, signed, word2_en, word4_en : std_logic) return word_t is + variable result : word_t; + variable sign : std_logic; + + begin + if bypass = '1' then + result := x; + elsif word2_en = '1' then + sign := signed and x(15); + result := (31 downto 16 => sign) & x(15 downto 0); + else + sign := signed and x(7); + result := (31 downto 8 => sign) & x(7 downto 0); + end if; + + return result; + end load_sign_ext; + +-------------------------------------------------------------------------- + function events_clr return event_t is + variable result : event_t; + begin + result.NMI := '0'; + result.Int := '0'; + result.data_load_err := '0'; + result.data_store_err := '0'; + result.inst_load_err := '0'; + result.inst_priv_addr := '0'; + result.alu_ovf := '0'; + result.alu_uvf := '0'; + result.syscall := '0'; + result.break := '0'; + result.illegal := '0'; + + return result; + + end events_clr; + +-------------------------------------------------------------------------- + function event_is_active(e : event_t) return std_logic is + variable result : std_logic; + begin + result := '0'; + + result := result or e.NMI; + result := result or e.Int; + result := result or e.data_load_err; + result := result or e.data_store_err; + result := result or e.inst_load_err; + result := result or e.inst_priv_addr; + result := result or e.alu_ovf; + result := result or e.alu_uvf; + result := result or e.syscall; + result := result or e.break; + result := result or e.illegal; + + return result; + + end event_is_active; + +-------------------------------------------------------------------------- + function "or" (a, b : event_t) return event_t is + variable result : event_t; + begin + result.NMI := a.NMI or b.NMI; + result.Int := a.Int or b.Int; + result.data_load_err := a.data_load_err or b.data_load_err; + result.data_store_err := a.data_store_err or b.data_store_err; + result.inst_load_err := a.inst_load_err or b.inst_load_err; + result.inst_priv_addr := a.inst_priv_addr or b.inst_priv_addr; + result.alu_ovf := a.alu_ovf or b.alu_ovf; + result.alu_uvf := a.alu_uvf or b.alu_uvf; + result.syscall := a.syscall or b.syscall; + result.break := a.break or b.break; + result.illegal := a.illegal or b.illegal; + + return result; + + end "or"; + +-------------------------------------------------------------------------- +end mips_types; + diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/mips_icache.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_icache.vhd new file mode 100644 index 0000000..55f862b --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_icache.vhd @@ -0,0 +1,663 @@ +-------------------------------------------------------------------------- +-- 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_util_pkg.all; +use work.mips_types.all; +use work.busmaster_types.all; + +ENTITY icache IS + Generic + ( + CACHE_SIZE : natural := 1024; -- words + LINE_SIZE : natural := 8 -- words + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + + -- CPU control/data + en : in STD_LOGIC; + addr : in word_t; + dout : out word_t; + rdy : out STD_LOGIC; + + -- Cache control + ctrl_in : in cache_ctrl_t; + + tlb_query_out : out tlb_query_in_t; + tlb_query_in : in tlb_query_out_t; + + -- busmaster data + bus_din : in word_t; + bus_din_rdy : out std_logic; + bus_din_vld : in std_logic; + + -- busmaster command + bus_cmd_rdy : in std_logic; + bus_cmd_we : out std_logic; + bus_cmd_cycle_en : out std_logic; + bus_cmd_out : out bm_cmd_t; + bus_cyc_complete : in std_logic + ); +END icache; + +ARCHITECTURE behavior OF icache IS + +COMPONENT dpram_1w1r2c_ra + 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; + 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_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 - lg2(CACHE_SIZE) - 2; + constant tag_ram_data_width : natural := 1 + tag_width + lg2(TLB_NUM_ENTRIES); + 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; + tag : unsigned(tag_width-1 downto 0); + tlb_idx : unsigned(lg2(TLB_NUM_ENTRIES)-1 downto 0); + end record; + + 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.tag := x(tag_width downto 1); + result.tlb_idx := x(lg2(TLB_NUM_ENTRIES)+tag_width downto tag_width+1); + + 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(tag_width downto 1) := x.tag; + result(lg2(TLB_NUM_ENTRIES)+tag_width downto tag_width+1) := x.tlb_idx; + + return result; + end to_tag_ram_data; + + function to_word_index(x : word_t) return unsigned is + variable result : unsigned(word_index_width-1 downto 0); + begin + result := x(word_index_width+1 downto 2); + + return result; + + end to_word_index; + + + function to_cache_index(x : word_t) return unsigned is + variable result : unsigned(cache_index_width-1 downto 0); + begin + result := x(cache_index_width+word_index_width+1 downto word_index_width+2); + + return result; + + end to_cache_index; + + function to_tag(x : word_t) return unsigned is + variable result : unsigned(tag_width-1 downto 0); + begin + result := x(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + return result; + + end to_tag; + + type cache_busy_state_t is (init, ready, busy); + signal cache_busy_state, cache_busy_state_next : cache_busy_state_t; + + type cache_ctrl_state_t is (init, ready, invalidate, invalidate_post, flush, bus_request, mem_read, mem_read_single, mem_read_data, mem_read_data_single, rd_cache, upd_cache); + signal cache_ctrl_state, cache_ctrl_state_next : cache_ctrl_state_t; + + type debug_t is record + ready : std_logic; + req_strobe : std_logic; + req_read : std_logic; + req_va : word_t; + req_pa : word_t; + dout_reg : word_t; + end record; + + signal debug : debug_t; + + signal req_strobe : std_logic; + signal cache_req_rd : std_logic; + signal read_rdy : std_logic; + signal cache_rdy : std_logic; + signal cache_hit : std_logic; + signal request_va : word_t; + signal request_pa : word_t; + + signal tag_match : std_logic; + signal cache_hit_inv : std_logic; + signal tag_match_inv : std_logic; + + signal mem_fetch_req : std_logic; + signal mem_fetch_ack : std_logic; + signal single_read_en : std_logic; + signal single_read_set : std_logic; + signal single_read_reg_vld : std_logic; + + 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 : 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 single_read_reg : word_t; + signal cache_dout : word_t; + + 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 ram_write_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; + + signal fill_word_index : unsigned(word_index_width-1 downto 0); + signal req_word_index : unsigned(word_index_width-1 downto 0); + +begin + + -- Print info + assert false report "addr_width = " & natural'image(addr_width) & "." severity note; + assert false report "tag_width = " & natural'image(tag_width) & "." severity note; + assert false report "word_index_width = " & natural'image(word_index_width) & "." severity note; + assert false report "cache_index_width = " & natural'image(cache_index_width) & "." severity note; + +-- tlb_query_out.vld <= cache_req_rd; +-- tlb_query_out.vaddr <= request_va; +-- tlb_query_out.write <= '0'; + tlb_query_out.vld <= req_strobe; + tlb_query_out.vaddr <= addr; + tlb_query_out.write <= '0'; + + cache_index_inv <= ctrl_in.inv_addr(cache_index_width+word_index_width+1 downto word_index_width+2); + tag_inv <= ctrl_in.inv_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + request_pa <= tlb_query_in.paddr; + + debug.req_strobe <= req_strobe; + debug.req_read <= cache_req_rd; + debug.req_va <= request_va; + debug.req_pa <= request_pa; + debug.dout_reg <= cache_dout; + debug.ready <= cache_rdy; + +------------------------------------------------- +-- ctrl +------------------------------------------------- +req_strobe <= cache_rdy and en; + +ctrl_request_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' then + cache_req_rd <= en; + end if; + end if; + end process; + +------------------------------------------------- +fill_address_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + request_va <= (others => '0'); + elsif req_strobe = '1' then + request_va <= addr; + fill_word_index <= to_word_index(addr); + elsif bus_din_vld = '1' and fill_count_en = '1' then + fill_word_index <= fill_word_index + 1; + end if; + end if; + end process; + +bus_request_address_register: + process(clk) + begin + if rising_edge(clk) then + if req_strobe = '1' then + req_word_index <= to_word_index(addr); + elsif request_count_en = '1' and bus_cmd_rdy = '1' then + req_word_index <= req_word_index + 1; + end if; + end if; + end process; + +single_read_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + single_read_en <= '0'; + single_read_reg_vld <= '0'; + elsif single_read_en = '0' then + if single_read_set = '1' then + single_read_en <= '1'; + end if; + elsif single_read_reg_vld = '1' then + if en = '1' then + single_read_en <= '0'; + single_read_reg_vld <= '0'; + end if; + elsif bus_din_vld = '1' and single_read_en = '1' then + single_read_reg <= bus_din; + single_read_reg_vld <= '1'; + end if; + end if; + end process; + +cache_invalidate_request: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' or ctrl_in.inv_all = '1' or ctrl_in.inv_at = '1' then + invalidate_req <= '1'; + invalidate_all <= ctrl_in.inv_all or rst; + 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_2w2r2c_ra + GENERIC MAP + ( + addr_width => tag_ram_addr_width, + data_width => tag_ram_data_width + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + 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_1w1r2c_ra + GENERIC MAP + ( + addr_width => lg2(CACHE_SIZE), + data_width => word_t'length + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + 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) + begin + if rising_edge(clk) then + if rst = '1' then + cache_ctrl_state <= init; + cache_busy_state <= init; + else + cache_ctrl_state <= cache_ctrl_state_next; + cache_busy_state <= cache_busy_state_next; + end if; + end if; + end process; + + bus_din_rdy <= fill_count_en or single_read_en; + bus_cmd_out.addr <= to_tag(request_pa) & to_cache_index(request_pa) & req_word_index & "00"; + bus_cmd_out.rw <= '0'; + bus_cmd_out.sel <= (others => '1'); + + cache_rdy <= read_rdy; + rdy <= cache_rdy; + + cache_dout <= single_read_reg when single_read_en = '1' else data_ram_data_rd; + dout <= cache_dout; + + cache_entry_out <= to_icache_entry(to_mips_01(tag_ram_data_rd)); + tag_match <= '1' when ((to_tag(request_pa) xor cache_entry_out.tag) = (tag_width-1 downto 0 => '0')) else '0'; + cache_hit <= tag_match and cache_entry_out.valid; + + cache_entry_out_inv <= to_icache_entry(to_mips_01(tag_ram_data_rd_inv)); + 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; + + ram_read_en <= en or was_miss; + 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 to_cache_index(request_va); + tag_ram_addr_rd <= to_cache_index(addr) when was_miss = '0' else to_cache_index(request_va); + data_ram_addr_rd <= (to_cache_index(addr) & to_word_index(addr)) when was_miss = '0' else (to_cache_index(request_va) & fill_word_index); + data_ram_addr_wr <= to_cache_index(request_va) & fill_word_index; + data_ram_data_wr <= bus_din; + data_ram_we <= ram_write_en and bus_din_vld; + +cache_busy_state_fsm: + process(cache_busy_state, tlb_query_in, cache_req_rd, cache_hit, mem_fetch_ack, single_read_en, single_read_reg_vld) + begin + + read_rdy <= '0'; + mem_fetch_req <= '0'; + single_read_set <= '0'; + cache_busy_state_next <= cache_busy_state; + + case cache_busy_state is + + when init => + cache_busy_state_next <= ready; + + when ready => + if single_read_en = '1' then + read_rdy <= single_read_reg_vld; + else + read_rdy <= '1'; + if cache_req_rd = '1' then + if tlb_query_in.exc = '0' then + if tlb_query_in.flags.N = '1' or cache_hit = '0' then +-- mem_fetch_req <= '1'; + read_rdy <= '0'; + single_read_set <= tlb_query_in.flags.N; + cache_busy_state_next <= busy; + end if; + end if; + end if; + end if; + + when busy => + mem_fetch_req <= '1'; + if mem_fetch_ack = '1' then + cache_busy_state_next <= ready; + end if; + + when others => + cache_busy_state_next <= ready; + + end case; + + end process; + +cache_ctrl_state_fsm: + process(cache_ctrl_state, mem_fetch_req, tlb_query_in, bus_din_vld, cache_hit_inv, flush_count_rdy, fill_count_rdy, request_count_rdy, request_pa, bus_cmd_rdy, invalidate_req, invalidate_all) + begin + tag_ram_we <= '0'; + flush_count_en <= '0'; + flush_count_rst <= '0'; + invalidate_en <= '0'; + request_count_en <= '0'; + fill_count_en <= '0'; + ram_write_en <= '0'; + bus_cmd_cycle_en <= '0'; + bus_cmd_we <= '0'; + was_miss <= '0'; + invalidate_ack <= '0'; + mem_fetch_ack <= '0'; + + cache_entry_in.tag <= to_tag(request_pa); + cache_entry_in.valid <= '0'; + cache_ctrl_state_next <= cache_ctrl_state; + + case cache_ctrl_state is + + when init => + cache_ctrl_state_next <= ready; + + when ready => + if invalidate_req = '1' then + cache_ctrl_state_next <= invalidate; + invalidate_en <= '1'; + elsif mem_fetch_req = '1' then + cache_ctrl_state_next <= bus_request; + bus_cmd_cycle_en <= '1'; + end if; + + when invalidate => + cache_ctrl_state_next <= rd_cache; + invalidate_en <= '1'; + invalidate_ack <= '1'; + if invalidate_all = '1' then + cache_ctrl_state_next <= 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'); + cache_ctrl_state_next <= invalidate_post; + 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'; + cache_ctrl_state_next <= invalidate_post; + end if; + + when invalidate_post => + was_miss <= '1'; + cache_ctrl_state_next <= ready; + + when bus_request => + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + cache_ctrl_state_next <= mem_read; + if tlb_query_in.flags.N = '1' then + cache_ctrl_state_next <= mem_read_single; + end if; + end if; + + when mem_read => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + request_count_en <= '1'; + ram_write_en <= '1'; + fill_count_en <= '1'; + if request_count_rdy = '1' then + bus_cmd_we <= '0'; + cache_ctrl_state_next <= mem_read_data; + end if; + + when mem_read_single => + mem_fetch_ack <= '1'; + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_ctrl_state_next <= mem_read_data_single; + + when mem_read_data => + bus_cmd_cycle_en <= '1'; + fill_count_en <= '1'; + ram_write_en <= '1'; + if fill_count_rdy = '1' then + tag_ram_we <= '1'; + cache_entry_in.valid <= '1'; + cache_ctrl_state_next <= rd_cache; + end if; + + when mem_read_data_single => + bus_cmd_cycle_en <= '1'; + if bus_din_vld = '1' then + cache_ctrl_state_next <= ready; + end if; + + when rd_cache => + mem_fetch_ack <= '1'; + was_miss <= '1'; + cache_ctrl_state_next <= ready; + + when others => + cache_ctrl_state_next <= ready; + end case; + + end process; + +flush_counter: + process(clk) + begin + if rising_edge(clk) then + if ctrl_in.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) + begin + if rising_edge(clk) then + if request_count_en = '0' then + request_count_rdy <= '0'; + request_count <= 2**word_index_width-1; + else + if bus_cmd_rdy = '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) + begin + if rising_edge(clk) then + if fill_count_en = '0' then + fill_count_rdy <= '0'; + fill_count <= 2**word_index_width-1; + else + if bus_din_vld = '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; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/mips_pipeline.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_pipeline.vhd new file mode 100644 index 0000000..e61aa27 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_pipeline.vhd @@ -0,0 +1,877 @@ +-------------------------------------------------------------------------- +-- Project: JIPS, a portable 32-bit RISC CPU written in VHDL +-- This file: The pipeline +-- +-- 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; +use work.mips_instr.all; +use work.mips_util_pkg.all; + +entity pipeline is + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + ce : in STD_LOGIC; + imem_err : in STD_LOGIC; + imem_rdy : in STD_LOGIC; + imem_en : out STD_LOGIC; + imem_addr : out word_t; + imem_data : in word_t; + dmem_err : in STD_LOGIC; + dmem_rdy : in STD_LOGIC; + dmem_en : out STD_LOGIC; + dmem_we : out STD_LOGIC; + dmem_be : out unsigned(3 downto 0); + dmem_addr : out word_t; + dmem_din : in word_t; + dmem_dout : out word_t; + cop_ir : out word_t; + cop_ir_en : out STD_LOGIC; + cop_din : in word_t; + cop_dout : out word_t; + ctrl_out : out pipe_ctrl_out_t; + ctrl_in : in pipe_ctrl_in_t + + ); +end pipeline; + +architecture Behavioral of pipeline is + +-------------------------------------------------------------------------- + COMPONENT reg_dual is + Generic + ( + addr_width : integer := 3; + data_width : integer := 8 + ); + Port + ( + clk_w : in STD_LOGIC; + en : in STD_LOGIC; + we : in STD_LOGIC; + wptr : in unsigned (addr_width-1 downto 0); + din : in unsigned (data_width-1 downto 0); + rptr_a : in unsigned (addr_width-1 downto 0); + rptr_b : in unsigned (addr_width-1 downto 0); + dout_a : out unsigned (data_width-1 downto 0); + dout_b : out unsigned (data_width-1 downto 0) + ); + END COMPONENT; + +-------------------------------------------------------------------------- + COMPONENT shifter is + Generic + ( + data_width : integer + ); + Port + ( + shift_ctrl : in shift_ctrl_t; + din : in unsigned (data_width-1 downto 0); + dout : out unsigned (data_width-1 downto 0) + ); + END COMPONENT; + +-------------------------------------------------------------------------- + COMPONENT alu is + Generic + ( + data_width : integer + ); + Port + ( + op1_in : in unsigned (data_width-1 downto 0); + op2_in : in unsigned (data_width-1 downto 0); + op2_shifted : in unsigned (data_width-1 downto 0); + ctrl : in alu_ctrl_t; + result : out unsigned (data_width-1 downto 0); + flags : out alu_flags_t + ); + END COMPONENT; + +-------------------------------------------------------------------------- + COMPONENT bcu is + Generic + ( + data_width : integer + ); + Port + ( + op1_in : in unsigned (data_width-1 downto 0); + op2_in : in unsigned (data_width-1 downto 0); + flags : out bcu_flags_t + ); + END COMPONENT; + +-------------------------------------------------------------------------- + COMPONENT muldiv is + Port + ( + rst : in std_logic; + clk : in std_logic; + hilo_we : in std_logic; + din_hi : in word_t; + din_lo : in word_t; + mul_divn : in std_logic; + start : in std_logic; + s_un : in std_logic; + hilo_sel : in std_logic; + busy : out std_logic; + dout : out word_t + ); + END COMPONENT; + +-------------------------------------------------------------------------- + signal ID_stage : ID_t; + signal EX_stage : EX_t; + signal MEM_stage : MEM_t; + signal WB_stage : WB_t; + signal clk_2, clk_1 : STD_LOGIC; + signal hdu : hdu_t; + signal sdu : sdu_t; + signal reg_a : word_t; + signal reg_b : word_t; + signal branch_ce : STD_LOGIC; + signal cpu_run : STD_LOGIC; + signal alu_result : word_t; + signal mul_result : word_t; + signal mul_busy : STD_LOGIC; + signal EX_events_instr : event_t; + signal EX_events_alu : event_t; + signal EX_events_mem : event_t; + signal EX_events : event_t; + signal MEM_events : event_t; + signal bcu_op_a : word_t; + signal bcu_op_b : word_t; + signal bcu_flags : bcu_flags_t; + signal vaddr1 : word_t; + signal vaddr2 : word_t; + signal dmem_src : word_t; + signal stage_rst : unsigned(3 downto 0); + signal pipe_rst : STD_LOGIC; + + signal pc : pc_t; + +-------------------------------------------------------------------------- +begin + + clk_1 <= clk; + clk_2 <= not clk; + cpu_run <= ce; + + -- Stall Detection Unit --------------------------------------------------- + sdu.ID_nop <= sdu.imem_dep or ctrl_in.exc_pending or ID_stage.exc or EX_stage.exc or MEM_stage.exc; + sdu.EX_nop <= sdu.mul_dep or ID_stage.nop or ID_stage.exc; + sdu.MEM_nop <= EX_stage.nop or EX_stage.exc; + sdu.WB_nop <= sdu.dmem_dep or MEM_stage.exc or WB_stage.exc or ctrl_in.exc_pending; + + sdu.stall_all <= sdu.dmem_dep; + sdu.ID_stall <= sdu.stall_all or sdu.imem_dep or sdu.mul_dep or ctrl_in.exc_exit; + sdu.EX_stall <= sdu.stall_all; + sdu.MEM_stall <= sdu.stall_all; + sdu.WB_stall <= '0'; + + sdu.mul_dep <= ID_stage.ctrl.mul_access and (EX_stage.ctrl.mul_start or mul_busy); + sdu.imem_dep <= not imem_rdy; + sdu.dmem_dep <= not dmem_rdy and MEM_stage.ctrl.dmem_en; + --------------------------------------------------------------------------- + imem_en <= cpu_run and not (sdu.mul_dep or sdu.dmem_dep or ctrl_in.exc_commit); + +-------------------------------------------------------------------------- +-- Coprocessor assignments +-------------------------------------------------------------------------- + ctrl_out.sdu <= sdu; + ctrl_out.exc_req <= WB_stage.exc and not WB_stage.exc_last; + ctrl_out.exc_ack <= '1'; + cop_ir <= ID_stage.IR; + cop_ir_en <= ID_stage.ctrl.cop_instr_en; + cop_dout <= EX_stage.reg_b; -- dmem_din when MEM_stage.ctrl.dmem_en = '1' else MEM_stage.ex_result; + +-------------------------------------------------------------------------- +-- Muldiv +-------------------------------------------------------------------------- +inst_muldiv: muldiv + PORT MAP + ( + rst => rst, + clk => clk, + hilo_we => EX_stage.ctrl.mul_hilo_we, + din_hi => EX_stage.reg_a, + din_lo => EX_stage.reg_b, + mul_divn => EX_stage.ctrl.mul_mul_divn, + start => EX_stage.ctrl.mul_start, + s_un => EX_stage.ctrl.mul_s_un, + hilo_sel => EX_stage.ctrl.mul_hilo_sel, + busy => mul_busy, + dout => mul_result + ); + +-------------------------------------------------------------------------- +-- IF stage +-------------------------------------------------------------------------- + imem_addr <= pc.curr; + + pipe_rst <= stage_rst(0); + +proc_stage_reset: + process(clk_1) + begin + if rising_edge(clk_1) then + if rst = '1' then + stage_rst <= (others => '1'); + else + stage_rst <= stage_rst(stage_rst'left-1 downto 0) & '0'; + end if; + end if; + end process; + +proc_stage_pc: + process(pc) + begin + if pc.branch_take = '1' then + pc.curr <= pc.pc_branch after 2 ns; + else + pc.curr <= pc.nxt after 2 ns; + end if; + end process; + +proc_stage_pc_branch: + process(clk_1) + begin + if rising_edge(clk_1) then + if sdu.ID_stall = '0' then + pc.pc_branch <= pc.curr + ID_stage.bimm18; + end if; + end if; + end process; + +proc_stage_branch_ce: + process(clk_1) + begin + if rising_edge(clk_1) then + if pipe_rst = '1' then + branch_ce <= '1'; + else + branch_ce <= ctrl_in.exc_pending; + if sdu.ID_stall = '0' and ctrl_in.exc_inject = '0' then + branch_ce <= '1'; + end if; + end if; + end if; + end process; + +proc_stage_pc_next: + process(clk_1) + begin + if rising_edge(clk_1) then + if ctrl_in.exc_inject = '1' then + pc.nxt <= ctrl_in.exc_vec; + elsif sdu.ID_stall = '0' then + pc.last <= pc.curr; + if ID_stage.ctrl.jump = '1' then + pc.nxt <= ID_stage.jimm32; + elsif ID_stage.ctrl.jump_long = '1' then + pc.nxt <= ID_stage.reg_a; + elsif cpu_run = '1' then + pc.nxt <= pc.curr + 4; + end if; + end if; + end if; + end process; + +proc_stage_branch: + process(clk_2) + begin + if rising_edge(clk_2) and branch_ce = '1' then + pc.branch_take <= '0'; + if EX_stage.ctrl.branch = '1' then + + case EX_stage.ctrl.bc_src is + + when bc_eq_ne => + pc.branch_take <= EX_stage.ctrl.bc_not xor bcu_flags.eq; + + when bc_lez_gtz => + pc.branch_take <= EX_stage.ctrl.bc_not xor (bcu_flags.eq or bcu_flags.ltz); + + when bc_ltz_gez => + pc.branch_take <= EX_stage.ctrl.bc_not xor bcu_flags.ltz; + + when others => null; + end case; + end if; + end if; + end process; + +-------------------------------------------------------------------------- +-- ID stage +-------------------------------------------------------------------------- + ID_stage.IR <= to_01(imem_data); + ID_stage.pcn <= pc.curr; + ID_stage.op <= decode_op(ID_stage.IR); + ID_stage.jimm32 <= extract_jimm32(ID_stage.IR, ID_stage.pcn); + ID_stage.bimm18 <= extract_bimm18(ID_stage.IR, ID_stage.pcn); + ID_stage.shamt <= extract_shamt(ID_stage.IR); + ID_stage.reg_a_rptr <= extract_rs(ID_stage.IR); + ID_stage.reg_b_rptr <= extract_rt(ID_stage.IR); + ID_stage.ctrl <= opcode_ctrl_lines(ID_stage.IR) when sdu.ID_nop = '0' else ctrl_lines_default; + ID_stage.reg_write <= ID_stage.ctrl.reg_write; + ID_stage.epc <= pc.last; + ID_stage.exc <= event_is_active(ID_stage.events); -- Todo: works + ID_stage.nop <= sdu.ID_nop; + ID_stage.va <= ID_stage.reg_a + extract_simm32(ID_stage.IR); + +proc_ID_except: + process(ID_stage, ctrl_in, pc) + begin + ID_stage.events <= events_clr; + ID_stage.events.inst_load_err <= not ctrl_in.exc_pending and (pc.nxt(1) or pc.nxt(0)); + ID_stage.events.inst_priv_addr <= not ctrl_in.exc_pending and (pc.nxt(word_t'left) and ctrl_in.user_mode); + ID_stage.events.ITLB_LOAD <= ctrl_in.ITLB.exc_vld and ctrl_in.ITLB.exc_miss and not ctrl_in.ITLB.write; + ID_stage.events.ITLB_STORE <= ctrl_in.ITLB.exc_vld and ctrl_in.ITLB.exc_miss and ctrl_in.ITLB.write; + ID_stage.events.ITLB_MOD <= ctrl_in.ITLB.exc_vld and ctrl_in.ITLB.exc_dirty; + end process; + +proc_stage_hdu: + process(ID_stage, EX_stage, MEM_stage, WB_stage) + variable read_a, read_b : boolean; + variable raw_a_EX, raw_a_MEM, raw_a_WB : boolean; + variable raw_b_EX, raw_b_MEM, raw_b_WB : boolean; + variable reg_ptr_a : reg_ptr_t; + variable reg_ptr_b : reg_ptr_t; + begin + + reg_ptr_a := ID_stage.reg_a_rptr; + reg_ptr_b := ID_stage.reg_b_rptr; + raw_a_EX := reg_ptr_a = EX_stage.reg_wptr and EX_stage.reg_we = '1'; + raw_a_MEM := reg_ptr_a = MEM_stage.reg_wptr and MEM_stage.reg_we = '1'; + raw_a_WB := reg_ptr_a = WB_stage.reg_wptr and WB_stage.reg_we = '1'; + raw_b_EX := reg_ptr_b = EX_stage.reg_wptr and EX_stage.reg_we = '1'; + raw_b_MEM := reg_ptr_b = MEM_stage.reg_wptr and MEM_stage.reg_we = '1'; + raw_b_WB := reg_ptr_b = WB_stage.reg_wptr and WB_stage.reg_we = '1'; + + hdu.alu_fwd_a_ex <= raw_a_EX after 1 ns; + hdu.alu_fwd_a_mem <= raw_a_MEM after 1 ns; + hdu.alu_fwd_a_wb <= raw_a_WB after 1 ns; + hdu.alu_fwd_b_ex <= raw_b_EX after 1 ns; + hdu.alu_fwd_b_mem <= raw_b_MEM after 1 ns; + hdu.alu_fwd_b_wb <= raw_b_WB after 1 ns; + + end process; + +proc_stage_fwd_a: + process(reg_a, hdu, EX_stage, MEM_stage, WB_stage) + variable data : word_t; + begin + data := reg_a; + if hdu.alu_fwd_a_ex then + data := EX_stage.result; + elsif hdu.alu_fwd_a_mem then + data := MEM_stage.reg_data; + elsif hdu.alu_fwd_a_wb then + data := WB_stage.reg_data; + end if; + + for i in 0 to 31 loop + if data(i) = '1' then + ID_stage.reg_a(i) <= '1' after 1 ns; + else + ID_stage.reg_a(i) <= '0' after 1 ns; + end if; + end loop; + + end process; + +proc_stage_fwd_b: + process(reg_b, hdu, EX_stage, MEM_stage, WB_stage) + variable data : word_t; + begin + data := reg_b; + if hdu.alu_fwd_b_ex then + data := EX_stage.result; + elsif hdu.alu_fwd_b_mem then + data := MEM_stage.reg_data; + elsif hdu.alu_fwd_b_wb then + data := WB_stage.reg_data; + end if; + ID_stage.reg_b <= to_mips_01(data) after 1 ns; + + end process; + +proc_imm_mux: + process(ID_stage) + variable data : word_t; + begin + data := extract_uimm16(ID_stage.IR); + + case ID_stage.ctrl.imm_src is + + when src_imm32 => + data := extract_simm32(ID_stage.IR); + + when src_imm16 => + data := extract_uimm16(ID_stage.IR); + + when src_imm16_high => + data := ID_stage.IR(word_t'length/2-1 downto 0) & (word_t'length/2-1 downto 0 => '0'); + + when others => null; + + end case; + + ID_stage.imm <= data after 1 ns; + + end process; + +-------------------------------------------------------------------------- +inst_reg_dual: reg_dual + GENERIC MAP + ( + addr_width => reg_ptr_t'length, + data_width => word_t'length + ) + PORT MAP + ( + clk_w => clk_1, + we => WB_stage.reg_we, + en => '1', + wptr => WB_stage.reg_wptr, + din => WB_stage.reg_data, + rptr_a => ID_stage.reg_a_rptr, + rptr_b => ID_stage.reg_b_rptr, + dout_a => reg_a, + dout_b => reg_b + ); + +-------------------------------------------------------------------------- +-- EX stage +-------------------------------------------------------------------------- + EX_stage.reg_a_rptr <= extract_rs(EX_stage.IR); + EX_stage.reg_b_rptr <= extract_rt(EX_stage.IR); + EX_stage.result <= alu_result when EX_stage.ctrl.mul_access = '0' else mul_result; + +proc_stage_ID_EX_1: + process(clk_1) + begin + if rising_edge(clk_1) then + if stage_rst(1) = '1' then + EX_stage.op <= op_nop; + EX_stage.IR <= (others => '0'); + EX_stage.ctrl <= ctrl_lines_default; + EX_stage.reg_write <= '0'; + EX_stage.epc <= (others => '0'); + EX_stage.pcn <= (others => '0'); + EX_stage.events_in <= events_clr; + elsif sdu.EX_stall = '0' then + EX_stage.nop <= sdu.EX_nop; + EX_stage.op <= ID_stage.op; + EX_stage.IR <= ID_stage.IR; + EX_stage.va <= ID_stage.va; + + EX_stage.reg_a <= ID_stage.reg_a; + EX_stage.reg_b <= ID_stage.reg_b; + EX_stage.events_in <= ID_stage.events; + EX_stage.ctrl <= ID_stage.ctrl; + EX_stage.reg_write <= ID_stage.reg_write; + EX_stage.pcn <= ID_stage.pcn; + EX_stage.epc <= ID_stage.epc; + if sdu.EX_nop = '1' then + EX_stage.IR <= (others => '0'); + EX_stage.ctrl <= ctrl_lines_default; + EX_stage.reg_write <= '0'; + end if; + end if; + end if; + end process; + +proc_stage_EX_instr_except: + process(EX_stage, ctrl_in) + begin + EX_events_instr <= events_clr; + EX_events_instr.illegal <= EX_stage.ctrl.exc_illegal; + EX_events_instr.break <= EX_stage.ctrl.exc_break; + EX_events_instr.syscall <= EX_stage.ctrl.exc_syscall; + end process; + +proc_stage_EX_alu_except: + process(EX_stage) + begin + EX_events_alu <= events_clr; + if EX_stage.ctrl.alu_exc_en = '1' then + if EX_stage.alu_flags.ovf = '1' then + EX_events_alu.alu_ovf <= '1'; + end if; + if EX_stage.alu_flags.uvf = '1' then + EX_events_alu.alu_uvf <= '1'; + end if; + end if; + end process; + +proc_stage_EX_mem_except: + process(clk_1) + begin + if rising_edge(clk_1) then + if pipe_rst = '1' then + dmem_en <= '0'; + elsif sdu.EX_stall = '0' then + EX_events_mem <= events_clr; + dmem_en <= '0'; + if ID_stage.ctrl.dmem_en = '1' then + dmem_en <= '1'; + if ID_stage.ctrl.except_en = '1' then + if ID_stage.ctrl.word2_en = '1' then + if ID_stage.va(0) = '1' then + EX_events_mem.data_load_err <= not ID_stage.ctrl.dmem_we; + EX_events_mem.data_store_err <= ID_stage.ctrl.dmem_we; + dmem_en <= '0'; + end if; + elsif ID_stage.va(1 downto 0) /= "00" then + EX_events_mem.data_load_err <= not ID_stage.ctrl.dmem_we; + EX_events_mem.data_store_err <= ID_stage.ctrl.dmem_we; + dmem_en <= '0'; + end if; + end if; + end if; + end if; + end if; + end process; + + ctrl_out.dtlb_va <= ID_stage.va; + ctrl_out.dtlb_en <= ID_stage.ctrl.dmem_en; + +proc_stage_DMEM_ADDR: + process(clk_1) + begin + if rising_edge(clk_1) then + if sdu.EX_stall = '0' then + if ctrl_in.EB = '1' then + if ID_stage.ctrl.word2_en = '0' then + EX_stage.pa_off <= not ID_stage.va(1 downto 0); + else + EX_stage.pa_off <= not ID_stage.va(1) & ID_stage.va(0); + end if; + else + EX_stage.pa_off <= ID_stage.va(1 downto 0); + end if; + end if; + end if; + end process; + dmem_src <= cop_din when EX_stage.ctrl.cop_instr_en = '1' else EX_stage.reg_b; + dmem_be <= store_be(EX_stage.pa_off, EX_stage.ctrl.dmem_en, EX_stage.ctrl.word2_en, EX_stage.ctrl.word4_en, EX_stage.ctrl.align_left, EX_stage.ctrl.shift_byp) after 1 ns; + dmem_we <= EX_stage.ctrl.dmem_we; + dmem_dout <= store_shift(dmem_src, EX_stage.pa_off, EX_stage.ctrl.shift_offset, EX_stage.ctrl.shift_byp) after 1ns; + dmem_addr <= EX_stage.va; + EX_stage.events <= EX_events_instr or EX_events_mem or EX_events_alu or EX_stage.events_in; + EX_stage.exc <= event_is_active(EX_stage.events); + +-------------------------------------------------------------------------- +proc_wptr_mux: + process(EX_stage) + variable opclass : opcode_t; + variable reg_wptr : reg_ptr_t; + begin + + opclass := extract_opc(EX_stage.IR); + case opclass is + when "000000" => + reg_wptr := extract_rd(EX_stage.IR); + when others => + reg_wptr := extract_rt(EX_stage.IR); + end case; + + EX_stage.reg_we <= EX_stage.reg_write after 1 ns; + if reg_wptr = "00000" then + EX_stage.reg_we <= '0' after 1 ns; + end if; + + EX_stage.reg_wptr <= reg_wptr after 1 ns; + case EX_stage.ctrl.wptr_srcsel is + when wptr_src_imm => + EX_stage.reg_wptr <= reg_wptr after 1 ns; + + when wptr_src_const => + EX_stage.reg_wptr <= to_unsigned(31, reg_ptr_t'length) after 1 ns; + EX_stage.reg_we <= '1' after 1 ns; + + when others => null; + end case; + + end process; + +-------------------------------------------------------------------------- +proc_stage_bcu_op: + process(clk_1) + begin + if rising_edge(clk_1) and sdu.EX_stall = '0' then + bcu_op_a <= ID_stage.reg_a; + bcu_op_b <= ID_stage.reg_b; + end if; + end process; + +EX_stage.alu_op1 <= EX_stage.reg_a; + +alu_op2_mux: + process(clk_1) + variable data : word_t; + begin + + if rising_edge(clk_1) and sdu.EX_stall = '0' then + data := ID_stage.reg_b; + + case ID_stage.ctrl.alu.op2_src is + + when alu_src_reg => + data := ID_stage.reg_b; + + when alu_src_imm => + data := ID_stage.imm; + + when others => null; + + end case; + + EX_stage.alu_op2 <= data; + end if; + + end process; + +shifter_sa_mux: + process(clk_1) + variable data : shamt_t; + variable data_inv : shamt_t; + + begin + if rising_edge(clk_1) and sdu.EX_stall = '0' then + data := ID_stage.reg_a(4 downto 0); + case ID_stage.ctrl.shamt2_srcsel is + + when sa_src_reg => + data := ID_stage.reg_a(4 downto 0); + + when sa_src_imm => + data := ID_stage.shamt; + + when others => null; + + end case; + data_inv := not data + 1; + if ID_stage.ctrl.alu.shift_right = '0' then + EX_stage.shift_ctrl.shamt_rnd <= data_inv after 1 ns; + else + EX_stage.shift_ctrl.shamt_rnd <= data after 1 ns; + end if; + EX_stage.shift_ctrl.shamt_nrm <= data after 1 ns; + EX_stage.shift_ctrl.shift_right <= ID_stage.ctrl.alu.shift_right; + EX_stage.shift_ctrl.shift_arith <= ID_stage.ctrl.alu.shift_arith; + end if; + + end process; + +-------------------------------------------------------------------------- +inst_shifter: shifter + GENERIC MAP + ( + data_width => word_t'length + ) + PORT MAP + ( + shift_ctrl => EX_stage.shift_ctrl, + din => EX_stage.reg_b, + dout => EX_stage.alu_op2_s + ); + +inst_alu: alu + GENERIC MAP + ( + data_width => word_t'length + ) + PORT MAP + ( + op1_in => EX_stage.alu_op1, + op2_in => EX_stage.alu_op2, + op2_shifted => EX_stage.alu_op2_s, + ctrl => EX_stage.ctrl.alu, + result => alu_result, + flags => EX_stage.alu_flags + ); + +inst_bcu: bcu + GENERIC MAP + ( + data_width => word_t'length + ) + PORT MAP + ( + op1_in => bcu_op_a, + op2_in => bcu_op_b, + flags => bcu_flags + ); + +-------------------------------------------------------------------------- +-- MEM stage +-------------------------------------------------------------------------- +proc_stage_MEM_n: + process(clk_1) + begin + if rising_edge(clk_1) then + if stage_rst(2) = '1' then + MEM_stage.op <= op_nop; + MEM_stage.reg_we <= '0'; + MEM_stage.ctrl <= ctrl_lines_default; + MEM_stage.pa_off <= (others => '0'); + MEM_stage.events_in <= events_clr; + elsif sdu.MEM_stall = '0' then + MEM_stage.nop <= sdu.MEM_nop; + MEM_stage.op <= EX_stage.op; + MEM_stage.epc <= EX_stage.epc; + MEM_stage.pcn <= EX_stage.pcn; + MEM_stage.events_in <= EX_stage.events; + MEM_stage.reg_we <= EX_stage.reg_we; + MEM_stage.ctrl <= EX_stage.ctrl; + if EX_stage.ctrl.dmem_en = '1' then + MEM_stage.va <= EX_stage.va; + end if; + MEM_stage.pa_off <= EX_stage.pa_off; + MEM_stage.reg_wptr <= EX_stage.reg_wptr; + if MEM_stage.reg_wptr = EX_stage.reg_wptr then + if (EX_stage.ctrl.dmem_en and MEM_stage.ctrl.dmem_en) = '1' then + MEM_stage.ex_result <= MEM_stage.reg_data; + end if; + else + MEM_stage.ex_result <= EX_stage.reg_b; + end if; + if EX_stage.ctrl.cop_instr_en = '1' then + if EX_stage.ctrl.cop_read = '1' then + MEM_stage.ex_result <= cop_din; + end if; + elsif EX_stage.ctrl.dmem_en = '0' then + MEM_stage.ex_result <= EX_stage.result; + end if; + if sdu.MEM_nop = '1' then + MEM_stage.reg_we <= '0'; + MEM_stage.ctrl <= ctrl_lines_default; + end if; + end if; + end if; + end process; + +proc_stage_MEM_mux: + process(MEM_stage, dmem_din) + variable temp1 : word_t; + variable temp2 : word_t; + variable data : word_t; + variable be : unsigned(3 downto 0); + begin + data := MEM_stage.ex_result; + if MEM_stage.ctrl.reg_link = '1' then + data := MEM_stage.epc + 8; + elsif MEM_stage.ctrl.dmem_en = '1' then + temp1 := load_shift(dmem_din, MEM_stage.pa_off, MEM_stage.ctrl.shift_offset, MEM_stage.ctrl.shift_byp); + temp2 := load_sign_ext(temp1, MEM_stage.ctrl.sign_ext_byp, MEM_stage.ctrl.load_signed, MEM_stage.ctrl.word2_en, MEM_stage.ctrl.word4_en); + be := load_be(MEM_stage.pa_off, MEM_stage.ctrl.align_left, MEM_stage.ctrl.byte_en_byp); + if be(0) = '1' then + data(7 downto 0) := temp2(7 downto 0); + end if; + if be(1) = '1' then + data(15 downto 8) := temp2(15 downto 8); + end if; + if be(2) = '1' then + data(23 downto 16) := temp2(23 downto 16); + end if; + if be(3) = '1' then + data(31 downto 24) := temp2(31 downto 24); + end if; + end if; + MEM_stage.reg_data <= data after 1 ns; + + end process; + +proc_stage_MEM_except: + process(MEM_stage, ctrl_in) + begin + MEM_stage.events <= MEM_stage.events_in; + MEM_stage.events.Int <= ctrl_in.int; + MEM_stage.events.NMI <= ctrl_in.NMI; + MEM_stage.events.DTLB_LOAD <= ctrl_in.DTLB.exc_vld and ctrl_in.DTLB.exc_miss and not ctrl_in.DTLB.write; + MEM_stage.events.DTLB_STORE <= ctrl_in.DTLB.exc_vld and ctrl_in.DTLB.exc_miss and ctrl_in.DTLB.write; + MEM_stage.events.DTLB_MOD <= ctrl_in.DTLB.exc_vld and ctrl_in.DTLB.exc_dirty; + end process; + + MEM_stage.exc <= event_is_active(MEM_stage.events); + +-------------------------------------------------------------------------- +-- WB stage +-------------------------------------------------------------------------- +proc_stage_WB_p: + process(clk_1) + variable branch_delay : std_logic; + + begin + if rising_edge(clk_1) then + if stage_rst(3) = '1' then + WB_stage.op <= op_nop; + WB_stage.reg_we <= '1'; + WB_stage.reg_wptr <= (others => '0'); + WB_stage.epc <= (others => '0'); + WB_stage.events <= events_clr; + WB_stage.exc <= '0'; + elsif sdu.WB_stall = '0' then + WB_stage.nop <= sdu.WB_nop; + WB_stage.op <= MEM_stage.op; + WB_stage.epc_last <= WB_stage.epc; + WB_stage.epc <= MEM_stage.epc; + branch_delay := MEM_stage.ctrl.branch or MEM_stage.ctrl.jump or MEM_stage.ctrl.jump_long; -- Todo: works + WB_stage.bd <= branch_delay; + WB_stage.exc_last <= WB_stage.exc; + WB_stage.exc <= MEM_stage.exc; + WB_stage.reg_we <= MEM_stage.reg_we; + WB_stage.reg_wptr <= MEM_stage.reg_wptr; + WB_stage.reg_data <= MEM_stage.reg_data; + WB_stage.events <= MEM_stage.events; + if sdu.stall_all = '0' then + if MEM_stage.exc = '1' and WB_stage.exc = '0' then + ctrl_out.dmem_addr <= MEM_stage.va; + ctrl_out.imem_addr <= MEM_stage.epc; + ctrl_out.epc_wb <= MEM_stage.epc; + ctrl_out.bd_wb <= WB_stage.bd; + ctrl_out.events <= MEM_stage.events; + if WB_stage.bd = '1' then + ctrl_out.epc_wb <= WB_stage.epc; + end if; + end if; + end if; + if sdu.WB_nop = '1' then + WB_stage.reg_we <= '0'; + end if; + if WB_stage.exc = '0' then + WB_stage.va <= MEM_stage.va; + end if; + end if; + end if; + end process; + +-------------------------------------------------------------------------- +end Behavioral; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb.vhd new file mode 100644 index 0000000..421032a --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb.vhd @@ -0,0 +1,340 @@ +LIBRARY IEEE; +USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; + +library work; +use work.mips_types.all; +use work.mips_util_pkg.all; + +ENTITY tlb IS + Generic + ( + NUM_ENTRIES : natural := 8; + CACHE_KSEG1 : boolean := false; + USE_TLB : boolean := false + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + ce : in STD_LOGIC; + + rdy : out STD_LOGIC; + + ctrl_in : in tlb_ctrl_in_t; + ctrl_out : out tlb_ctrl_out_t; + + query_in : in tlb_query_in_t; + query_out : out tlb_query_out_t + + ); +END tlb; + +ARCHITECTURE behavior OF tlb IS + + signal clk_rd : STD_LOGIC; + signal cam_entry_lo : tlb_entry_lo_t; + + + signal entry_lo_UMC : tlb_entry_lo_t; + signal entry_lo_UMUC : tlb_entry_lo_t; + signal entry_lo_res : tlb_entry_lo_t; + + signal qry_res : tlb_query_out_t; + signal stat_res : tlb_status_t; + + signal pa : word_t; + signal hit : STD_LOGIC; + signal valid : STD_LOGIC; + + signal cam_rdy : STD_LOGIC; + signal cam_hit_vld : STD_LOGIC; + + signal vaddr_reg : word_t; + + subtype reg_lo_t is unsigned(23 downto 0); + subtype reg_hi_t is unsigned(25 downto 0); + + signal cam_lo_dout : reg_lo_t; + signal reg_lo_din : reg_lo_t; + signal reg_lo_dout : reg_lo_t; + signal reg_hi_din : reg_hi_t; + signal reg_hi_dout : reg_hi_t; + + function to_entry_lo(x : reg_lo_t) return tlb_entry_lo_t is + variable result : tlb_entry_lo_t; + begin + result.PFN := x(23 downto 4); + result.N := x(3); + result.D := x(2); + result.V := x(1); + result.G := x(0); + + return result; + + end to_entry_lo; + + function to_entry_hi(x : reg_hi_t) return tlb_entry_hi_t is + variable result : tlb_entry_hi_t; + begin + result.VPN := x(25 downto 6); + result.ASID := x(5 downto 0); + + return result; + + end to_entry_hi; + + function to_reg_lo(x : tlb_entry_lo_t) return reg_lo_t is + variable result : reg_lo_t; + begin + result(23 downto 4) := x.PFN; + result(3) := x.N; + result(2) := x.D; + result(1) := x.V; + result(0) := x.G; + + return result; + + end to_reg_lo; + + function to_reg_hi(x : tlb_entry_hi_t) return reg_hi_t is + variable result : reg_hi_t; + begin + result(25 downto 6) := x.VPN; + result(5 downto 0) := x.ASID; + + return result; + + end to_reg_hi; + + signal hit_idx : integer; + function decode(x : unsigned; N : integer) return integer is + variable result : integer; + begin + result := 0; + for i in 0 to N-1 loop + if x(i) = '1' then + result := i; + end if; + end loop; + + return result; + + end decode; + +begin + clk_rd <= clk; + rdy <= cam_rdy; + + stat_res.exc_vld <= valid; + stat_res.exc_miss <= not hit; + stat_res.exc_dirty <= hit and qry_res.flags.D and query_in.write; + stat_res.write <= query_in.write; + + qry_res.paddr <= pa; + qry_res.hit <= hit; + qry_res.flags.N <= entry_lo_res.N; + qry_res.flags.D <= entry_lo_res.D; + qry_res.flags.V <= entry_lo_res.V; + qry_res.exc <= stat_res.exc_miss or stat_res.exc_dirty; + qry_res.vld <= valid; + + query_out <= qry_res; + ctrl_out.stat <= stat_res; + + pa <= entry_lo_res.PFN & vaddr_reg(lg2(TLB_PAGE_SIZE)-1 downto 0); + cam_entry_lo <= to_entry_lo(to_mips_01(cam_lo_dout)); + +inst_reg_entry_lo: entity work.dpram_1w1r2c_ra + GENERIC MAP + ( + addr_width => lg2(NUM_ENTRIES), + data_width => 24 + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + en_a => '1', + en_b => ctrl_in.entry_re, + we_a => ctrl_in.entry_we, + addr_a => ctrl_in.entry_wr_idx(lg2(NUM_ENTRIES)-1 downto 0), + addr_b => ctrl_in.entry_rd_idx(lg2(NUM_ENTRIES)-1 downto 0), + din_a => reg_lo_din, + dout_b => reg_lo_dout + ); + reg_lo_din <= to_reg_lo(ctrl_in.entry_lo); + ctrl_out.entry_lo <= to_entry_lo(to_mips_01(reg_lo_dout)); + +inst_reg_entry_hi: entity work.dpram_1w1r2c_ra + GENERIC MAP + ( + addr_width => lg2(NUM_ENTRIES), + data_width => 26 + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + en_a => '1', + en_b => ctrl_in.entry_re, + we_a => ctrl_in.entry_we, + addr_a => ctrl_in.entry_wr_idx(lg2(NUM_ENTRIES)-1 downto 0), + addr_b => ctrl_in.entry_rd_idx(lg2(NUM_ENTRIES)-1 downto 0), + din_a => reg_hi_din, + dout_b => reg_hi_dout + ); + + reg_hi_din <= to_reg_hi(ctrl_in.entry_hi); + ctrl_out.entry_hi <= to_entry_hi(to_mips_01(reg_hi_dout)); + +inst_cam_va: entity work.cam2 + GENERIC MAP + ( + NUM_ENTRIES => NUM_ENTRIES, + ENTRY_WIDTH => reg_lo_t'length, + DATA_WIDTH => 20, + CAM_RAM_MAX_WIDTH => 8 + ) + PORT MAP + ( + rst => rst, + clk => clk, + + rdy => cam_rdy, + + re => query_in.vld, + we => ctrl_in.entry_we, + vld => '1', + addr => ctrl_in.entry_wr_idx, + entry_din => reg_lo_din, + entry_dout => cam_lo_dout, + + tag_wr => ctrl_in.entry_hi.VPN, + tag_rd => query_in.vaddr(31 downto lg2(TLB_PAGE_SIZE)), + + hit_vld => cam_hit_vld + + ); + + +proc_entry_lo_umc: + process(clk) + variable index : integer; + begin + if rising_edge(clk) then + entry_lo_UMC.N <= '0'; + entry_lo_UMC.D <= '0'; + entry_lo_UMC.G <= '0'; + if query_in.vld = '1' then + entry_lo_UMC.V <= '0'; + if query_in.vaddr(31 downto 29) = "100" then -- 80000000 to 9FFFFFFF + entry_lo_UMC.V <= '1'; + entry_lo_UMC.PFN <= "000" & query_in.vaddr(28 downto lg2(TLB_PAGE_SIZE)); + end if; + end if; + end if; + end process; + +proc_entry_lo_umuc: + process(clk) + variable index : integer; + begin + if rising_edge(clk) then + if CACHE_KSEG1 then + entry_lo_UMUC.N <= '0'; + else + entry_lo_UMUC.N <= '1'; + end if; + entry_lo_UMUC.D <= '0'; + entry_lo_UMUC.G <= '0'; + if query_in.vld = '1' then + entry_lo_UMUC.V <= '0'; + if query_in.vaddr(31 downto 29) = "101" then -- A0000000 to BFFFFFFF + entry_lo_UMUC.V <= '1'; + entry_lo_UMUC.PFN <= "000" & query_in.vaddr(28 downto lg2(TLB_PAGE_SIZE)); + end if; + end if; + end if; + end process; + +tlb_entry_lo_result_translate: +if USE_TLB = true generate + tlb_entry_lo_result: + process(cam_entry_lo, entry_lo_umc, entry_lo_umuc) + begin + entry_lo_res <= cam_entry_lo; + if entry_lo_umc.V = '1' then + entry_lo_res <= entry_lo_umc; + end if; + if entry_lo_umuc.V = '1' then + entry_lo_res <= entry_lo_umuc; + end if; + + end process; +end generate; + +tlb_entry_lo_result_notranslate: +if USE_TLB = false generate + tlb_entry_lo_result: + process(cam_entry_lo, vaddr_reg, entry_lo_umc, entry_lo_umuc) + begin + entry_lo_res.N <= '0'; + entry_lo_res.D <= '0'; + entry_lo_res.V <= '1'; + entry_lo_res.PFN <= vaddr_reg(31 downto lg2(TLB_PAGE_SIZE)); + if entry_lo_umc.V = '1' then + entry_lo_res <= entry_lo_umc; + end if; + if entry_lo_umuc.V = '1' then + entry_lo_res <= entry_lo_umuc; + end if; + + end process; +end generate; + +tlb_hit_combine_translate: +if USE_TLB = true generate + tlb_hit_combine: + process(cam_hit_vld, entry_lo_umc, entry_lo_umuc) + variable t_v : std_logic; + begin + t_v := cam_hit_vld; + t_v := t_v or entry_lo_umc.V or entry_lo_umuc.V; + hit <= t_v; + + end process; +end generate; + +tlb_hit_combine_notranslate: +if USE_TLB = false generate + tlb_hit_combine: + process(cam_entry_lo, entry_lo_umc, entry_lo_umuc) + variable t_v : std_logic; + begin + t_v := entry_lo_umc.V or entry_lo_umuc.V; + hit <= t_v; + + end process; +end generate; + +registered_output: + process(clk) + begin + if rising_edge(clk) then + if query_in.vld = '1' then + vaddr_reg <= query_in.vaddr; + end if; + valid <= query_in.vld; + if ce = '1' then + if ctrl_in.entry_re = '1' then + ctrl_out.entry_vld <= '1'; + end if; + end if; + end if; + end process; + + + +------------------------------------------------------------------ +end behavior; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb_20130811.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb_20130811.vhd new file mode 100644 index 0000000..d6a270a --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb_20130811.vhd @@ -0,0 +1,204 @@ +LIBRARY IEEE; +USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; + +library work; +use work.mips_types.all; +use work.mips_util_pkg.all; + +ENTITY tlb IS + Generic + ( + NUM_ENTRIES : natural := 8; + CACHE_ALL : boolean := false; + USE_CACHE : boolean := true; + USE_TLB : boolean := false + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + ce : in STD_LOGIC; + + rdy : out STD_LOGIC; + + ctrl_in : in tlb_ctrl_in_t; + ctrl_out : out tlb_ctrl_out_t; + + query_in : in tlb_query_in_t; + query_out : out tlb_query_out_t + + ); +END tlb; + +ARCHITECTURE behavior OF tlb IS + + constant UNMAPPED_CACHED : natural := NUM_ENTRIES; + constant UNMAPPED_UNCACHED : natural := NUM_ENTRIES+1; + + signal blk_entry_we : unsigned(NUM_ENTRIES-1 downto 0); + signal blk_hit : unsigned(NUM_ENTRIES-1+2 downto 0); + + type flags_array_t is array (0 to NUM_ENTRIES-1+2) of tlb_flags_t; + signal blk_flags : flags_array_t; + + type word_array_t is array (0 to NUM_ENTRIES-1+2) of word_t; + signal blk_pfn : word_array_t; + + type tlb_entry_lo_array_t is array (0 to NUM_ENTRIES-1) of tlb_entry_lo_t; + signal blk_entry_lo : tlb_entry_lo_array_t; + + type tlb_entry_hi_array_t is array (0 to NUM_ENTRIES-1) of tlb_entry_hi_t; + signal blk_entry_hi : tlb_entry_hi_array_t; + + signal qry_res_no_translate : tlb_query_out_t; + signal qry_res : tlb_query_out_t; + + signal stat_res_no_translate : tlb_status_t; + signal stat_res : tlb_status_t; + + signal pa : word_t; + signal hit : STD_LOGIC; + signal flags : tlb_flags_t; + + +begin + + rdy <= '1'; + + blk_hit(UNMAPPED_CACHED) <= '1' when query_in.vaddr(31 downto 29) = "100" else '0'; + blk_pfn(UNMAPPED_CACHED) <= "000" & query_in.vaddr(28 downto lg2(TLB_PAGE_SIZE)) & (lg2(TLB_PAGE_SIZE)-1 downto 0 => '0'); + blk_flags(UNMAPPED_CACHED).N <= '0'; + blk_flags(UNMAPPED_CACHED).D <= '0'; + blk_flags(UNMAPPED_CACHED).V <= '1'; + + blk_hit(UNMAPPED_UNCACHED) <= '1' when query_in.vaddr(31 downto 29) = "101" else '0'; + blk_pfn(UNMAPPED_UNCACHED) <= "000" & query_in.vaddr(28 downto lg2(TLB_PAGE_SIZE)) & (lg2(TLB_PAGE_SIZE)-1 downto 0 => '0'); + blk_flags(UNMAPPED_UNCACHED).N <= '1'; + blk_flags(UNMAPPED_UNCACHED).D <= '0'; + blk_flags(UNMAPPED_UNCACHED).V <= '1'; + + stat_res.exc_vld <= '0'; + stat_res.exc_miss <= not hit; + stat_res.exc_dirty <= hit and flags.D and query_in.write; + stat_res.write <= query_in.write; + + stat_res_no_translate.exc_vld <= '0'; + stat_res_no_translate.exc_miss <= '0'; + stat_res_no_translate.exc_dirty <= '0'; + stat_res_no_translate.write <= query_in.write; + + qry_res.paddr <= pa; + qry_res.hit <= hit; + qry_res.flags <= flags; + qry_res.exc <= stat_res.exc_miss or stat_res.exc_dirty; + qry_res.vld <= '0'; + + qry_res_no_translate.vld <= '0'; + qry_res_no_translate.paddr <= query_in.vaddr; + qry_res_no_translate.hit <= '1'; + qry_res_no_translate.exc <= '0'; + qry_res_no_translate.flags.N <= '0' when CACHE_ALL = true else '1' when USE_CACHE = false else '1' when query_in.vaddr(31 downto 29) = "101" else '0'; + qry_res_no_translate.flags.D <= '0'; + qry_res_no_translate.flags.V <= '1'; + + +gen_tlb: +for i in 0 to NUM_ENTRIES-1 generate + + inst_tlb_block : entity work.tlb_block + Port map + ( + clk => clk, + rst => rst, + + entry_we => blk_entry_we(i), + entry_hi_in => ctrl_in.entry_hi, + entry_lo_in => ctrl_in.entry_lo, + + entry_hi_out => blk_entry_hi(i), + entry_lo_out => blk_entry_lo(i), + + va => query_in.vaddr, + pfn => blk_pfn(i), + flags => blk_flags(i), + hit => blk_hit(i) + ); + +end generate; + +tlb_blk_write_mux: + process(ctrl_in) + begin + blk_entry_we <= (others => '0'); + blk_entry_we(to_integer(ctrl_in.entry_wr_idx(lg2(NUM_ENTRIES)-1 downto 0))) <= ctrl_in.entry_we; + end process; + +tlb_pa_combine: + process(blk_hit, blk_pfn, query_in.vaddr) + variable t_v : word_t; + begin + for i in 0 to NUM_ENTRIES-1+2 loop + if blk_hit(i) = '1' then + t_v := blk_pfn(i); + end if; + end loop; + pa <= t_v(31 downto lg2(TLB_PAGE_SIZE)) & query_in.vaddr(lg2(TLB_PAGE_SIZE)-1 downto 0); + + end process; + +tlb_flags_combine: + process(blk_hit, blk_flags) + variable t_v : tlb_flags_t; + begin + for i in 0 to NUM_ENTRIES-1+2 loop + if blk_hit(i) = '1' then + t_v := blk_flags(i); + end if; + end loop; + flags <= t_v; + + end process; + +tlb_hit_combine: + process(blk_hit, blk_flags) + variable t_v : std_logic; + begin + t_v := '0'; + for i in 0 to NUM_ENTRIES-1+2 loop + t_v := t_v or (blk_hit(i) and blk_flags(i).V); + end loop; + hit <= t_v; + + end process; + +registered_output: + process(clk) + begin + if rising_edge(clk) then + ctrl_out.entry_vld <= '0'; + if query_in.vld = '1' then + if USE_TLB then + query_out <= qry_res; + ctrl_out.stat <= stat_res; + else + query_out <= qry_res_no_translate; + ctrl_out.stat <= stat_res_no_translate; + end if; + end if; + query_out.vld <= query_in.vld; + ctrl_out.stat.exc_vld <= query_in.vld; + if ce = '1' then + if ctrl_in.entry_re = '1' then + ctrl_out.entry_lo <= blk_entry_lo(to_integer(ctrl_in.entry_rd_idx(lg2(NUM_ENTRIES)-1 downto 0))); + ctrl_out.entry_hi <= blk_entry_hi(to_integer(ctrl_in.entry_rd_idx(lg2(NUM_ENTRIES)-1 downto 0))); + ctrl_out.entry_vld <= '1'; + end if; + end if; + end if; + end process; + + + +------------------------------------------------------------------ +end behavior; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb_block.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb_block.vhd new file mode 100644 index 0000000..14466e3 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb_block.vhd @@ -0,0 +1,85 @@ +LIBRARY IEEE; +USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; + +library work; +use work.mips_types.all; +use work.mips_util_pkg.all; + +ENTITY tlb_block IS + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + entry_we : in STD_LOGIC; + entry_lo_in : in tlb_entry_lo_t; + entry_hi_in : in tlb_entry_hi_t; + entry_lo_out : out tlb_entry_lo_t; + entry_hi_out : out tlb_entry_hi_t; + va : in word_t; + pfn : out word_t; + flags : out tlb_flags_t; + hit : out STD_LOGIC + + ); +END tlb_block; + +ARCHITECTURE behavior OF tlb_block IS + + signal entry_lo : tlb_entry_lo_t; + signal entry_hi : tlb_entry_hi_t; + signal vpn_match : STD_LOGIC; + signal asid_match : STD_LOGIC; + signal tlb_hit : STD_LOGIC; + signal tlb_pfn : word_t; + +begin + +asid_match <= '1' when (entry_hi.ASID = entry_hi_in.ASID) else entry_lo.G; +vpn_match <= '1' when (va(31 downto lg2(TLB_PAGE_SIZE)) = entry_hi.VPN) else '0'; + +tlb_hit <= (asid_match and vpn_match) after 0 ns; +tlb_pfn <= entry_lo.PFN & (lg2(TLB_PAGE_SIZE)-1 downto 0 => '0'); + +pfn <= tlb_pfn after 0 ns; + +flags.N <= entry_lo.N; +flags.D <= entry_lo.D; +flags.V <= entry_lo.V; + +entry_lo_out <= entry_lo; +entry_hi_out <= entry_hi; + +hit <= tlb_hit; + +entry_lo_write: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + entry_lo.N <= '0'; + entry_lo.D <= '0'; + entry_lo.V <= '0'; + entry_lo.G <= '0'; + entry_lo.PFN <= (others => '0'); + elsif entry_we = '1' then + entry_lo <= entry_lo_in; + end if; + end if; + end process; + +entry_hi_write: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + entry_hi.VPN <= (others => '0'); + entry_hi.ASID <= (others => '0'); + elsif entry_we = '1' then + entry_hi <= entry_hi_in; + end if; + end if; + end process; + +------------------------------------------------------------------ +end behavior; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb_with_cam.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb_with_cam.vhd new file mode 100644 index 0000000..7477e34 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb_with_cam.vhd @@ -0,0 +1,350 @@ +LIBRARY IEEE; +USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; + +library work; +use work.mips_types.all; +use work.mips_util_pkg.all; + +ENTITY tlb IS + Generic + ( + NUM_ENTRIES : natural := 8; + CACHE_KSEG1 : boolean := false; + TRANSLATE_KSEG0_1 : boolean := true; + USE_TLB : boolean := false + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + ce : in STD_LOGIC; + + rdy : out STD_LOGIC; + + ctrl_in : in tlb_ctrl_in_t; + ctrl_out : out tlb_ctrl_out_t; + + query_in : in tlb_query_in_t; + query_out : out tlb_query_out_t + + ); +END tlb; + +ARCHITECTURE behavior OF tlb IS + + signal clk_rd : STD_LOGIC; + signal blk_hit : unsigned(NUM_ENTRIES-1 downto 0); + signal blk_entry_lo : tlb_entry_lo_t; + + constant C_ENTRY_LO_DEFAULT : tlb_entry_lo_t := + ( + N => '0', D => '0', V => '1', G => '0', PFN => (others => '0') + ); + + signal kseg1_force_cached : STD_LOGIC; + signal kseg_0_1_pfn : unsigned (31 downto lg2(TLB_PAGE_SIZE)); + signal hit_umc : STD_LOGIC; + signal hit_umuc : STD_LOGIC; + signal entry_lo_res : tlb_entry_lo_t; + signal qry_res : tlb_query_out_t; + signal stat_res : tlb_status_t; + + signal pa : word_t; + signal hit : STD_LOGIC; + signal valid : STD_LOGIC; + + signal cam_rdy : STD_LOGIC; + signal cam_hit_vld : STD_LOGIC; + + signal vaddr_reg : word_t; + + subtype reg_lo_t is unsigned(23 downto 0); + subtype reg_hi_t is unsigned(25 downto 0); + + signal reg_ctrl_entry_lo_din : reg_lo_t; + signal reg_ctrl_entry_lo_dout : reg_lo_t; + signal reg_qry_entry_lo_dout : reg_lo_t; + signal reg_qry_entry_lo_addr : unsigned(lg2(NUM_ENTRIES)-1 downto 0); + signal reg_ctrl_entry_lo_addr : unsigned(lg2(NUM_ENTRIES)-1 downto 0); + + signal reg_ctrl_entry_hi_din : reg_hi_t; + signal reg_ctrl_entry_hi_dout : reg_hi_t; + + function to_entry_lo(x : reg_lo_t) return tlb_entry_lo_t is + variable result : tlb_entry_lo_t; + begin + result.PFN := x(23 downto 4); + result.N := x(3); + result.D := x(2); + result.V := x(1); + result.G := x(0); + + return result; + + end to_entry_lo; + + function to_entry_hi(x : reg_hi_t) return tlb_entry_hi_t is + variable result : tlb_entry_hi_t; + begin + result.VPN := x(25 downto 6); + result.ASID := x(5 downto 0); + + return result; + + end to_entry_hi; + + function to_reg_lo(x : tlb_entry_lo_t) return reg_lo_t is + variable result : reg_lo_t; + begin + result(23 downto 4) := x.PFN; + result(3) := x.N; + result(2) := x.D; + result(1) := x.V; + result(0) := x.G; + + return result; + + end to_reg_lo; + + function to_reg_hi(x : tlb_entry_hi_t) return reg_hi_t is + variable result : reg_hi_t; + begin + result(25 downto 6) := x.VPN; + result(5 downto 0) := x.ASID; + + return result; + + end to_reg_hi; + + signal hit_idx : integer; + function decode(x : unsigned; N : integer) return integer is + variable result : integer; + begin + result := 0; + for i in 0 to N-1 loop + if x(i) = '1' then + result := i; + end if; + end loop; + + return result; + + end decode; + + function decode2(x : unsigned; N : integer) return unsigned is + variable result : unsigned (lg2(N)-1 downto 0); + begin + result := (others => '0'); + for i in 0 to N-1 loop + result := result or ((lg2(N)-1 downto 0 => x(i)) and to_unsigned(i, lg2(N))); + end loop; + + return result; + + end decode2; + +begin + kseg1_force_cached <= '1' when CACHE_KSEG1 else '0'; + kseg_0_1_pfn <= "000" & vaddr_reg(28 downto lg2(TLB_PAGE_SIZE)) when TRANSLATE_KSEG0_1 else vaddr_reg(31 downto lg2(TLB_PAGE_SIZE)); + + clk_rd <= clk; + rdy <= cam_rdy; + + stat_res.exc_vld <= valid; + stat_res.exc_miss <= not hit; + stat_res.exc_dirty <= hit and qry_res.flags.D and query_in.write; + stat_res.write <= query_in.write; + + qry_res.paddr <= pa; + qry_res.hit <= hit; + qry_res.flags.N <= entry_lo_res.N; + qry_res.flags.D <= entry_lo_res.D; + qry_res.flags.V <= entry_lo_res.V; + qry_res.exc <= stat_res.exc_miss or stat_res.exc_dirty; + qry_res.vld <= valid; + qry_res.idx <= (hit_umc or hit_umuc) & reg_qry_entry_lo_addr; + + query_out <= qry_res; + ctrl_out.stat <= stat_res; + + pa <= entry_lo_res.PFN & vaddr_reg(lg2(TLB_PAGE_SIZE)-1 downto 0); + + hit_idx <= decode(blk_hit, NUM_ENTRIES); + + blk_entry_lo <= to_entry_lo(to_mips_01(reg_qry_entry_lo_dout)); +-- reg_qry_entry_lo_addr <= to_unsigned(hit_idx, lg2(NUM_ENTRIES)); + reg_qry_entry_lo_addr <= decode2(blk_hit, NUM_ENTRIES); + reg_ctrl_entry_lo_din <= to_reg_lo(ctrl_in.entry_lo); + + ctrl_out.entry_hi <= to_entry_hi(to_mips_01(reg_ctrl_entry_hi_dout)); + reg_ctrl_entry_hi_din <= to_reg_hi(ctrl_in.entry_hi); + +inst_reg_entry_lo: entity work.reg_dual + GENERIC MAP + ( + addr_width => lg2(NUM_ENTRIES), + data_width => reg_lo_t'length + ) + PORT MAP ( + clk_w => clk, + we => ctrl_in.entry_we, + en => '1', + wptr => ctrl_in.entry_wr_idx(lg2(NUM_ENTRIES)-1 downto 0), + din => reg_ctrl_entry_lo_din, + rptr_a => ctrl_in.entry_rd_idx(lg2(NUM_ENTRIES)-1 downto 0), + rptr_b => reg_qry_entry_lo_addr, + dout_a => reg_ctrl_entry_lo_dout, + dout_b => reg_qry_entry_lo_dout + ); + +inst_reg_entry_hi: entity work.dpram_1w1r2c_ra + GENERIC MAP + ( + addr_width => lg2(NUM_ENTRIES), + data_width => reg_hi_t'length + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + en_a => '1', + en_b => ctrl_in.entry_re, + we_a => ctrl_in.entry_we, + addr_a => ctrl_in.entry_wr_idx(lg2(NUM_ENTRIES)-1 downto 0), + addr_b => ctrl_in.entry_rd_idx(lg2(NUM_ENTRIES)-1 downto 0), + din_a => reg_ctrl_entry_hi_din, + dout_b => reg_ctrl_entry_hi_dout + ); + +inst_cam_va: entity work.cam + GENERIC MAP + ( + NUM_ENTRIES => NUM_ENTRIES, + DATA_WIDTH => 20, + CAM_RAM_MAX_WIDTH => 15 + ) + PORT MAP + ( + rst => rst, + clk => clk, + clk_rd => clk_rd, + rdy => cam_rdy, + + re => query_in.vld, + we => ctrl_in.entry_we, + vld => '1', + addr => ctrl_in.entry_wr_idx, + tag_wr => ctrl_in.entry_hi.VPN, + + tag_rd => query_in.vaddr(31 downto lg2(TLB_PAGE_SIZE)), + hit => blk_hit(NUM_ENTRIES-1 downto 0), + hit_vld => cam_hit_vld + + ); + +proc_entry_lo_umc: + process(clk) + variable index : integer; + begin + if rising_edge(clk) then + if query_in.vld = '1' then + hit_umc <= '0'; + if query_in.vaddr(31 downto 29) = "100" then -- 80000000 to 9FFFFFFF + hit_umc <= '1'; + end if; + end if; + end if; + end process; + +proc_entry_lo_umuc: + process(clk) + variable index : integer; + begin + if rising_edge(clk) then + if query_in.vld = '1' then + hit_umuc <= '0'; + if query_in.vaddr(31 downto 29) = "101" then -- A0000000 to BFFFFFFF + hit_umuc <= '1'; + end if; + end if; + end if; + end process; + +tlb_entry_lo_result_translate: +if USE_TLB = true generate + tlb_entry_lo_result: + process(blk_entry_lo, hit_umc, hit_umuc, kseg_0_1_pfn, kseg1_force_cached) + begin + entry_lo_res <= blk_entry_lo; + if hit_umc = '1' or hit_umuc = '1' then + entry_lo_res.N <= hit_umuc and not kseg1_force_cached; + entry_lo_res <= C_ENTRY_LO_DEFAULT; + entry_lo_res.PFN <= kseg_0_1_pfn; + end if; + + end process; +end generate; + +tlb_entry_lo_result_notranslate: +if USE_TLB = false generate + tlb_entry_lo_result: + process(blk_entry_lo, vaddr_reg, hit_umc, hit_umuc, kseg_0_1_pfn, kseg1_force_cached) + begin + entry_lo_res <= C_ENTRY_LO_DEFAULT; + entry_lo_res.PFN <= vaddr_reg(31 downto lg2(TLB_PAGE_SIZE)); + if hit_umc = '1' or hit_umuc = '1' then + entry_lo_res.N <= hit_umuc and not kseg1_force_cached; + entry_lo_res <= C_ENTRY_LO_DEFAULT; + entry_lo_res.PFN <= kseg_0_1_pfn; + end if; + end process; +end generate; + +tlb_hit_combine_translate: +if USE_TLB = true generate + tlb_hit_combine: + process(blk_entry_lo, hit_umc, hit_umuc) + variable t_v : std_logic; + begin + t_v := blk_entry_lo.V; + t_v := t_v or hit_umc or hit_umuc; + hit <= t_v; + + end process; +end generate; + +tlb_hit_combine_notranslate: +if USE_TLB = false generate + tlb_hit_combine: + process(blk_entry_lo, hit_umc, hit_umuc) + variable t_v : std_logic; + begin + t_v := hit_umc or hit_umuc; + hit <= t_v; + + end process; +end generate; + +registered_output: + process(clk) + begin + if rising_edge(clk) then + if query_in.vld = '1' then + vaddr_reg <= query_in.vaddr; + end if; + valid <= query_in.vld; + if ce = '1' then + if ctrl_in.entry_re = '1' then + + ctrl_out.entry_lo <= to_entry_lo(to_mips_01(reg_ctrl_entry_lo_dout)); + ctrl_out.entry_vld <= '1'; + end if; + end if; + end if; + end process; + + + +------------------------------------------------------------------ +end behavior; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb_with_comp.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb_with_comp.vhd new file mode 100644 index 0000000..8cdadc3 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_tlb_with_comp.vhd @@ -0,0 +1,343 @@ +LIBRARY IEEE; +USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; + +library work; +use work.mips_types.all; +use work.mips_util_pkg.all; + +ENTITY tlb IS + Generic + ( + NUM_ENTRIES : natural := 8; + CACHE_KSEG1 : boolean := false; + USE_TLB : boolean := false + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + ce : in STD_LOGIC; + + rdy : out STD_LOGIC; + + ctrl_in : in tlb_ctrl_in_t; + ctrl_out : out tlb_ctrl_out_t; + + query_in : in tlb_query_in_t; + query_out : out tlb_query_out_t + + ); +END tlb; + +ARCHITECTURE behavior OF tlb IS + + signal clk_rd : STD_LOGIC; + signal blk_hit : unsigned(NUM_ENTRIES-1 downto 0); + signal blk_entry_lo : tlb_entry_lo_t; + + + signal entry_lo_UMC : tlb_entry_lo_t; + signal entry_lo_UMUC : tlb_entry_lo_t; + signal entry_lo_res : tlb_entry_lo_t; + + signal qry_res : tlb_query_out_t; + signal stat_res : tlb_status_t; + + signal pa : word_t; + signal hit : STD_LOGIC; + signal valid : STD_LOGIC; + + signal cam_rdy : STD_LOGIC; + signal cam_hit_vld : STD_LOGIC; + + signal vaddr_reg : word_t; + + subtype reg_lo_t is unsigned(23 downto 0); + subtype reg_hi_t is unsigned(25 downto 0); + + signal reg_lo_din : reg_lo_t; + signal reg_lo_dout_a : reg_lo_t; + signal reg_lo_dout_b : reg_lo_t; + signal reg_lo_addr_b : unsigned(lg2(NUM_ENTRIES)-1 downto 0); + + signal reg_hi_din : reg_hi_t; + signal reg_hi_dout_b : reg_hi_t; + + function to_entry_lo(x : reg_lo_t) return tlb_entry_lo_t is + variable result : tlb_entry_lo_t; + begin + result.PFN := x(23 downto 4); + result.N := x(3); + result.D := x(2); + result.V := x(1); + result.G := x(0); + + return result; + + end to_entry_lo; + + function to_entry_hi(x : reg_hi_t) return tlb_entry_hi_t is + variable result : tlb_entry_hi_t; + begin + result.VPN := x(25 downto 6); + result.ASID := x(5 downto 0); + + return result; + + end to_entry_hi; + + function to_reg_lo(x : tlb_entry_lo_t) return reg_lo_t is + variable result : reg_lo_t; + begin + result(23 downto 4) := x.PFN; + result(3) := x.N; + result(2) := x.D; + result(1) := x.V; + result(0) := x.G; + + return result; + + end to_reg_lo; + + function to_reg_hi(x : tlb_entry_hi_t) return reg_hi_t is + variable result : reg_hi_t; + begin + result(25 downto 6) := x.VPN; + result(5 downto 0) := x.ASID; + + return result; + + end to_reg_hi; + + signal hit_idx : integer; + function decode(x : unsigned; N : integer) return integer is + variable result : integer; + begin + result := 0; + for i in 0 to N-1 loop + if x(i) = '1' then + result := i; + end if; + end loop; + + return result; + + end decode; + +begin + clk_rd <= clk; + rdy <= cam_rdy; + + stat_res.exc_vld <= valid; + stat_res.exc_miss <= not hit; + stat_res.exc_dirty <= hit and qry_res.flags.D and query_in.write; + stat_res.write <= query_in.write; + + qry_res.paddr <= pa; + qry_res.hit <= hit; + qry_res.flags.N <= entry_lo_res.N; + qry_res.flags.D <= entry_lo_res.D; + qry_res.flags.V <= entry_lo_res.V; + qry_res.exc <= stat_res.exc_miss or stat_res.exc_dirty; + qry_res.vld <= valid; + + query_out <= qry_res; + ctrl_out.stat <= stat_res; + + pa <= entry_lo_res.PFN & vaddr_reg(lg2(TLB_PAGE_SIZE)-1 downto 0); + + hit_idx <= decode(blk_hit, NUM_ENTRIES); + + blk_entry_lo <= to_entry_lo(to_mips_01(reg_lo_dout_b)); + reg_lo_addr_b <= to_unsigned(hit_idx, lg2(NUM_ENTRIES)); + reg_lo_din <= to_reg_lo(ctrl_in.entry_lo); + + ctrl_out.entry_hi <= to_entry_hi(to_mips_01(reg_hi_dout_b)); + reg_hi_din <= to_reg_hi(ctrl_in.entry_hi); + +inst_reg_entry_lo: entity work.reg_dual + GENERIC MAP + ( + addr_width => lg2(NUM_ENTRIES), + data_width => 24 + ) + PORT MAP ( + clk_w => clk, + we => ctrl_in.entry_we, + en => '1', + wptr => ctrl_in.entry_wr_idx(lg2(NUM_ENTRIES)-1 downto 0), + din => reg_lo_din, + rptr_a => ctrl_in.entry_rd_idx(lg2(NUM_ENTRIES)-1 downto 0), + rptr_b => reg_lo_addr_b, + dout_a => reg_lo_dout_a, + dout_b => reg_lo_dout_b + ); + +inst_reg_entry_hi: entity work.dpram_1w1r2c_ra + GENERIC MAP + ( + addr_width => lg2(NUM_ENTRIES), + data_width => 26 + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + en_a => '1', + en_b => ctrl_in.entry_re, + we_a => ctrl_in.entry_we, + addr_a => ctrl_in.entry_wr_idx(lg2(NUM_ENTRIES)-1 downto 0), + addr_b => ctrl_in.entry_rd_idx(lg2(NUM_ENTRIES)-1 downto 0), + din_a => reg_hi_din, + dout_b => reg_hi_dout_b + ); + +inst_cam_va: entity work.cam + GENERIC MAP + ( + NUM_ENTRIES => NUM_ENTRIES, + DATA_WIDTH => 20, + CAM_RAM_MAX_WIDTH => 15 + ) + PORT MAP + ( + rst => rst, + clk => clk, + clk_rd => clk_rd, + rdy => cam_rdy, + + re => query_in.vld, + we => ctrl_in.entry_we, + vld => '1', + addr => ctrl_in.entry_wr_idx, + tag_wr => ctrl_in.entry_hi.VPN, + + tag_rd => query_in.vaddr(31 downto lg2(TLB_PAGE_SIZE)), + hit => blk_hit(NUM_ENTRIES-1 downto 0), + hit_vld => cam_hit_vld + + ); + +proc_entry_lo_umc: + process(clk) + variable index : integer; + begin + if rising_edge(clk) then + entry_lo_UMC.N <= '0'; + entry_lo_UMC.D <= '0'; + entry_lo_UMC.G <= '0'; + if query_in.vld = '1' then + entry_lo_UMC.V <= '0'; + if query_in.vaddr(31 downto 29) = "100" then -- 80000000 to 9FFFFFFF + entry_lo_UMC.V <= '1'; + entry_lo_UMC.PFN <= "000" & query_in.vaddr(28 downto lg2(TLB_PAGE_SIZE)); + end if; + end if; + end if; + end process; + +proc_entry_lo_umuc: + process(clk) + variable index : integer; + begin + if rising_edge(clk) then + if CACHE_KSEG1 then + entry_lo_UMUC.N <= '0'; + else + entry_lo_UMUC.N <= '1'; + end if; + entry_lo_UMUC.D <= '0'; + entry_lo_UMUC.G <= '0'; + if query_in.vld = '1' then + entry_lo_UMUC.V <= '0'; + if query_in.vaddr(31 downto 29) = "101" then -- A0000000 to BFFFFFFF + entry_lo_UMUC.V <= '1'; + entry_lo_UMUC.PFN <= "000" & query_in.vaddr(28 downto lg2(TLB_PAGE_SIZE)); + end if; + end if; + end if; + end process; + +tlb_entry_lo_result_translate: +if USE_TLB = true generate + tlb_entry_lo_result: + process(blk_entry_lo, entry_lo_umc, entry_lo_umuc) + begin + entry_lo_res <= blk_entry_lo; + if entry_lo_umc.V = '1' then + entry_lo_res <= entry_lo_umc; + end if; + if entry_lo_umuc.V = '1' then + entry_lo_res <= entry_lo_umuc; + end if; + + end process; +end generate; + +tlb_entry_lo_result_notranslate: +if USE_TLB = false generate + tlb_entry_lo_result: + process(blk_entry_lo, vaddr_reg, entry_lo_umc, entry_lo_umuc) + begin + entry_lo_res.N <= '0'; + entry_lo_res.D <= '0'; + entry_lo_res.V <= '1'; + entry_lo_res.PFN <= vaddr_reg(31 downto lg2(TLB_PAGE_SIZE)); + if entry_lo_umc.V = '1' then + entry_lo_res <= entry_lo_umc; + end if; + if entry_lo_umuc.V = '1' then + entry_lo_res <= entry_lo_umuc; + end if; + + end process; +end generate; + +tlb_hit_combine_translate: +if USE_TLB = true generate + tlb_hit_combine: + process(blk_entry_lo, entry_lo_umc, entry_lo_umuc) + variable t_v : std_logic; + begin + t_v := blk_entry_lo.V; + t_v := t_v or entry_lo_umc.V or entry_lo_umuc.V; + hit <= t_v; + + end process; +end generate; + +tlb_hit_combine_notranslate: +if USE_TLB = false generate + tlb_hit_combine: + process(blk_entry_lo, entry_lo_umc, entry_lo_umuc) + variable t_v : std_logic; + begin + t_v := entry_lo_umc.V or entry_lo_umuc.V; + hit <= t_v; + + end process; +end generate; + +registered_output: + process(clk) + begin + if rising_edge(clk) then + if query_in.vld = '1' then + vaddr_reg <= query_in.vaddr; + end if; + valid <= query_in.vld; + if ce = '1' then + if ctrl_in.entry_re = '1' then + + ctrl_out.entry_lo <= to_entry_lo(to_mips_01(reg_lo_dout_a)); + ctrl_out.entry_vld <= '1'; + end if; + end if; + end if; + end process; + + + +------------------------------------------------------------------ +end behavior; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/mips_top.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_top.vhd new file mode 100644 index 0000000..984f6a4 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_top.vhd @@ -0,0 +1,396 @@ +-------------------------------------------------------------------------- +-- 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 mips_top is + Generic + ( + icache_size : natural := 8192; -- words + icache_line : natural := 8; -- words + dcache_size : natural := 4096; -- words + dcache_line : natural := 8; -- words + WITH_TLB : boolean := true + ); + Port + ( + debug : out chip_debug_t; + eb : in STD_LOGIC; + nmi : in STD_LOGIC; + cpu_clk : in STD_LOGIC; + RST_I : in STD_LOGIC; + CLK_I : in STD_LOGIC; + ACK_I : in STD_LOGIC; + SRDY_I : in STD_LOGIC; + ADDR_O : out unsigned(31 downto 0); + DAT_I : in unsigned(31 downto 0); + DAT_O : out unsigned(31 downto 0); + WE_O : out STD_LOGIC; + SEL_O : out unsigned(3 downto 0); + CYC_O : out STD_LOGIC; + STB_O : out STD_LOGIC; + MRDY_O : out STD_LOGIC; + INT : in unsigned (5 downto 0) + ); + +end mips_top; + +architecture rtl of mips_top is + +-------------------------------------------------------------------------- +-- Pipeline +-------------------------------------------------------------------------- + COMPONENT pipeline is + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + ce : in STD_LOGIC; + imem_err : in STD_LOGIC; + imem_rdy : in STD_LOGIC; + imem_en : out STD_LOGIC; + imem_addr : out word_t; + imem_data : in word_t; + dmem_err : in STD_LOGIC; + dmem_rdy : in STD_LOGIC; + dmem_en : out STD_LOGIC; + dmem_we : out STD_LOGIC; + dmem_be : out unsigned(3 downto 0); + dmem_addr : out word_t; + dmem_din : in word_t; + dmem_dout : out word_t; + cop_ir : out word_t; + cop_ir_en : out STD_LOGIC; + cop_din : in word_t; + cop_dout : out word_t; + ctrl_out : out pipe_ctrl_out_t; + ctrl_in : in pipe_ctrl_in_t + ); + END COMPONENT; + signal imem_err : std_logic; + signal imem_rdy : std_logic; + signal imem_en : std_logic; + signal imem_addr : word_t; + signal imem_din : word_t; + signal dmem_err : std_logic; + signal dmem_rdy : std_logic; + signal dmem_en : std_logic; + signal dmem_we : std_logic; + signal dmem_addr : word_t; + signal dmem_dout : word_t; + signal dmem_din : word_t; + signal dmem_be : unsigned(3 downto 0); + + signal biu_busy : STD_LOGIC; + signal biu_rst : STD_LOGIC; + signal cpu_rst : STD_LOGIC; + signal cpu_run : STD_LOGIC; + + signal pipe_cop_ir : word_t; + signal pipe_cop_ir_en : STD_LOGIC; + signal pipe_cop_din : word_t; + signal pipe_cop_dout : word_t; + signal pipe_ctrl_in : pipe_ctrl_in_t; + signal pipe_ctrl_out : pipe_ctrl_out_t; + + signal cop0_ctrl_in : cop0_ctrl_in_t; + signal cop0_ctrl_out : cop0_ctrl_out_t; + + signal biu_ctrl_in : biu_ctrl_in_t; + signal biu_ctrl_out : biu_ctrl_out_t; + +-------------------------------------------------------------------------- +-- Coprocessor +-------------------------------------------------------------------------- + COMPONENT cop is + GENERIC + ( + icache_size : natural; + icache_line : natural; + dcache_size : natural; + dcache_line : natural + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + nmi : in STD_LOGIC; + eb : in STD_LOGIC; + int : in unsigned (5 downto 0); + ir_en : in STD_LOGIC; + ir : in word_t; + ctrl_in : in cop0_ctrl_in_t; + ctrl_out : out cop0_ctrl_out_t; + din : in word_t; + dout : out word_t + ); + END COMPONENT; + +-------------------------------------------------------------------------- +-- Bus Interface Unit +-------------------------------------------------------------------------- + COMPONENT biu + GENERIC + ( + icache_size : natural; + icache_line : natural; + dcache_size : natural; + dcache_line : natural; + WRITE_FIFO_SIZE : natural; + WITH_TLB : boolean + ); + PORT + ( + clk : in STD_LOGIC; + busy : out STD_LOGIC; + + imem_err : out STD_LOGIC; + imem_rdy : out STD_LOGIC; + imem_en : in STD_LOGIC; + imem_addr : in word_t; + imem_dout : out word_t; + dmem_err : out STD_LOGIC; + dmem_rdy : out STD_LOGIC; + dmem_en : in STD_LOGIC; + dmem_we : in STD_LOGIC; + dmem_be : in unsigned(3 downto 0); + dmem_din : in word_t; + dmem_dout : out word_t; + dmem_addr : in word_t; + + RST_I : in STD_LOGIC; + CLK_I : in STD_LOGIC; + ACK_I : in STD_LOGIC; + SRDY_I : in STD_LOGIC; + ADDR_O : out unsigned(31 downto 0); + MDAT_I : in unsigned(31 downto 0); + MDAT_O : out unsigned(31 downto 0); + WE_O : out STD_LOGIC; + SEL_O : out unsigned(3 downto 0); + CYC_O : out STD_LOGIC; + STB_O : out STD_LOGIC; + MRDY_O : out STD_LOGIC; + + ctrl_in : in biu_ctrl_in_t; + ctrl_out : out biu_ctrl_out_t + + ); + END COMPONENT; + +begin + +------------------------------------------------------------------- + process(cpu_clk) + variable reset_delay : unsigned (31 downto 0); + begin + if rising_edge(cpu_clk) then + if RST_I = '1' then + reset_delay := (others => '0'); + biu_rst <= '1'; + cpu_rst <= '1'; + cpu_run <= '0'; + else + reset_delay := reset_delay(reset_delay'left-1 downto 0) & '1'; + if reset_delay(20) = '1' then + biu_rst <= '0'; + end if; + if reset_delay(31) = '1' then + cpu_rst <= '0'; + end if; + if reset_delay(16) = '1' and biu_busy = '0' then + cpu_run <= '1'; + end if; + end if; + end if; + end process; + +inst_pipeline: pipeline + PORT MAP + ( + rst => cpu_rst, + clk => cpu_clk, + ce => cpu_run, + imem_err => imem_err, + imem_rdy => imem_rdy, + imem_en => imem_en, + imem_addr => imem_addr, + imem_data => imem_din, + dmem_err => dmem_err, + dmem_rdy => dmem_rdy, + dmem_en => dmem_en, + dmem_we => dmem_we, + dmem_be => dmem_be, + dmem_addr => dmem_addr, + dmem_din => dmem_din, + dmem_dout => dmem_dout, + cop_ir => pipe_cop_ir, + cop_ir_en => pipe_cop_ir_en, + cop_din => pipe_cop_din, + cop_dout => pipe_cop_dout, + ctrl_out => pipe_ctrl_out, + ctrl_in => pipe_ctrl_in + ); + + pipe_ctrl_in <= cop0_ctrl_out.pipe; + +inst_cop: cop + GENERIC MAP + ( + icache_size => icache_size, -- words + icache_line => icache_line, -- words + dcache_size => dcache_size, -- words + dcache_line => dcache_line -- words + ) + PORT MAP + ( + rst => cpu_rst, + clk => cpu_clk, + nmi => nmi, + eb => eb, + int => INT, + ir_en => pipe_cop_ir_en, + ir => pipe_cop_ir, + ctrl_in => cop0_ctrl_in, + ctrl_out => cop0_ctrl_out, + dout => pipe_cop_din, + din => pipe_cop_dout + ); + + cop0_ctrl_in.pipe <= pipe_ctrl_out; + cop0_ctrl_in.itlb <= biu_ctrl_out.itlb; + cop0_ctrl_in.dtlb <= biu_ctrl_out.dtlb; + +inst_biu: biu + GENERIC MAP + ( + icache_size => icache_size, -- words + icache_line => icache_line, -- words + dcache_size => dcache_size, -- words + dcache_line => dcache_line, -- words + WRITE_FIFO_SIZE => 4, + WITH_TLB => WITH_TLB + ) + PORT MAP + ( + + clk => cpu_clk, + busy => biu_busy, + + imem_err => imem_err, + imem_rdy => imem_rdy, + imem_en => imem_en, + imem_addr => imem_addr, + imem_dout => imem_din, + dmem_err => dmem_err, + dmem_rdy => dmem_rdy, + dmem_en => dmem_en, + dmem_we => dmem_we, + dmem_be => dmem_be, + dmem_addr => dmem_addr, + dmem_din => dmem_dout, + dmem_dout => dmem_din, + + RST_I => biu_rst, + CLK_I => CLK_I, + ACK_I => ACK_I, + SRDY_I => SRDY_I, + ADDR_O => ADDR_O, + MDAT_I => DAT_I, + MDAT_O => DAT_O, + WE_O => WE_O, + SEL_O => SEL_O, + CYC_O => CYC_O, + STB_O => STB_O, + MRDY_O => MRDY_O, + + ctrl_in => biu_ctrl_in, + ctrl_out => biu_ctrl_out + + ); + + biu_ctrl_in.dcache <= cop0_ctrl_out.dcache; + biu_ctrl_in.icache <= cop0_ctrl_out.icache; + biu_ctrl_in.dtlb <= cop0_ctrl_out.dtlb; + biu_ctrl_in.itlb <= cop0_ctrl_out.itlb; + +------------------------------------------- +-- debug +------------------------------------------- + debug.imem_err <= imem_err; + debug.dmem_err <= dmem_err; + +debug_imem_din_register: + process(cpu_clk) + variable en_r: std_logic; + variable addr_r: word_t; + begin + if rising_edge(cpu_clk) then + debug.imem_din_vld <= '0'; + if imem_rdy = '1' then + if en_r = '1' then + debug.imem_addr_reg <= addr_r; + debug.imem_din_reg <= imem_din; + debug.imem_din_vld <= '1'; + end if; + if imem_en = '1' then + addr_r := imem_addr; + end if; + en_r := imem_en; + end if; + end if; + end process; + +debug_dmem_din_register: + process(cpu_clk) + variable en_r: std_logic; + variable we_r: std_logic; + variable addr_r: word_t; + variable dout_r: word_t; + begin + if rising_edge(cpu_clk) then + debug.dmem_din_vld <= '0'; + debug.dmem_dout_vld <= '0'; + if dmem_rdy = '1' then + if en_r = '1' then + debug.dmem_addr_reg <= addr_r; + debug.dmem_din_reg <= dmem_din; + debug.dmem_din_vld <= not we_r; + debug.dmem_dout_reg <= dout_r; + debug.dmem_dout_vld <= we_r; + end if; + if dmem_en = '1' then + dout_r := dmem_dout; + addr_r := dmem_addr; + end if; + en_r := dmem_en; + we_r := dmem_we; + end if; + end if; + end process; + +end rtl; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/mips_types.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_types.vhd new file mode 100644 index 0000000..f14ab8a --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_types.vhd @@ -0,0 +1,807 @@ +-------------------------------------------------------------------------- +-- Project: JIPS, a portable 32-bit RISC CPU written in VHDL +-- This file: Types, constants and functions for JIPS +-- +-- 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; +use IEEE.MATH_REAL.ALL; + +library work; +use work.mips_util_pkg.all; + +-------------------------------------------------------------------------- +package mips_types is + + -- Revision of the CPU + constant REVISION : integer := 13; + constant WORD_WIDTH : integer := 32; + constant TLB_PAGE_SIZE : positive := 4096; + constant TLB_NUM_ASIDS : positive := 64; + constant TLB_NUM_ENTRIES : positive := 32; + + --Types + subtype instr_name_t is string(1 to 12); + type instr_name_array_t is array (0 to 63) of instr_name_t; + subtype opcode_t is unsigned (5 downto 0); + subtype word_t is unsigned (WORD_WIDTH-1 downto 0); + subtype reg_ptr_t is unsigned (4 downto 0); + subtype shamt_t is unsigned(4 downto 0); + subtype sa_t is natural range 0 to 63; + subtype func_t is unsigned(5 downto 0); + + type chip_debug_t is record + imem_err : std_logic; + imem_addr_reg : word_t; + imem_din_reg : word_t; + imem_din_vld : std_logic; + dmem_err : std_logic; + dmem_addr_reg : word_t; + dmem_din_reg : word_t; + dmem_din_vld : std_logic; + dmem_dout_reg : word_t; + dmem_dout_vld : std_logic; + end record; + + type op_t is + ( + op_illegal, + op_nop, + op_sll, + op_srl, + op_sra, + op_sllv, + op_srlv, + op_srav, + op_jr, + op_jalr, + op_syscall, + op_break, + op_mfhi, + op_mthi, + op_mflo, + op_mtlo, + op_mult, + op_multu, + op_div, + op_divu, + op_add, + op_addu, + op_sub, + op_subu, + op_and, + op_or, + op_xor, + op_nor, + op_slt, + op_sltu, + op_bltz, + op_bgez, + op_bltzal, + op_bgezal, + op_j, + op_jal, + op_beq, + op_bne, + op_blez, + op_bgtz, + op_addi, + op_addiu, + op_slti, + op_sltiu, + op_andi, + op_ori, + op_xori, + op_lui, + op_lb, + op_lh, + op_lwl, + op_lw, + op_lbu, + op_lhu, + op_lwr, + op_sb, + op_sh, + op_swl, + op_sw, + op_swr, + op_cop0, + op_cop1, + op_cop2, + op_cop3, + op_mfc0, + op_cfc0, + op_mtc0, + op_ctc0, + op_bcf0, + op_bct0, + op_mfc1, + op_cfc1, + op_mtc1, + op_ctc1, + op_bcf1, + op_bct1, + op_mfc2, + op_cfc2, + op_mtc2, + op_ctc2, + op_bcf2, + op_bct2, + op_mfc3, + op_cfc3, + op_mtc3, + op_ctc3, + op_bcf3, + op_bct3, + op_lwc0, + op_lwc1, + op_lwc2, + op_lwc3, + op_swc0, + op_swc1, + op_swc2, + op_swc3 + ); + + attribute ENUM_ENCODING : string; + + type imm_src_t is (src_imm32, src_imm16, src_imm16_high); + type sa_src_t is (sa_src_reg, sa_src_imm); + type alu_src1_t is (alu_src_reg); + type alu_src2_t is (alu_src_reg, alu_src_imm); + type bc_src_t is (bc_eq_ne, bc_lez_gtz, bc_ltz_gez); + type shift_opsel_t is (shift_sll, shift_srl, shift_sra); + type wptr_src_t is (wptr_src_imm, wptr_src_const); + + type pc_t is record + curr : word_t; + last : word_t; + nxt : word_t; + pc_branch : word_t; + branch_take : std_logic; + end record; + + type alu_outsel_t is + ( + alu_adder, + alu_shift2, + alu_and, + alu_or, + alu_xor, + alu_nor, + alu_ltu, + alu_lts + ); + + type sdu_t is record + imem_dep : STD_LOGIC; + dmem_dep : STD_LOGIC; + mul_dep : STD_LOGIC; + ID_nop : STD_LOGIC; + EX_nop : STD_LOGIC; + MEM_nop : STD_LOGIC; + WB_nop : STD_LOGIC; + ID_stall : STD_LOGIC; + EX_stall : STD_LOGIC; + MEM_stall : STD_LOGIC; + WB_stall : STD_LOGIC; + stall_all : STD_LOGIC; + end record; + + type hdu_t is record + alu_fwd_a_ex : boolean; + alu_fwd_a_mem : boolean; + alu_fwd_a_wb : boolean; + alu_fwd_b_ex : boolean; + alu_fwd_b_mem : boolean; + alu_fwd_b_wb : boolean; + end record; + + type alu_flags_t is record + c : STD_LOGIC; + uvf : STD_LOGIC; + ovf : STD_LOGIC; + ltu : STD_LOGIC; + lts : STD_LOGIC; + end record; + + type bcu_flags_t is record + eq : STD_LOGIC; + ltz : STD_LOGIC; + end record; + + type event_t is record + NMI : STD_LOGIC; + Int : STD_LOGIC; + data_load_err : STD_LOGIC; + data_store_err : STD_LOGIC; + inst_load_err : STD_LOGIC; + inst_priv_addr : STD_LOGIC; + alu_ovf : STD_LOGIC; + alu_uvf : STD_LOGIC; + syscall : STD_LOGIC; + break : STD_LOGIC; + illegal : STD_LOGIC; + ITLB_MOD : STD_LOGIC; + ITLB_LOAD : STD_LOGIC; + ITLB_STORE : STD_LOGIC; + DTLB_MOD : STD_LOGIC; + DTLB_LOAD : STD_LOGIC; + DTLB_STORE : STD_LOGIC; + end record; + + type exc_flags_t is record + Int : STD_LOGIC; + DAdEL : STD_LOGIC; + DAdES : STD_LOGIC; + IAdEL : STD_LOGIC; + IAdEK : STD_LOGIC; + Ov : STD_LOGIC; + Sys : STD_LOGIC; + Bp : STD_LOGIC; + RI : STD_LOGIC; + ITLB_MOD : STD_LOGIC; + ITLB_LOAD : STD_LOGIC; + ITLB_STORE : STD_LOGIC; + DTLB_MOD : STD_LOGIC; + DTLB_LOAD : STD_LOGIC; + DTLB_STORE : STD_LOGIC; + IBE : STD_LOGIC; + DBE : STD_LOGIC; + end record; + + type except_t is record + act : STD_LOGIC; + epc : word_t; + cause : word_t; + end record; + + type cache_ctrl_t is record + inv_addr : word_t; + inv_at : STD_LOGIC; + inv_all : STD_LOGIC; + end record; + + type tlb_flags_t is record + N : std_logic; + D : std_logic; + V : std_logic; + end record; + + type tlb_entry_lo_t is record + PFN : unsigned (31 downto lg2(TLB_PAGE_SIZE)); + G : std_logic; + N : std_logic; + D : std_logic; + V : std_logic; + end record; + + type tlb_entry_hi_t is record + VPN : unsigned (31 downto lg2(TLB_PAGE_SIZE)); + ASID : unsigned (lg2(TLB_NUM_ASIDS)-1 downto 0); + end record; + + type tlb_query_in_t is record + vld : std_logic; + write : std_logic; + vaddr : word_t; + idx : unsigned (lg2(TLB_NUM_ENTRIES) downto 0); + end record; + + type tlb_query_out_t is record + vld : std_logic; + exc : STD_LOGIC; + hit : STD_LOGIC; + paddr : word_t; + flags : tlb_flags_t; + idx : unsigned (lg2(TLB_NUM_ENTRIES) downto 0); + end record; + + type tlb_status_t is record + exc_vld : std_logic; + exc_miss : std_logic; + exc_dirty : std_logic; + write : std_logic; + end record; + + type tlb_ctrl_in_t is record + entry_we : STD_LOGIC; + entry_re : STD_LOGIC; + entry_rd_idx : unsigned (lg2(TLB_NUM_ENTRIES)-1 downto 0); + entry_wr_idx : unsigned (lg2(TLB_NUM_ENTRIES)-1 downto 0); + entry_lo : tlb_entry_lo_t; + entry_hi : tlb_entry_hi_t; + end record; + + type tlb_ctrl_out_t is record + entry_vld : std_logic; + entry_lo : tlb_entry_lo_t; + entry_hi : tlb_entry_hi_t; + entry_prb_idx : unsigned (lg2(TLB_NUM_ENTRIES)-1 downto 0); + stat : tlb_status_t; + end record; + + type biu_ctrl_in_t is record + itlb : tlb_ctrl_in_t; + dtlb : tlb_ctrl_in_t; + icache : cache_ctrl_t; + dcache : cache_ctrl_t; + dtlb_va : word_t; + dtlb_en : STD_LOGIC; + end record; + + type biu_ctrl_out_t is record + itlb : tlb_ctrl_out_t; + dtlb : tlb_ctrl_out_t; + end record; + + type pipe_ctrl_in_t is record + EB : STD_LOGIC; + exc_inject : STD_LOGIC; + exc_commit : STD_LOGIC; + exc_pending : STD_LOGIC; + exc_exit : STD_LOGIC; + user_mode : STD_LOGIC; + NMI : STD_LOGIC; + int : STD_LOGIC; + exc_vec : word_t; + ITLB : tlb_status_t; + DTLB : tlb_status_t; + end record; + + type pipe_ctrl_out_t is record + sdu : sdu_t; + events : event_t; + bd_wb : STD_LOGIC; + epc_wb : word_t; + imem_addr : word_t; + dmem_addr : word_t; + exc_req : STD_LOGIC; + exc_ack : STD_LOGIC; + dtlb_va : word_t; + dtlb_en : STD_LOGIC; + end record; + + type cop0_ctrl_in_t is record + pipe : pipe_ctrl_out_t; + itlb : tlb_ctrl_out_t; + dtlb : tlb_ctrl_out_t; + end record; + + type cop0_ctrl_out_t is record + pipe : pipe_ctrl_in_t; + itlb : tlb_ctrl_in_t; + dtlb : tlb_ctrl_in_t; + icache : cache_ctrl_t; + dcache : cache_ctrl_t; + end record; + + type alu_ctrl_t is record + outsel : alu_outsel_t; + add : STD_LOGIC; + shift_right : STD_LOGIC; + shift_arith : STD_LOGIC; + op1_src : alu_src1_t; + op2_src : alu_src2_t; + end record; + + type shift_ctrl_t is record + shamt_nrm : shamt_t; + shamt_rnd : shamt_t; + shift_right : STD_LOGIC; + shift_arith : STD_LOGIC; + end record; + + type ctrl_lines_t is record + branch : STD_LOGIC; + jump_long : STD_LOGIC; + jump : STD_LOGIC; + bc_not : STD_LOGIC; + bc_src : bc_src_t; + imm_src : imm_src_t; + alu : alu_ctrl_t; + reg_write : STD_LOGIC; + reg_link : STD_LOGIC; + dmem_we : STD_LOGIC; + dmem_en : STD_LOGIC; + shamt2_srcsel : sa_src_t; + wptr_srcsel : wptr_src_t; + cop_instr_en : STD_LOGIC; + cop_read : STD_LOGIC; + shift_offset : unsigned(1 downto 0); + shift_byp : STD_LOGIC; + byte_en_byp : STD_LOGIC; + sign_ext_byp : STD_LOGIC; + word4_en : STD_LOGIC; + word2_en : STD_LOGIC; + align_left : STD_LOGIC; + load_signed : STD_LOGIC; + mul_access : STD_LOGIC; + mul_hilo_we : STD_LOGIC; + mul_mul_divn : STD_LOGIC; + mul_start : STD_LOGIC; + mul_s_un : STD_LOGIC; + mul_hilo_sel : STD_LOGIC; + exc_break : STD_LOGIC; + exc_syscall : STD_LOGIC; + exc_illegal : STD_LOGIC; + except_en : STD_LOGIC; + alu_exc_en : STD_LOGIC; + end record; + + type ID_t is record + nop : STD_LOGIC; + exc : std_logic; + op : op_t; + IR : word_t; + epc : word_t; + pcn : word_t; + jimm32 : word_t; + bimm18 : word_t; + imm : word_t; + shamt : shamt_t; + ctrl : ctrl_lines_t; + va : word_t; + reg_write : STD_LOGIC; + reg_a_rptr : reg_ptr_t; + reg_b_rptr : reg_ptr_t; + reg_a : word_t; + reg_b : word_t; + events : event_t; + end record; + + type EX_t is record + nop : STD_LOGIC; + exc : std_logic; + op : op_t; + IR : word_t; + epc : word_t; + pcn : word_t; + reg_a : word_t; + reg_b : word_t; + ctrl : ctrl_lines_t; + shift_ctrl : shift_ctrl_t; + alu_op1 : word_t; + alu_op2 : word_t; + alu_op2_s : word_t; + alu_flags : alu_flags_t; + result : word_t; + reg_write : STD_LOGIC; + reg_we : STD_LOGIC; + reg_wptr : reg_ptr_t; + reg_a_rptr : reg_ptr_t; + reg_b_rptr : reg_ptr_t; + va : word_t; + pa_off : unsigned(1 downto 0); + events_in : event_t; + events : event_t; + end record; + + type MEM_t is record + nop : STD_LOGIC; + exc : std_logic; + op : op_t; + epc : word_t; + pcn : word_t; + ctrl : ctrl_lines_t; + ex_result : word_t; + reg_wptr : reg_ptr_t; + reg_we : STD_LOGIC; + reg_data : word_t; + va : word_t; + pa_off : unsigned(1 downto 0); + events_in : event_t; + events : event_t; + end record; + + type WB_t is record + nop : STD_LOGIC; + epc : word_t; + epc_last : word_t; + bd : STD_LOGIC; + exc : std_logic; + exc_last : std_logic; + events : event_t; + op : op_t; + reg_wptr : reg_ptr_t; + reg_we : STD_LOGIC; + reg_data : word_t; + va : word_t; + end record; + + + type rom_special_t is array (0 to 2**func_t'length-1) of ctrl_lines_t; + type rom_regimm_t is array (0 to 2**reg_ptr_t'length-1) of ctrl_lines_t; + type rom_opcode_t is array (0 to 2**opcode_t'length-1) of ctrl_lines_t; + + function store_shift(x : word_t; va, shift_off : unsigned(1 downto 0); bypass : std_logic) return word_t; + function store_be(va : unsigned(1 downto 0); be_src, word2_en, word4_en, align_left, bypass : std_logic) return unsigned; + function load_shift(x : word_t; va, shift_off : unsigned(1 downto 0); bypass : std_logic) return word_t; + function load_be(va : unsigned(1 downto 0); align_left, bypass : std_logic) return unsigned; + function load_sign_ext(x : word_t; bypass, signed, word2_en, word4_en : std_logic) return word_t; + function events_clr return event_t; + function event_is_active(e : event_t) return std_logic; + function "or" (a, b : event_t) return event_t; + function "or" (a, b : tlb_flags_t) return tlb_flags_t; + +end mips_types; + +-------------------------------------------------------------------------- +package body mips_types is + + function store_shift(x : word_t; va, shift_off : unsigned(1 downto 0); bypass : std_logic) return word_t is + variable stage1 : word_t; + variable result : word_t; + variable sa : unsigned(1 downto 0); + begin + + if bypass = '1' then + sa := "00"; + else + sa := va + shift_off; + end if; + + stage1 := x; + if sa(0) = '1' then + stage1 := x(23 downto 0) & x(31 downto 24); + end if; + result := stage1; + if sa(1) = '1' then + result := stage1(15 downto 0) & stage1(31 downto 16); + end if; + + return result; + end store_shift; + +-------------------------------------------------------------------------- + function store_be(va : unsigned(1 downto 0); be_src, word2_en, word4_en, align_left, bypass : std_logic) return unsigned is + variable result : unsigned(3 downto 0); + begin + result := (3 downto 0 => be_src); + if bypass = '0' then + if word2_en = '1' then + case va is + when "00" => + result := "00" & be_src & be_src; + when "10" => + result := be_src & be_src & "00"; + when others => null; + end case; + elsif word4_en = '1' then + case va is + when "00" => + result := "000" & be_src; + when "01" => + result := "00" & be_src & '0'; + when "10" => + result := '0' & be_src & "00"; + when "11" => + result := be_src & "000"; + when others => null; + end case; + elsif align_left = '1' then + case va is + when "00" => + result := be_src & be_src & be_src & be_src; + when "01" => + result := be_src & be_src & be_src & '0'; + when "10" => + result := be_src & be_src & "00"; + when "11" => + result := be_src & "000"; + when others => null; + end case; + else + case va is + when "00" => + result := "000" & be_src; + when "01" => + result := "00" & be_src & be_src; + when "10" => + result := '0' & be_src & be_src & be_src; + when "11" => + result := be_src & be_src & be_src & be_src; + when others => null; + end case; + end if; + end if; + + return result; + + end store_be; + +-------------------------------------------------------------------------- + function load_shift(x : word_t; va, shift_off : unsigned(1 downto 0); bypass : std_logic) return word_t is + variable stage1 : word_t; + variable result : word_t; + variable sa : unsigned(1 downto 0); + + begin + if bypass = '1' then + sa := "00"; + else + sa := va + shift_off; + end if; + + stage1 := x; + if sa(0) = '1' then + stage1 := x(7 downto 0) & x(31 downto 8); + end if; + result := stage1; + if sa(1) = '1' then + result := stage1(15 downto 0) & stage1(31 downto 16); + end if; + + return result; + end load_shift; + +-------------------------------------------------------------------------- + function load_be(va : unsigned(1 downto 0); align_left, bypass : std_logic) return unsigned is + variable result : unsigned(3 downto 0); + begin + result := (3 downto 0 => '1'); + if bypass = '0' then + if align_left = '1' then + case va is + when "00" => + result := "1000"; + when "01" => + result := "1100"; + when "10" => + result := "1110"; + when "11" => + result := "1111"; + when others => null; + end case; + else + case va is + when "00" => + result := "1111"; + when "01" => + result := "0111"; + when "10" => + result := "0011"; + when "11" => + result := "0001"; + when others => null; + end case; + end if; + end if; + + return result; + + end load_be; + +-------------------------------------------------------------------------- + function load_sign_ext(x : word_t; bypass, signed, word2_en, word4_en : std_logic) return word_t is + variable result : word_t; + variable sign : std_logic; + + begin + if bypass = '1' then + result := x; + elsif word2_en = '1' then + sign := signed and x(15); + result := (31 downto 16 => sign) & x(15 downto 0); + else + sign := signed and x(7); + result := (31 downto 8 => sign) & x(7 downto 0); + end if; + + return result; + end load_sign_ext; + +-------------------------------------------------------------------------- + function events_clr return event_t is + variable result : event_t; + begin + result.NMI := '0'; + result.Int := '0'; + result.data_load_err := '0'; + result.data_store_err := '0'; + result.inst_load_err := '0'; + result.inst_priv_addr := '0'; + result.alu_ovf := '0'; + result.alu_uvf := '0'; + result.syscall := '0'; + result.break := '0'; + result.illegal := '0'; + result.ITLB_MOD := '0'; + result.ITLB_LOAD := '0'; + result.ITLB_STORE := '0'; + result.DTLB_MOD := '0'; + result.DTLB_LOAD := '0'; + result.DTLB_STORE := '0'; + + return result; + + end events_clr; + +-------------------------------------------------------------------------- + function event_is_active(e : event_t) return std_logic is + variable result : std_logic; + begin + result := '0'; + + result := result or e.NMI; + result := result or e.Int; + result := result or e.data_load_err; + result := result or e.data_store_err; + result := result or e.inst_load_err; + result := result or e.inst_priv_addr; + result := result or e.alu_ovf; + result := result or e.alu_uvf; + result := result or e.syscall; + result := result or e.break; + result := result or e.illegal; + result := result or e.ITLB_MOD; + result := result or e.ITLB_LOAD; + result := result or e.ITLB_STORE; + result := result or e.DTLB_MOD; + result := result or e.DTLB_LOAD; + result := result or e.DTLB_STORE; + + return result; + + end event_is_active; + +-------------------------------------------------------------------------- + function "or" (a, b : event_t) return event_t is + variable result : event_t; + begin + result.NMI := a.NMI or b.NMI; + result.Int := a.Int or b.Int; + result.data_load_err := a.data_load_err or b.data_load_err; + result.data_store_err := a.data_store_err or b.data_store_err; + result.inst_load_err := a.inst_load_err or b.inst_load_err; + result.inst_priv_addr := a.inst_priv_addr or b.inst_priv_addr; + result.alu_ovf := a.alu_ovf or b.alu_ovf; + result.alu_uvf := a.alu_uvf or b.alu_uvf; + result.syscall := a.syscall or b.syscall; + result.break := a.break or b.break; + result.illegal := a.illegal or b.illegal; + result.ITLB_MOD := a.ITLB_MOD or b.ITLB_MOD; + result.ITLB_LOAD := a.ITLB_LOAD or b.ITLB_LOAD; + result.ITLB_STORE := a.ITLB_STORE or b.ITLB_STORE; + result.DTLB_MOD := a.DTLB_MOD or b.DTLB_MOD; + result.DTLB_LOAD := a.DTLB_LOAD or b.DTLB_LOAD; + result.DTLB_STORE := a.DTLB_STORE or b.DTLB_STORE; + + return result; + + end "or"; + +-------------------------------------------------------------------------- + function "or" (a, b : tlb_flags_t) return tlb_flags_t is + variable result : tlb_flags_t; + begin + result.N := a.N or b.N; + result.D := a.D or b.D; + result.V := a.V or b.V; + + return result; + + end "or"; + +-------------------------------------------------------------------------- +end mips_types; + diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/mips_util_pkg.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_util_pkg.vhd new file mode 100644 index 0000000..07d3118 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/mips_util_pkg.vhd @@ -0,0 +1,68 @@ +-------------------------------------------------------------------------- +-- Project: JIPS, a portable 32-bit RISC CPU written in VHDL +-- This file: Types, constants and functions for JIPS +-- +-- 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; +use IEEE.MATH_REAL.ALL; + +-------------------------------------------------------------------------- +package mips_util_pkg is + + + function to_mips_01(x : unsigned) return unsigned; + function lg2(x : natural) return natural; + function po2(x : natural) return natural; + +end mips_util_pkg; + +-------------------------------------------------------------------------- +package body mips_util_pkg is + + function to_mips_01(x : unsigned) return unsigned is + variable result : unsigned (x'left downto x'right); + begin + for i in x'left downto x'right loop + if x(i) = '1' then + result(i) := '1'; + else + result(i) := '0'; + end if; + end loop; + return result; + end to_mips_01; + + 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; + +-------------------------------------------------------------------------- +end mips_util_pkg; + diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache.vhd new file mode 100644 index 0000000..49141e1 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache.vhd @@ -0,0 +1,696 @@ +-------------------------------------------------------------------------- +-- 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_util_pkg.all; +use work.mips_types.all; +use work.busmaster_types.all; + +ENTITY dcache IS + Generic + ( + cache_size : natural := 2048; -- words + line_size : natural := 8; -- words + write_fifo_size : natural := 4; + CACHE_ALL : boolean := false; + USE_CACHE : boolean := true; + USE_TLB : boolean := false + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + + en : in STD_LOGIC; + we : in STD_LOGIC; + be : in unsigned(3 downto 0); + addr : in word_t; + din : in word_t; + dout : out word_t; + rdy : out STD_LOGIC; + + -- Cache control + ctrl_in : in cache_ctrl_t; + + tlb_query_out : out tlb_query_in_t; + tlb_query_in : in tlb_query_out_t; + + -- busmaster data + bus_din : in word_t; + bus_din_rdy : out std_logic; + bus_din_vld : in std_logic; + + bus_dout : out word_t; + bus_dout_vld : out std_logic; + bus_dout_rdy : in std_logic; + + -- busmaster command + bus_cmd_rdy : in std_logic; + bus_cmd_we : out std_logic; + bus_cmd_cycle_en : out std_logic; + bus_cmd_out : out bm_cmd_t; + bus_cyc_complete : in 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; + + function to_word_index(x : word_t) return unsigned is + variable result : unsigned(word_index_width-1 downto 0); + begin + result := x(word_index_width+1 downto 2); + + return result; + + end to_word_index; + + + function to_cache_index(x : word_t) return unsigned is + variable result : unsigned(cache_index_width-1 downto 0); + begin + result := x(cache_index_width+word_index_width+1 downto word_index_width+2); + + return result; + + end to_cache_index; + + function to_tag(x : word_t) return unsigned is + variable result : unsigned(tag_width-1 downto 0); + begin + result := x(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + return result; + + end to_tag; + + + type cache_ctrl2_state_t is (init, ready, refill_pre, refill, refill_post, read_nofill); + signal cache_ctrl2_state, cache_ctrl2_state_next : cache_ctrl2_state_t; + + type cache_ctrl_state_t is (init, ready, invalidate, flush, bus_request, mem_read, mem_read_single, mem_read_data, mem_read_data_single, rd_cache, upd_cache, single_valid); + signal cache_ctrl_state, cache_ctrl_state_next : cache_ctrl_state_t; + + signal cache_req_rd : std_logic; + signal cache_req_wr : std_logic; + signal single_read_reg_vld : std_logic; + signal cache_rdy : std_logic; + signal cache_hit : std_logic; + signal read_rdy : std_logic; + signal tag_match : std_logic; + signal cache_hit_inv : std_logic; + signal tag_match_inv : std_logic; + signal request_va : word_t; + signal request_pa : word_t; + + 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 data_ram_addr : unsigned(lg2(cache_size)-1 downto 0); + signal data_ram_dout : word_t; + signal data_reg : word_t; + signal be_reg : unsigned(3 downto 0); + signal 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 data_ram_we : unsigned(3 downto 0); + signal single_read_reg : word_t; + + 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 write_hit : std_logic; + signal instant_raw : std_logic; + signal ram_write_en : std_logic; + signal fill_word_index : unsigned(word_index_width-1 downto 0); + signal req_word_index : unsigned(word_index_width-1 downto 0); + signal hit_cache_index : unsigned(cache_index_width-1 downto 0); + + signal write_fifo_din : unsigned(67 downto 0); + signal write_fifo_dout : unsigned(67 downto 0); + signal write_fifo_re : std_logic; + signal write_fifo_we : std_logic; + signal write_fifo_full : std_logic; + signal write_fifo_empty : std_logic; + signal write_data_avail : std_logic; + signal read_write_buffer : std_logic; + + alias write_fifo_addr_in is write_fifo_din(31 downto 0); + alias write_fifo_data_in is write_fifo_din(63 downto 32); + alias write_fifo_sel_in is write_fifo_din(67 downto 64); + alias write_fifo_addr_out is write_fifo_dout(31 downto 0); + alias write_fifo_data_out is write_fifo_dout(63 downto 32); + alias write_fifo_sel_out is write_fifo_dout(67 downto 64); + + signal write_through_en : std_logic; + signal tlb_exc_miss : std_logic; + signal tlb_exc_read : std_logic; + signal tlb_exc_write : std_logic; + signal tlb_exc_dirty : std_logic; + signal tlb_exc : std_logic; + signal tlb_exc_type : unsigned(1 downto 0); + + signal tlb_result_no_translate : tlb_query_out_t; + signal tlb_result : tlb_query_out_t; + +begin + + cache_index_inv <= ctrl_in.inv_addr(cache_index_width+word_index_width+1 downto word_index_width+2); + tag_inv <= ctrl_in.inv_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + bus_din_rdy <= bus_din_vld; + bus_cmd_out.addr <= write_fifo_addr_out when read_write_buffer = '1' else to_tag(request_pa) & to_cache_index(request_pa) & req_word_index & "00"; + bus_cmd_out.rw <= read_write_buffer; + bus_cmd_out.sel <= write_fifo_sel_out when read_write_buffer = '1' else (others => '1'); + bus_dout <= write_fifo_data_out; + bus_dout_vld <= read_write_buffer and write_data_avail; + + tlb_query_out.vaddr <= request_va; + tlb_result_no_translate.paddr <= request_va; + tlb_result_no_translate.hit_idx <= (others => '0'); + tlb_result_no_translate.hit <= '1'; + tlb_result_no_translate.flags.N <= '0' when CACHE_ALL = true else '1' when USE_CACHE = false else '1' when request_va(31 downto 29) = "101" else '0'; + tlb_result_no_translate.flags.D <= '0'; + tlb_result_no_translate.flags.V <= '1'; + + tlb_result <= tlb_query_in when USE_TLB else tlb_result_no_translate; + request_pa <= tlb_result.paddr; + + tlb_exc_miss <= not tlb_result.hit; + tlb_exc_read <= tlb_exc_miss and not we_reg; + tlb_exc_write <= tlb_exc_miss and we_reg; + tlb_exc_dirty <= tlb_result.hit and tlb_result.flags.D and we_reg; + tlb_exc <= tlb_exc_read or tlb_exc_write or tlb_exc_dirty; + + tlb_exc_type <= "10" when tlb_exc_read = '1' else "01" when tlb_exc_write = '1' else "11" when tlb_exc_dirty = '1' else "00"; + + write_hit <= we_reg and cache_hit and not tlb_exc; + +-- Instantiate synchronous FIFO +inst_write_fifo: entity work.fifo_sync + GENERIC MAP + ( + addr_width => lg2(write_fifo_size), + data_width => 68, + do_last_read_update => true + ) + PORT MAP + ( + rst => rst, + clk => clk, + we => write_fifo_we, + re => write_fifo_re, + fifo_full => write_fifo_full, + fifo_empty => write_fifo_empty, + fifo_afull => open, + fifo_aempty => open, + data_w => write_fifo_din, + data_r => write_fifo_dout + ); + + write_fifo_data_in <= data_reg; + write_fifo_addr_in <= request_pa; + write_fifo_sel_in <= be_reg; + write_fifo_re <= read_write_buffer and bus_dout_rdy and bus_cmd_rdy; + write_fifo_we <= write_through_en; + write_data_avail <= not write_fifo_empty; + +fill_address_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' and en = '1' then + request_va <= addr; + fill_word_index <= to_word_index(addr); + elsif bus_din_vld = '1' and fill_count_en = '1' then + fill_word_index <= fill_word_index + 1; + end if; + end if; + end process; + +request_address_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' and en = '1' then + req_word_index <= to_word_index(addr); + elsif request_count_en = '1' and bus_cmd_rdy = '1' then + req_word_index <= req_word_index + 1; + end if; + end if; + end process; + +single_read_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then +-- single_read_reg_vld <= '0'; + elsif bus_din_vld = '1' then + single_read_reg <= bus_din; +-- single_read_reg_vld <= tlb_result.flags.N; + elsif cache_rdy = '1' then +-- single_read_reg_vld <= '0'; + end if; + end if; + end process; + +request_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' then + cache_req_rd <= en and not we; + cache_req_wr <= en and we; + end if; + end if; + end process; + +request_register2: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + we_reg <= '0'; + elsif cache_rdy = '1' then + data_reg <= din; + be_reg <= be; + we_reg <= we; + if we = '1' then + hit_cache_index <= to_cache_index(addr); + end if; + end if; + end if; + end process; + +instant_raw_logic: + process(clk) + begin + if rising_edge(clk) then + instant_raw <= '0'; + if to_word_index(addr) = fill_word_index and to_cache_index(addr) = hit_cache_index then + instant_raw <= write_hit and en and not 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, + clk_b => clk, + 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, + clk_b => clk, + en_a => '1', + en_b => '1', + we_a => data_ram_we(i), + we_b => ctrl_data_ram_we(i), + addr_a => ctrl_data_ram_addr, + addr_b => data_ram_addr, + din_a => data_reg(8*(i+1)-1 downto 8*i), + din_b => bus_din(8*(i+1)-1 downto 8*i), + dout_a => open, + dout_b => data_ram_dout(8*(i+1)-1 downto 8*i) + ); + end generate; + + + +cache_invalidate_request: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' or ctrl_in.inv_all = '1' or ctrl_in.inv_at = '1' then + invalidate_req <= '1'; + invalidate_all <= ctrl_in.inv_all or rst; + 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) + begin + if rising_edge(clk) then + if rst = '1' then + cache_ctrl_state <= init; + cache_ctrl2_state <= init; + else + cache_ctrl_state <= cache_ctrl_state_next; + cache_ctrl2_state <= cache_ctrl2_state_next; + end if; + end if; + end process; + + cache_rdy <= read_rdy and not write_fifo_full and not instant_raw; + rdy <= cache_rdy; + + dout <= single_read_reg when single_read_reg_vld = '1' else data_ram_dout; + + tag_match <= '1' when to_tag(request_pa) = 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 to_cache_index(request_va); + tag_ram_addr_rd <= to_cache_index(addr) when (was_miss = '0' and instant_raw = '0') else to_cache_index(request_va); + + cache_entry_out <= to_dcache_entry(tag_ram_dout); + cache_entry_out_inv <= to_dcache_entry(tag_ram_dout_inv); + + data_ram_addr <= (to_cache_index(addr) & to_word_index(addr)) when (was_miss = '0' and instant_raw = '0' and fill_count_en = '0') else (to_cache_index(request_va) & fill_word_index); + ctrl_data_ram_addr <= to_cache_index(request_va) & fill_word_index; + ctrl_data_ram_we <= (others => ram_write_en and bus_din_vld); + data_ram_we <= be_reg when (write_hit = '1') else (others => '0'); + + write_through_en <= cache_req_wr and tlb_result.hit and not tlb_result.flags.D; + +cache_ctrl_state_fsm: + process(cache_ctrl_state, cache_req_rd, cache_hit, single_read_reg_vld, tlb_result, flush_count_rdy, fill_count_rdy, request_count_rdy, request_pa, bus_cmd_rdy, bus_din_vld, invalidate_req, invalidate_all, write_data_avail) + begin + read_rdy <= '0'; + tag_ram_we <= '0'; + flush_count_en <= '0'; + flush_count_rst <= '0'; + invalidate_en <= '0'; + request_count_en <= '0'; + fill_count_en <= '0'; + ram_write_en <= '0'; + bus_cmd_cycle_en <= '0'; + bus_cmd_we <= '0'; + was_miss <= '0'; + invalidate_ack <= '0'; + single_read_reg_vld <= '0'; + read_write_buffer <= '0'; + + cache_entry_in.tv_p <= (others => '0'); + cache_entry_in.tag <= to_tag(request_pa); + cache_entry_in.valid <= '0'; + cache_ctrl_state_next <= cache_ctrl_state; + + case cache_ctrl_state is + + when init => + cache_ctrl_state_next <= ready; + read_rdy <= '1'; + + when ready => + read_rdy <= (tlb_result.hit and (not tlb_result.flags.N or cache_hit)) or not cache_req_rd; + if invalidate_req = '1' then + cache_ctrl_state_next <= invalidate; + invalidate_en <= '1'; + elsif write_data_avail = '1' then + cache_ctrl_state_next <= ready; + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + read_write_buffer <= '1'; + bus_cmd_we <= '1'; + end if; + elsif cache_req_rd = '1' then + if tlb_result.hit = '1' then + if tlb_result.flags.N = '1' or cache_hit = '0' then + read_rdy <= '0'; + cache_ctrl_state_next <= bus_request; + bus_cmd_cycle_en <= '1'; + end if; + end if; + end if; + + when invalidate => + cache_ctrl_state_next <= rd_cache; + invalidate_en <= '1'; + invalidate_ack <= '1'; + if invalidate_all = '1' then + cache_ctrl_state_next <= 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'); + cache_ctrl_state_next <= 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'; + cache_ctrl_state_next <= rd_cache; + end if; + + when bus_request => + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + cache_ctrl_state_next <= mem_read; + if tlb_result.flags.N = '1' then + cache_ctrl_state_next <= mem_read_single; + end if; + end if; + + when mem_read => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + request_count_en <= '1'; + ram_write_en <= '1'; + fill_count_en <= '1'; + if request_count_rdy = '1' then + bus_cmd_we <= '0'; + cache_ctrl_state_next <= mem_read_data; + end if; + + when mem_read_single => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_ctrl_state_next <= mem_read_data_single; + + when mem_read_data => + bus_cmd_cycle_en <= '1'; + fill_count_en <= '1'; + ram_write_en <= '1'; + if fill_count_rdy = '1' then + tag_ram_we <= '1'; + cache_entry_in.valid <= '1'; + cache_ctrl_state_next <= rd_cache; + end if; + + when mem_read_data_single => + bus_cmd_cycle_en <= '1'; + if bus_din_vld = '1' then + cache_ctrl_state_next <= single_valid; + end if; + + when single_valid => + read_rdy <= '1'; + single_read_reg_vld <= '1'; + cache_ctrl_state_next <= ready; + + when rd_cache => + was_miss <= '1'; + cache_ctrl_state_next <= ready; + + when others => + cache_ctrl_state_next <= ready; + end case; + + end process; + +flush_counter: + process(clk) + begin + if rising_edge(clk) then + if ctrl_in.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) + begin + if rising_edge(clk) then + if request_count_en = '0' then + request_count_rdy <= '0'; + request_count <= 2**word_index_width-1; + else + if bus_cmd_rdy = '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) + begin + if rising_edge(clk) then + if fill_count_en = '0' then + fill_count_rdy <= '0'; + fill_count <= 2**word_index_width-1; + else + if bus_din_vld = '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; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_20130728.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_20130728.vhd new file mode 100644 index 0000000..aec932f --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_20130728.vhd @@ -0,0 +1,743 @@ +-------------------------------------------------------------------------- +-- 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_util_pkg.all; +use work.mips_types.all; +use work.busmaster_types.all; + +ENTITY dcache IS + Generic + ( + cache_size : natural := 2048; -- words + line_size : natural := 8; -- words + CACHE_ALL : boolean := false; + USE_CACHE : boolean := true; + USE_TLB : boolean := false + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + + en : in STD_LOGIC; + we : in STD_LOGIC; + be : in unsigned(3 downto 0); + addr : in word_t; + din : in word_t; + dout : out word_t; + rdy : out STD_LOGIC; + + -- Cache control + ctrl_in : in cache_ctrl_t; + + tlb_query_out : out tlb_query_in_t; + tlb_query_in : in tlb_query_out_t; + + -- busmaster data + bus_din : in word_t; + bus_din_rdy : out std_logic; + bus_din_vld : in std_logic; + + bus_dout : out word_t; + bus_dout_vld : out std_logic; + bus_dout_rdy : in std_logic; + + -- busmaster command + bus_cmd_rdy : in std_logic; + bus_cmd_we : out std_logic; + bus_cmd_cycle_en : out std_logic; + bus_cmd_out : out bm_cmd_t; + bus_cyc_complete : in 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; + + function to_word_index(x : word_t) return unsigned is + variable result : unsigned(word_index_width-1 downto 0); + begin + result := x(word_index_width+1 downto 2); + + return result; + + end to_word_index; + + + function to_cache_index(x : word_t) return unsigned is + variable result : unsigned(cache_index_width-1 downto 0); + begin + result := x(cache_index_width+word_index_width+1 downto word_index_width+2); + + return result; + + end to_cache_index; + + function to_tag(x : word_t) return unsigned is + variable result : unsigned(tag_width-1 downto 0); + begin + result := x(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + return result; + + end to_tag; + + + type cache_ctrl_state_t is (init, ready, refill_pre, refill, refill_post, read_nofill); + signal cache_ctrl_state, cache_ctrl_state_next : cache_ctrl_state_t; + + type cache_mgmt_state_t is (init, ready, invalidate, flush, bus_request, mem_read, mem_read_single, mem_read_data, mem_read_data_single, rd_cache, upd_cache, mem_write_through); + signal cache_mgmt_state, cache_mgmt_state_next : cache_mgmt_state_t; + + signal cache_req_rd : std_logic; + signal cache_req_wr : std_logic; + signal cache_refill_en : std_logic; + signal cache_refill_bsy : std_logic; + signal single_read_reg_vld : std_logic; + signal cache_rdy : 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_va : word_t; + signal request_pa : word_t; + + 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 data_ram_addr : unsigned(lg2(cache_size)-1 downto 0); + signal data_ram_dout : word_t; + signal data_reg : word_t; + signal be_reg : unsigned(3 downto 0); + signal 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 data_ram_we : unsigned(3 downto 0); + signal single_read_reg : word_t; + + 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 write_hit : std_logic; + signal instant_raw : std_logic; + signal ram_write_en : std_logic; + signal fill_word_index : unsigned(word_index_width-1 downto 0); + signal req_word_index : unsigned(word_index_width-1 downto 0); + signal hit_cache_index : unsigned(cache_index_width-1 downto 0); + + signal write_fifo_din : unsigned(67 downto 0); + signal write_fifo_dout : unsigned(67 downto 0); + signal write_fifo_re : std_logic; + signal write_fifo_we : std_logic; + signal write_fifo_full : std_logic; + signal write_fifo_empty : std_logic; + signal write_data_avail : std_logic; + signal read_write_buffer : std_logic; + + alias write_fifo_addr_in is write_fifo_din(31 downto 0); + alias write_fifo_data_in is write_fifo_din(63 downto 32); + alias write_fifo_sel_in is write_fifo_din(67 downto 64); + alias write_fifo_addr_out is write_fifo_dout(31 downto 0); + alias write_fifo_data_out is write_fifo_dout(63 downto 32); + alias write_fifo_sel_out is write_fifo_dout(67 downto 64); + + signal write_through_en : std_logic; + signal tlb_exc_miss : std_logic; + signal tlb_exc_read : std_logic; + signal tlb_exc_write : std_logic; + signal tlb_exc_dirty : std_logic; + signal tlb_exc : std_logic; + signal tlb_exc_type : unsigned(1 downto 0); + + signal tlb_result_no_translate : tlb_query_out_t; + signal tlb_result : tlb_query_out_t; + +begin + + cache_index_inv <= ctrl_in.inv_addr(cache_index_width+word_index_width+1 downto word_index_width+2); + tag_inv <= ctrl_in.inv_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + bus_din_rdy <= bus_din_vld; + bus_cmd_out.addr <= write_fifo_addr_out when read_write_buffer = '1' else to_tag(request_pa) & to_cache_index(request_pa) & req_word_index & "00"; + bus_cmd_out.rw <= read_write_buffer; + bus_cmd_out.sel <= write_fifo_sel_out when read_write_buffer = '1' else (others => '1'); + bus_dout <= write_fifo_data_out; + bus_dout_vld <= read_write_buffer and write_data_avail; + + tlb_query_out.vaddr <= request_va; + tlb_result_no_translate.paddr <= request_va; + tlb_result_no_translate.hit_idx <= (others => '0'); + tlb_result_no_translate.hit <= '1'; + tlb_result_no_translate.flags.N <= '0' when CACHE_ALL = true else '1' when USE_CACHE = false else '1' when request_va(31 downto 29) = "101" else '0'; + tlb_result_no_translate.flags.D <= '0'; + tlb_result_no_translate.flags.V <= '1'; + + tlb_result <= tlb_query_in when USE_TLB else tlb_result_no_translate; + request_pa <= tlb_result.paddr; + + tlb_exc_miss <= not tlb_result.hit; + tlb_exc_read <= tlb_exc_miss and not we_reg; + tlb_exc_write <= tlb_exc_miss and we_reg; + tlb_exc_dirty <= tlb_result.hit and tlb_result.flags.D and we_reg; + tlb_exc <= tlb_exc_read or tlb_exc_write or tlb_exc_dirty; + + tlb_exc_type <= "10" when tlb_exc_read = '1' else "01" when tlb_exc_write = '1' else "11" when tlb_exc_dirty = '1' else "00"; + + write_hit <= we_reg and cache_hit and not tlb_exc; + +-- Instantiate synchronous FIFO +inst_write_fifo: entity work.fifo_sync + GENERIC MAP + ( + addr_width => 4, + data_width => 68, + do_last_read_update => true + ) + PORT MAP + ( + rst => rst, + clk => clk, + we => write_fifo_we, + re => write_fifo_re, + fifo_full => write_fifo_full, + fifo_empty => write_fifo_empty, + fifo_afull => open, + fifo_aempty => open, + data_w => write_fifo_din, + data_r => write_fifo_dout + ); + + write_fifo_data_in <= data_reg; + write_fifo_addr_in <= request_pa; + write_fifo_sel_in <= be_reg; + write_fifo_re <= read_write_buffer and bus_dout_rdy and bus_cmd_rdy; + write_fifo_we <= write_through_en; + write_data_avail <= not write_fifo_empty; + +fill_address_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' then + request_va <= addr; + fill_word_index <= to_word_index(addr); + elsif bus_din_vld = '1' and fill_count_en = '1' then + fill_word_index <= fill_word_index + 1; + end if; + end if; + end process; + +request_address_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' then + req_word_index <= to_word_index(addr); + elsif request_count_en = '1' and bus_cmd_rdy = '1' then + req_word_index <= req_word_index + 1; + end if; + end if; + end process; + +single_read_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + single_read_reg_vld <= '0'; + elsif bus_din_vld = '1' then + single_read_reg <= bus_din; + single_read_reg_vld <= tlb_result.flags.N; + elsif cache_rdy = '1' then + single_read_reg_vld <= '0'; + end if; + end if; + end process; + +request_register: + process(clk) + begin + if rising_edge(clk) then + cache_req_rd <= en and not we; + cache_req_wr <= en and we; + end if; + end process; + +request_register2: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + we_reg <= '0'; + elsif cache_rdy = '1' then + data_reg <= din; + be_reg <= be; + we_reg <= we; + if we = '1' then + hit_cache_index <= to_cache_index(addr); + end if; + end if; + end if; + end process; + +instant_raw_logic: + process(clk) + begin + if rising_edge(clk) then + instant_raw <= '0'; + if to_word_index(addr) = fill_word_index and to_cache_index(addr) = hit_cache_index then + instant_raw <= write_hit and en and not 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, + clk_b => clk, + 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, + clk_b => clk, + en_a => '1', + en_b => '1', + we_a => data_ram_we(i), + we_b => ctrl_data_ram_we(i), + addr_a => ctrl_data_ram_addr, + addr_b => data_ram_addr, + din_a => data_reg(8*(i+1)-1 downto 8*i), + din_b => bus_din(8*(i+1)-1 downto 8*i), + dout_a => open, + dout_b => data_ram_dout(8*(i+1)-1 downto 8*i) + ); + end generate; + + + +cache_invalidate_request: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' or ctrl_in.inv_all = '1' or ctrl_in.inv_at = '1' then + invalidate_req <= '1'; + invalidate_all <= ctrl_in.inv_all or rst; + 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) + begin + if rising_edge(clk) then + if rst = '1' then + cache_mgmt_state <= init; + cache_ctrl_state <= init; + else + cache_mgmt_state <= cache_mgmt_state_next; + cache_ctrl_state <= cache_ctrl_state_next; + end if; + end if; + end process; + + rdy <= cache_rdy and not write_fifo_full; + dout <= single_read_reg when single_read_reg_vld = '1' else data_ram_dout; + + tag_match <= '1' when to_tag(request_pa) = 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 to_cache_index(request_va); + tag_ram_addr_rd <= to_cache_index(addr) when (was_miss = '0' and instant_raw = '0') else to_cache_index(request_va); + + cache_entry_out <= to_dcache_entry(tag_ram_dout); + cache_entry_out_inv <= to_dcache_entry(tag_ram_dout_inv); + + data_ram_addr <= (to_cache_index(addr) & to_word_index(addr)) when (was_miss = '0' and instant_raw = '0' and fill_count_en = '0') else (to_cache_index(request_va) & fill_word_index); + ctrl_data_ram_addr <= to_cache_index(request_va) & fill_word_index; + ctrl_data_ram_we <= (others => ram_write_en and bus_din_vld); + data_ram_we <= be_reg when (write_hit = '1') else (others => '0'); + + write_through_en <= cache_req_wr and tlb_result.hit and not tlb_result.flags.D; + +cache_ctrl_state_fsm: + process(cache_ctrl_state, tlb_result, cache_req_rd, cache_hit, cache_refill_bsy, instant_raw, we_reg, bus_dout_rdy, bus_cmd_rdy) + begin + + cache_rdy <= '0'; + cache_refill_en <= '0'; + cache_ctrl_state_next <= cache_ctrl_state; + + case cache_ctrl_state is + + when init => + cache_rdy <= '1'; + cache_ctrl_state_next <= ready; + + when ready => + cache_rdy <= not (instant_raw and cache_hit); + if cache_req_rd = '1' then + if tlb_result.flags.N = '1' then + cache_rdy <= '0'; + cache_ctrl_state_next <= refill_pre; + elsif cache_hit = '0' and tlb_result.hit = '1' then + cache_rdy <= '0'; + cache_ctrl_state_next <= refill_pre; + end if; + end if; + + when refill_pre => + cache_refill_en <= '1'; + if cache_refill_bsy = '1' then + cache_ctrl_state_next <= refill; + end if; + + when refill => + if cache_refill_bsy = '0' then + cache_ctrl_state_next <= refill_post; + end if; + + when refill_post => + cache_rdy <= '1'; + cache_ctrl_state_next <= ready; + + when read_nofill => + cache_rdy <= '1'; + cache_ctrl_state_next <= ready; + + when others => + cache_ctrl_state_next <= ready; + + end case; + + end process; + +cache_state: + process(cache_mgmt_state, cache_refill_en, tlb_result, flush_count_rdy, fill_count_rdy, request_count_rdy, request_pa, bus_cmd_rdy, bus_din_vld, invalidate_req, invalidate_all, write_data_avail) + begin + cache_refill_bsy <= '0'; + tag_ram_we <= '0'; + flush_count_en <= '0'; + flush_count_rst <= '0'; + invalidate_en <= '0'; + request_count_en <= '0'; + fill_count_en <= '0'; + ram_write_en <= '0'; + bus_cmd_cycle_en <= '0'; + bus_cmd_we <= '0'; + was_miss <= '0'; + invalidate_ack <= '0'; + read_write_buffer <= '0'; + + cache_entry_in.tv_p <= (others => '0'); + cache_entry_in.tag <= to_tag(request_pa); + cache_entry_in.valid <= '0'; + cache_mgmt_state_next <= cache_mgmt_state; + + case cache_mgmt_state is + + when init => + cache_mgmt_state_next <= ready; + + when ready => + if invalidate_req = '1' then + cache_mgmt_state_next <= invalidate; + invalidate_en <= '1'; + elsif write_data_avail = '1' then + cache_mgmt_state_next <= bus_request; + bus_cmd_cycle_en <= '1'; + elsif cache_refill_en = '1' then + cache_mgmt_state_next <= bus_request; + bus_cmd_cycle_en <= '1'; + end if; + + when invalidate => + cache_mgmt_state_next <= rd_cache; + invalidate_en <= '1'; + invalidate_ack <= '1'; + if invalidate_all = '1' then + cache_mgmt_state_next <= 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'); + cache_mgmt_state_next <= 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'; + cache_mgmt_state_next <= rd_cache; + end if; + + when bus_request => + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + cache_mgmt_state_next <= mem_read; + if write_data_avail = '1' then + cache_mgmt_state_next <= mem_write_through; + elsif tlb_result.flags.N = '1' then + cache_refill_bsy <= '1'; + cache_mgmt_state_next <= mem_read_single; + end if; + end if; + when mem_write_through => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + read_write_buffer <= '1'; + if write_data_avail = '0' then + read_write_buffer <= '0'; + bus_cmd_we <= '0'; + cache_mgmt_state_next <= ready; + end if; + + when mem_read => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_refill_bsy <= '1'; + request_count_en <= '1'; + ram_write_en <= '1'; + fill_count_en <= '1'; + if request_count_rdy = '1' then + bus_cmd_we <= '0'; + cache_mgmt_state_next <= mem_read_data; + end if; + + when mem_read_single => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_refill_bsy <= '1'; + cache_mgmt_state_next <= mem_read_data_single; + + when mem_read_data => + bus_cmd_cycle_en <= '1'; + cache_refill_bsy <= '1'; + fill_count_en <= '1'; + ram_write_en <= '1'; + if fill_count_rdy = '1' then + tag_ram_we <= '1'; + cache_entry_in.valid <= '1'; + cache_mgmt_state_next <= rd_cache; + end if; + + when mem_read_data_single => + bus_cmd_cycle_en <= '1'; + cache_refill_bsy <= '1'; + if bus_din_vld = '1' then + cache_mgmt_state_next <= ready; + end if; + + when rd_cache => + was_miss <= '1'; + cache_mgmt_state_next <= ready; + + when others => + cache_mgmt_state_next <= ready; + end case; + + end process; + +flush_counter: + process(clk) + begin + if rising_edge(clk) then + if ctrl_in.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) + begin + if rising_edge(clk) then + if request_count_en = '0' then + request_count_rdy <= '0'; + request_count <= 2**word_index_width-1; + else + if bus_cmd_rdy = '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) + begin + if rising_edge(clk) then + if fill_count_en = '0' then + fill_count_rdy <= '0'; + fill_count <= 2**word_index_width-1; + else + if bus_din_vld = '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; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_20130729.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_20130729.vhd new file mode 100644 index 0000000..b84b402 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_20130729.vhd @@ -0,0 +1,754 @@ +-------------------------------------------------------------------------- +-- 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_util_pkg.all; +use work.mips_types.all; +use work.busmaster_types.all; + +ENTITY dcache IS + Generic + ( + cache_size : natural := 2048; -- words + line_size : natural := 8; -- words + write_fifo_size : natural := 4; + CACHE_ALL : boolean := false; + USE_CACHE : boolean := true; + USE_TLB : boolean := false + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + + en : in STD_LOGIC; + we : in STD_LOGIC; + be : in unsigned(3 downto 0); + addr : in word_t; + din : in word_t; + dout : out word_t; + rdy : out STD_LOGIC; + + -- Cache control + ctrl_in : in cache_ctrl_t; + + tlb_query_out : out tlb_query_in_t; + tlb_query_in : in tlb_query_out_t; + + -- busmaster data + bus_din : in word_t; + bus_din_rdy : out std_logic; + bus_din_vld : in std_logic; + + bus_dout : out word_t; + bus_dout_vld : out std_logic; + bus_dout_rdy : in std_logic; + + -- busmaster command + bus_cmd_rdy : in std_logic; + bus_cmd_we : out std_logic; + bus_cmd_cycle_en : out std_logic; + bus_cmd_out : out bm_cmd_t; + bus_cyc_complete : in 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; + + function to_word_index(x : word_t) return unsigned is + variable result : unsigned(word_index_width-1 downto 0); + begin + result := x(word_index_width+1 downto 2); + + return result; + + end to_word_index; + + + function to_cache_index(x : word_t) return unsigned is + variable result : unsigned(cache_index_width-1 downto 0); + begin + result := x(cache_index_width+word_index_width+1 downto word_index_width+2); + + return result; + + end to_cache_index; + + function to_tag(x : word_t) return unsigned is + variable result : unsigned(tag_width-1 downto 0); + begin + result := x(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + return result; + + end to_tag; + + + type cache_ctrl2_state_t is (init, ready, refill_pre, refill, refill_post, read_nofill); + signal cache_ctrl2_state, cache_ctrl2_state_next : cache_ctrl2_state_t; + + type cache_ctrl_state_t is (init, ready, invalidate, flush, bus_request, mem_read, mem_read_single, mem_read_data, mem_read_data_single, rd_cache, upd_cache, mem_write_through); + signal cache_ctrl_state, cache_ctrl_state_next : cache_ctrl_state_t; + + signal cache_req_rd : std_logic; + signal cache_req_wr : std_logic; + signal cache_refill_en : std_logic; + signal cache_refill_bsy : std_logic; + signal single_read_reg_vld : std_logic; + signal cache_rdy : std_logic; + signal cache_hit : std_logic; + signal read_rdy : std_logic; + signal tag_match : std_logic; + signal cache_hit_inv : std_logic; + signal tag_match_inv : std_logic; + signal request_va : word_t; + signal request_pa : word_t; + + 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 data_ram_addr : unsigned(lg2(cache_size)-1 downto 0); + signal data_ram_dout : word_t; + signal data_reg : word_t; + signal be_reg : unsigned(3 downto 0); + signal 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 data_ram_we : unsigned(3 downto 0); + signal single_read_reg : word_t; + + 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 write_hit : std_logic; + signal instant_raw : std_logic; + signal ram_write_en : std_logic; + signal fill_word_index : unsigned(word_index_width-1 downto 0); + signal req_word_index : unsigned(word_index_width-1 downto 0); + signal hit_cache_index : unsigned(cache_index_width-1 downto 0); + + signal write_fifo_din : unsigned(67 downto 0); + signal write_fifo_dout : unsigned(67 downto 0); + signal write_fifo_re : std_logic; + signal write_fifo_we : std_logic; + signal write_fifo_full : std_logic; + signal write_fifo_empty : std_logic; + signal write_data_avail : std_logic; + signal read_write_buffer : std_logic; + + alias write_fifo_addr_in is write_fifo_din(31 downto 0); + alias write_fifo_data_in is write_fifo_din(63 downto 32); + alias write_fifo_sel_in is write_fifo_din(67 downto 64); + alias write_fifo_addr_out is write_fifo_dout(31 downto 0); + alias write_fifo_data_out is write_fifo_dout(63 downto 32); + alias write_fifo_sel_out is write_fifo_dout(67 downto 64); + + signal write_through_en : std_logic; + signal tlb_exc_miss : std_logic; + signal tlb_exc_read : std_logic; + signal tlb_exc_write : std_logic; + signal tlb_exc_dirty : std_logic; + signal tlb_exc : std_logic; + signal tlb_exc_type : unsigned(1 downto 0); + + signal tlb_result_no_translate : tlb_query_out_t; + signal tlb_result : tlb_query_out_t; + +begin + + cache_index_inv <= ctrl_in.inv_addr(cache_index_width+word_index_width+1 downto word_index_width+2); + tag_inv <= ctrl_in.inv_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + bus_din_rdy <= bus_din_vld; + bus_cmd_out.addr <= write_fifo_addr_out when read_write_buffer = '1' else to_tag(request_pa) & to_cache_index(request_pa) & req_word_index & "00"; + bus_cmd_out.rw <= read_write_buffer; + bus_cmd_out.sel <= write_fifo_sel_out when read_write_buffer = '1' else (others => '1'); + bus_dout <= write_fifo_data_out; + bus_dout_vld <= read_write_buffer and write_data_avail; + + tlb_query_out.vaddr <= request_va; + tlb_result_no_translate.paddr <= request_va; + tlb_result_no_translate.hit_idx <= (others => '0'); + tlb_result_no_translate.hit <= '1'; + tlb_result_no_translate.flags.N <= '0' when CACHE_ALL = true else '1' when USE_CACHE = false else '1' when request_va(31 downto 29) = "101" else '0'; + tlb_result_no_translate.flags.D <= '0'; + tlb_result_no_translate.flags.V <= '1'; + + tlb_result <= tlb_query_in when USE_TLB else tlb_result_no_translate; + request_pa <= tlb_result.paddr; + + tlb_exc_miss <= not tlb_result.hit; + tlb_exc_read <= tlb_exc_miss and not we_reg; + tlb_exc_write <= tlb_exc_miss and we_reg; + tlb_exc_dirty <= tlb_result.hit and tlb_result.flags.D and we_reg; + tlb_exc <= tlb_exc_read or tlb_exc_write or tlb_exc_dirty; + + tlb_exc_type <= "10" when tlb_exc_read = '1' else "01" when tlb_exc_write = '1' else "11" when tlb_exc_dirty = '1' else "00"; + + write_hit <= we_reg and cache_hit and not tlb_exc; + +-- Instantiate synchronous FIFO +inst_write_fifo: entity work.fifo_sync + GENERIC MAP + ( + addr_width => lg2(write_fifo_size), + data_width => 68, + do_last_read_update => true + ) + PORT MAP + ( + rst => rst, + clk => clk, + we => write_fifo_we, + re => write_fifo_re, + fifo_full => write_fifo_full, + fifo_empty => write_fifo_empty, + fifo_afull => open, + fifo_aempty => open, + data_w => write_fifo_din, + data_r => write_fifo_dout + ); + + write_fifo_data_in <= data_reg; + write_fifo_addr_in <= request_pa; + write_fifo_sel_in <= be_reg; + write_fifo_re <= read_write_buffer and bus_dout_rdy and bus_cmd_rdy; + write_fifo_we <= write_through_en; + write_data_avail <= not write_fifo_empty; + +fill_address_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' and en = '1' then + request_va <= addr; + fill_word_index <= to_word_index(addr); + elsif bus_din_vld = '1' and fill_count_en = '1' then + fill_word_index <= fill_word_index + 1; + end if; + end if; + end process; + +request_address_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' and en = '1' then + req_word_index <= to_word_index(addr); + elsif request_count_en = '1' and bus_cmd_rdy = '1' then + req_word_index <= req_word_index + 1; + end if; + end if; + end process; + +single_read_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + single_read_reg_vld <= '0'; + elsif bus_din_vld = '1' then + single_read_reg <= bus_din; + single_read_reg_vld <= tlb_result.flags.N; + elsif cache_rdy = '1' then + single_read_reg_vld <= '0'; + end if; + end if; + end process; + +request_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' then + cache_req_rd <= en and not we; + cache_req_wr <= en and we; + end if; + end if; + end process; + +request_register2: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + we_reg <= '0'; + elsif cache_rdy = '1' then + data_reg <= din; + be_reg <= be; + we_reg <= we; + if we = '1' then + hit_cache_index <= to_cache_index(addr); + end if; + end if; + end if; + end process; + +instant_raw_logic: + process(clk) + begin + if rising_edge(clk) then + instant_raw <= '0'; + if to_word_index(addr) = fill_word_index and to_cache_index(addr) = hit_cache_index then + instant_raw <= write_hit and en and not 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, + clk_b => clk, + 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, + clk_b => clk, + en_a => '1', + en_b => '1', + we_a => data_ram_we(i), + we_b => ctrl_data_ram_we(i), + addr_a => ctrl_data_ram_addr, + addr_b => data_ram_addr, + din_a => data_reg(8*(i+1)-1 downto 8*i), + din_b => bus_din(8*(i+1)-1 downto 8*i), + dout_a => open, + dout_b => data_ram_dout(8*(i+1)-1 downto 8*i) + ); + end generate; + + + +cache_invalidate_request: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' or ctrl_in.inv_all = '1' or ctrl_in.inv_at = '1' then + invalidate_req <= '1'; + invalidate_all <= ctrl_in.inv_all or rst; + 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) + begin + if rising_edge(clk) then + if rst = '1' then + cache_ctrl_state <= init; + cache_ctrl2_state <= init; + else + cache_ctrl_state <= cache_ctrl_state_next; + cache_ctrl2_state <= cache_ctrl2_state_next; + end if; + end if; + end process; + + cache_rdy <= read_rdy and not write_fifo_full and not instant_raw; + rdy <= cache_rdy; + + dout <= single_read_reg when single_read_reg_vld = '1' else data_ram_dout; + + tag_match <= '1' when to_tag(request_pa) = 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 to_cache_index(request_va); + tag_ram_addr_rd <= to_cache_index(addr) when (was_miss = '0' and instant_raw = '0') else to_cache_index(request_va); + + cache_entry_out <= to_dcache_entry(tag_ram_dout); + cache_entry_out_inv <= to_dcache_entry(tag_ram_dout_inv); + + data_ram_addr <= (to_cache_index(addr) & to_word_index(addr)) when (was_miss = '0' and instant_raw = '0' and fill_count_en = '0') else (to_cache_index(request_va) & fill_word_index); + ctrl_data_ram_addr <= to_cache_index(request_va) & fill_word_index; + ctrl_data_ram_we <= (others => ram_write_en and bus_din_vld); + data_ram_we <= be_reg when (write_hit = '1') else (others => '0'); + + write_through_en <= cache_req_wr and tlb_result.hit and not tlb_result.flags.D; + +cache_ctrl2_state_fsm: + process(cache_ctrl2_state, tlb_result, cache_req_rd, cache_hit, cache_refill_bsy) + begin + + read_rdy <= '0'; + cache_refill_en <= '0'; + cache_ctrl2_state_next <= cache_ctrl2_state; + + case cache_ctrl2_state is + + when init => + read_rdy <= '1'; + cache_ctrl2_state_next <= ready; + + when ready => + read_rdy <= '1'; + if cache_req_rd = '1' then + if tlb_result.hit = '1' then + if tlb_result.flags.N = '1' then + read_rdy <= '0'; + cache_ctrl2_state_next <= refill_pre; + elsif cache_hit = '0' then + read_rdy <= '0'; + cache_ctrl2_state_next <= refill_pre; + end if; + end if; + end if; + + when refill_pre => + cache_refill_en <= '1'; + if cache_refill_bsy = '1' then + cache_ctrl2_state_next <= refill; + end if; + + when refill => + if cache_refill_bsy = '0' then + cache_ctrl2_state_next <= refill_post; + end if; + + when refill_post => + read_rdy <= '1'; + cache_ctrl2_state_next <= ready; + + when read_nofill => + read_rdy <= '1'; + cache_ctrl2_state_next <= ready; + + when others => + cache_ctrl2_state_next <= ready; + + end case; + + end process; + +cache_ctrl_state_fsm: + process(cache_ctrl_state, cache_refill_en, tlb_result, flush_count_rdy, fill_count_rdy, request_count_rdy, request_pa, bus_cmd_rdy, bus_din_vld, invalidate_req, invalidate_all, write_data_avail) + begin + cache_refill_bsy <= '0'; + tag_ram_we <= '0'; + flush_count_en <= '0'; + flush_count_rst <= '0'; + invalidate_en <= '0'; + request_count_en <= '0'; + fill_count_en <= '0'; + ram_write_en <= '0'; + bus_cmd_cycle_en <= '0'; + bus_cmd_we <= '0'; + was_miss <= '0'; + invalidate_ack <= '0'; + read_write_buffer <= '0'; + + cache_entry_in.tv_p <= (others => '0'); + cache_entry_in.tag <= to_tag(request_pa); + cache_entry_in.valid <= '0'; + cache_ctrl_state_next <= cache_ctrl_state; + + case cache_ctrl_state is + + when init => + cache_ctrl_state_next <= ready; + + when ready => + if invalidate_req = '1' then + cache_ctrl_state_next <= invalidate; + invalidate_en <= '1'; + elsif write_data_avail = '1' then + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + read_write_buffer <= '1'; + bus_cmd_we <= '1'; + end if; + elsif cache_refill_en = '1' then + cache_ctrl_state_next <= bus_request; + bus_cmd_cycle_en <= '1'; + end if; + + when invalidate => + cache_ctrl_state_next <= rd_cache; + invalidate_en <= '1'; + invalidate_ack <= '1'; + if invalidate_all = '1' then + cache_ctrl_state_next <= 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'); + cache_ctrl_state_next <= 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'; + cache_ctrl_state_next <= rd_cache; + end if; + + when bus_request => + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + cache_ctrl_state_next <= mem_read; + if write_data_avail = '1' then + cache_ctrl_state_next <= mem_write_through; + elsif tlb_result.flags.N = '1' then + cache_refill_bsy <= '1'; + cache_ctrl_state_next <= mem_read_single; + end if; + end if; + when mem_write_through => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + read_write_buffer <= '1'; + if write_data_avail = '0' then + read_write_buffer <= '0'; + bus_cmd_we <= '0'; + cache_ctrl_state_next <= ready; + end if; + + when mem_read => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_refill_bsy <= '1'; + request_count_en <= '1'; + ram_write_en <= '1'; + fill_count_en <= '1'; + if request_count_rdy = '1' then + bus_cmd_we <= '0'; + cache_ctrl_state_next <= mem_read_data; + end if; + + when mem_read_single => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_refill_bsy <= '1'; + cache_ctrl_state_next <= mem_read_data_single; + + when mem_read_data => + bus_cmd_cycle_en <= '1'; + cache_refill_bsy <= '1'; + fill_count_en <= '1'; + ram_write_en <= '1'; + if fill_count_rdy = '1' then + tag_ram_we <= '1'; + cache_entry_in.valid <= '1'; + cache_ctrl_state_next <= rd_cache; + end if; + + when mem_read_data_single => + bus_cmd_cycle_en <= '1'; + cache_refill_bsy <= '1'; + if bus_din_vld = '1' then + cache_ctrl_state_next <= ready; + end if; + + when rd_cache => + was_miss <= '1'; + cache_ctrl_state_next <= ready; + + when others => + cache_ctrl_state_next <= ready; + end case; + + end process; + +flush_counter: + process(clk) + begin + if rising_edge(clk) then + if ctrl_in.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) + begin + if rising_edge(clk) then + if request_count_en = '0' then + request_count_rdy <= '0'; + request_count <= 2**word_index_width-1; + else + if bus_cmd_rdy = '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) + begin + if rising_edge(clk) then + if fill_count_en = '0' then + fill_count_rdy <= '0'; + fill_count <= 2**word_index_width-1; + else + if bus_din_vld = '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; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_20130729_works.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_20130729_works.vhd new file mode 100644 index 0000000..b84b402 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_20130729_works.vhd @@ -0,0 +1,754 @@ +-------------------------------------------------------------------------- +-- 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_util_pkg.all; +use work.mips_types.all; +use work.busmaster_types.all; + +ENTITY dcache IS + Generic + ( + cache_size : natural := 2048; -- words + line_size : natural := 8; -- words + write_fifo_size : natural := 4; + CACHE_ALL : boolean := false; + USE_CACHE : boolean := true; + USE_TLB : boolean := false + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + + en : in STD_LOGIC; + we : in STD_LOGIC; + be : in unsigned(3 downto 0); + addr : in word_t; + din : in word_t; + dout : out word_t; + rdy : out STD_LOGIC; + + -- Cache control + ctrl_in : in cache_ctrl_t; + + tlb_query_out : out tlb_query_in_t; + tlb_query_in : in tlb_query_out_t; + + -- busmaster data + bus_din : in word_t; + bus_din_rdy : out std_logic; + bus_din_vld : in std_logic; + + bus_dout : out word_t; + bus_dout_vld : out std_logic; + bus_dout_rdy : in std_logic; + + -- busmaster command + bus_cmd_rdy : in std_logic; + bus_cmd_we : out std_logic; + bus_cmd_cycle_en : out std_logic; + bus_cmd_out : out bm_cmd_t; + bus_cyc_complete : in 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; + + function to_word_index(x : word_t) return unsigned is + variable result : unsigned(word_index_width-1 downto 0); + begin + result := x(word_index_width+1 downto 2); + + return result; + + end to_word_index; + + + function to_cache_index(x : word_t) return unsigned is + variable result : unsigned(cache_index_width-1 downto 0); + begin + result := x(cache_index_width+word_index_width+1 downto word_index_width+2); + + return result; + + end to_cache_index; + + function to_tag(x : word_t) return unsigned is + variable result : unsigned(tag_width-1 downto 0); + begin + result := x(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + return result; + + end to_tag; + + + type cache_ctrl2_state_t is (init, ready, refill_pre, refill, refill_post, read_nofill); + signal cache_ctrl2_state, cache_ctrl2_state_next : cache_ctrl2_state_t; + + type cache_ctrl_state_t is (init, ready, invalidate, flush, bus_request, mem_read, mem_read_single, mem_read_data, mem_read_data_single, rd_cache, upd_cache, mem_write_through); + signal cache_ctrl_state, cache_ctrl_state_next : cache_ctrl_state_t; + + signal cache_req_rd : std_logic; + signal cache_req_wr : std_logic; + signal cache_refill_en : std_logic; + signal cache_refill_bsy : std_logic; + signal single_read_reg_vld : std_logic; + signal cache_rdy : std_logic; + signal cache_hit : std_logic; + signal read_rdy : std_logic; + signal tag_match : std_logic; + signal cache_hit_inv : std_logic; + signal tag_match_inv : std_logic; + signal request_va : word_t; + signal request_pa : word_t; + + 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 data_ram_addr : unsigned(lg2(cache_size)-1 downto 0); + signal data_ram_dout : word_t; + signal data_reg : word_t; + signal be_reg : unsigned(3 downto 0); + signal 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 data_ram_we : unsigned(3 downto 0); + signal single_read_reg : word_t; + + 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 write_hit : std_logic; + signal instant_raw : std_logic; + signal ram_write_en : std_logic; + signal fill_word_index : unsigned(word_index_width-1 downto 0); + signal req_word_index : unsigned(word_index_width-1 downto 0); + signal hit_cache_index : unsigned(cache_index_width-1 downto 0); + + signal write_fifo_din : unsigned(67 downto 0); + signal write_fifo_dout : unsigned(67 downto 0); + signal write_fifo_re : std_logic; + signal write_fifo_we : std_logic; + signal write_fifo_full : std_logic; + signal write_fifo_empty : std_logic; + signal write_data_avail : std_logic; + signal read_write_buffer : std_logic; + + alias write_fifo_addr_in is write_fifo_din(31 downto 0); + alias write_fifo_data_in is write_fifo_din(63 downto 32); + alias write_fifo_sel_in is write_fifo_din(67 downto 64); + alias write_fifo_addr_out is write_fifo_dout(31 downto 0); + alias write_fifo_data_out is write_fifo_dout(63 downto 32); + alias write_fifo_sel_out is write_fifo_dout(67 downto 64); + + signal write_through_en : std_logic; + signal tlb_exc_miss : std_logic; + signal tlb_exc_read : std_logic; + signal tlb_exc_write : std_logic; + signal tlb_exc_dirty : std_logic; + signal tlb_exc : std_logic; + signal tlb_exc_type : unsigned(1 downto 0); + + signal tlb_result_no_translate : tlb_query_out_t; + signal tlb_result : tlb_query_out_t; + +begin + + cache_index_inv <= ctrl_in.inv_addr(cache_index_width+word_index_width+1 downto word_index_width+2); + tag_inv <= ctrl_in.inv_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + bus_din_rdy <= bus_din_vld; + bus_cmd_out.addr <= write_fifo_addr_out when read_write_buffer = '1' else to_tag(request_pa) & to_cache_index(request_pa) & req_word_index & "00"; + bus_cmd_out.rw <= read_write_buffer; + bus_cmd_out.sel <= write_fifo_sel_out when read_write_buffer = '1' else (others => '1'); + bus_dout <= write_fifo_data_out; + bus_dout_vld <= read_write_buffer and write_data_avail; + + tlb_query_out.vaddr <= request_va; + tlb_result_no_translate.paddr <= request_va; + tlb_result_no_translate.hit_idx <= (others => '0'); + tlb_result_no_translate.hit <= '1'; + tlb_result_no_translate.flags.N <= '0' when CACHE_ALL = true else '1' when USE_CACHE = false else '1' when request_va(31 downto 29) = "101" else '0'; + tlb_result_no_translate.flags.D <= '0'; + tlb_result_no_translate.flags.V <= '1'; + + tlb_result <= tlb_query_in when USE_TLB else tlb_result_no_translate; + request_pa <= tlb_result.paddr; + + tlb_exc_miss <= not tlb_result.hit; + tlb_exc_read <= tlb_exc_miss and not we_reg; + tlb_exc_write <= tlb_exc_miss and we_reg; + tlb_exc_dirty <= tlb_result.hit and tlb_result.flags.D and we_reg; + tlb_exc <= tlb_exc_read or tlb_exc_write or tlb_exc_dirty; + + tlb_exc_type <= "10" when tlb_exc_read = '1' else "01" when tlb_exc_write = '1' else "11" when tlb_exc_dirty = '1' else "00"; + + write_hit <= we_reg and cache_hit and not tlb_exc; + +-- Instantiate synchronous FIFO +inst_write_fifo: entity work.fifo_sync + GENERIC MAP + ( + addr_width => lg2(write_fifo_size), + data_width => 68, + do_last_read_update => true + ) + PORT MAP + ( + rst => rst, + clk => clk, + we => write_fifo_we, + re => write_fifo_re, + fifo_full => write_fifo_full, + fifo_empty => write_fifo_empty, + fifo_afull => open, + fifo_aempty => open, + data_w => write_fifo_din, + data_r => write_fifo_dout + ); + + write_fifo_data_in <= data_reg; + write_fifo_addr_in <= request_pa; + write_fifo_sel_in <= be_reg; + write_fifo_re <= read_write_buffer and bus_dout_rdy and bus_cmd_rdy; + write_fifo_we <= write_through_en; + write_data_avail <= not write_fifo_empty; + +fill_address_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' and en = '1' then + request_va <= addr; + fill_word_index <= to_word_index(addr); + elsif bus_din_vld = '1' and fill_count_en = '1' then + fill_word_index <= fill_word_index + 1; + end if; + end if; + end process; + +request_address_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' and en = '1' then + req_word_index <= to_word_index(addr); + elsif request_count_en = '1' and bus_cmd_rdy = '1' then + req_word_index <= req_word_index + 1; + end if; + end if; + end process; + +single_read_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + single_read_reg_vld <= '0'; + elsif bus_din_vld = '1' then + single_read_reg <= bus_din; + single_read_reg_vld <= tlb_result.flags.N; + elsif cache_rdy = '1' then + single_read_reg_vld <= '0'; + end if; + end if; + end process; + +request_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' then + cache_req_rd <= en and not we; + cache_req_wr <= en and we; + end if; + end if; + end process; + +request_register2: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + we_reg <= '0'; + elsif cache_rdy = '1' then + data_reg <= din; + be_reg <= be; + we_reg <= we; + if we = '1' then + hit_cache_index <= to_cache_index(addr); + end if; + end if; + end if; + end process; + +instant_raw_logic: + process(clk) + begin + if rising_edge(clk) then + instant_raw <= '0'; + if to_word_index(addr) = fill_word_index and to_cache_index(addr) = hit_cache_index then + instant_raw <= write_hit and en and not 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, + clk_b => clk, + 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, + clk_b => clk, + en_a => '1', + en_b => '1', + we_a => data_ram_we(i), + we_b => ctrl_data_ram_we(i), + addr_a => ctrl_data_ram_addr, + addr_b => data_ram_addr, + din_a => data_reg(8*(i+1)-1 downto 8*i), + din_b => bus_din(8*(i+1)-1 downto 8*i), + dout_a => open, + dout_b => data_ram_dout(8*(i+1)-1 downto 8*i) + ); + end generate; + + + +cache_invalidate_request: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' or ctrl_in.inv_all = '1' or ctrl_in.inv_at = '1' then + invalidate_req <= '1'; + invalidate_all <= ctrl_in.inv_all or rst; + 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) + begin + if rising_edge(clk) then + if rst = '1' then + cache_ctrl_state <= init; + cache_ctrl2_state <= init; + else + cache_ctrl_state <= cache_ctrl_state_next; + cache_ctrl2_state <= cache_ctrl2_state_next; + end if; + end if; + end process; + + cache_rdy <= read_rdy and not write_fifo_full and not instant_raw; + rdy <= cache_rdy; + + dout <= single_read_reg when single_read_reg_vld = '1' else data_ram_dout; + + tag_match <= '1' when to_tag(request_pa) = 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 to_cache_index(request_va); + tag_ram_addr_rd <= to_cache_index(addr) when (was_miss = '0' and instant_raw = '0') else to_cache_index(request_va); + + cache_entry_out <= to_dcache_entry(tag_ram_dout); + cache_entry_out_inv <= to_dcache_entry(tag_ram_dout_inv); + + data_ram_addr <= (to_cache_index(addr) & to_word_index(addr)) when (was_miss = '0' and instant_raw = '0' and fill_count_en = '0') else (to_cache_index(request_va) & fill_word_index); + ctrl_data_ram_addr <= to_cache_index(request_va) & fill_word_index; + ctrl_data_ram_we <= (others => ram_write_en and bus_din_vld); + data_ram_we <= be_reg when (write_hit = '1') else (others => '0'); + + write_through_en <= cache_req_wr and tlb_result.hit and not tlb_result.flags.D; + +cache_ctrl2_state_fsm: + process(cache_ctrl2_state, tlb_result, cache_req_rd, cache_hit, cache_refill_bsy) + begin + + read_rdy <= '0'; + cache_refill_en <= '0'; + cache_ctrl2_state_next <= cache_ctrl2_state; + + case cache_ctrl2_state is + + when init => + read_rdy <= '1'; + cache_ctrl2_state_next <= ready; + + when ready => + read_rdy <= '1'; + if cache_req_rd = '1' then + if tlb_result.hit = '1' then + if tlb_result.flags.N = '1' then + read_rdy <= '0'; + cache_ctrl2_state_next <= refill_pre; + elsif cache_hit = '0' then + read_rdy <= '0'; + cache_ctrl2_state_next <= refill_pre; + end if; + end if; + end if; + + when refill_pre => + cache_refill_en <= '1'; + if cache_refill_bsy = '1' then + cache_ctrl2_state_next <= refill; + end if; + + when refill => + if cache_refill_bsy = '0' then + cache_ctrl2_state_next <= refill_post; + end if; + + when refill_post => + read_rdy <= '1'; + cache_ctrl2_state_next <= ready; + + when read_nofill => + read_rdy <= '1'; + cache_ctrl2_state_next <= ready; + + when others => + cache_ctrl2_state_next <= ready; + + end case; + + end process; + +cache_ctrl_state_fsm: + process(cache_ctrl_state, cache_refill_en, tlb_result, flush_count_rdy, fill_count_rdy, request_count_rdy, request_pa, bus_cmd_rdy, bus_din_vld, invalidate_req, invalidate_all, write_data_avail) + begin + cache_refill_bsy <= '0'; + tag_ram_we <= '0'; + flush_count_en <= '0'; + flush_count_rst <= '0'; + invalidate_en <= '0'; + request_count_en <= '0'; + fill_count_en <= '0'; + ram_write_en <= '0'; + bus_cmd_cycle_en <= '0'; + bus_cmd_we <= '0'; + was_miss <= '0'; + invalidate_ack <= '0'; + read_write_buffer <= '0'; + + cache_entry_in.tv_p <= (others => '0'); + cache_entry_in.tag <= to_tag(request_pa); + cache_entry_in.valid <= '0'; + cache_ctrl_state_next <= cache_ctrl_state; + + case cache_ctrl_state is + + when init => + cache_ctrl_state_next <= ready; + + when ready => + if invalidate_req = '1' then + cache_ctrl_state_next <= invalidate; + invalidate_en <= '1'; + elsif write_data_avail = '1' then + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + read_write_buffer <= '1'; + bus_cmd_we <= '1'; + end if; + elsif cache_refill_en = '1' then + cache_ctrl_state_next <= bus_request; + bus_cmd_cycle_en <= '1'; + end if; + + when invalidate => + cache_ctrl_state_next <= rd_cache; + invalidate_en <= '1'; + invalidate_ack <= '1'; + if invalidate_all = '1' then + cache_ctrl_state_next <= 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'); + cache_ctrl_state_next <= 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'; + cache_ctrl_state_next <= rd_cache; + end if; + + when bus_request => + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + cache_ctrl_state_next <= mem_read; + if write_data_avail = '1' then + cache_ctrl_state_next <= mem_write_through; + elsif tlb_result.flags.N = '1' then + cache_refill_bsy <= '1'; + cache_ctrl_state_next <= mem_read_single; + end if; + end if; + when mem_write_through => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + read_write_buffer <= '1'; + if write_data_avail = '0' then + read_write_buffer <= '0'; + bus_cmd_we <= '0'; + cache_ctrl_state_next <= ready; + end if; + + when mem_read => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_refill_bsy <= '1'; + request_count_en <= '1'; + ram_write_en <= '1'; + fill_count_en <= '1'; + if request_count_rdy = '1' then + bus_cmd_we <= '0'; + cache_ctrl_state_next <= mem_read_data; + end if; + + when mem_read_single => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_refill_bsy <= '1'; + cache_ctrl_state_next <= mem_read_data_single; + + when mem_read_data => + bus_cmd_cycle_en <= '1'; + cache_refill_bsy <= '1'; + fill_count_en <= '1'; + ram_write_en <= '1'; + if fill_count_rdy = '1' then + tag_ram_we <= '1'; + cache_entry_in.valid <= '1'; + cache_ctrl_state_next <= rd_cache; + end if; + + when mem_read_data_single => + bus_cmd_cycle_en <= '1'; + cache_refill_bsy <= '1'; + if bus_din_vld = '1' then + cache_ctrl_state_next <= ready; + end if; + + when rd_cache => + was_miss <= '1'; + cache_ctrl_state_next <= ready; + + when others => + cache_ctrl_state_next <= ready; + end case; + + end process; + +flush_counter: + process(clk) + begin + if rising_edge(clk) then + if ctrl_in.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) + begin + if rising_edge(clk) then + if request_count_en = '0' then + request_count_rdy <= '0'; + request_count <= 2**word_index_width-1; + else + if bus_cmd_rdy = '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) + begin + if rising_edge(clk) then + if fill_count_en = '0' then + fill_count_rdy <= '0'; + fill_count <= 2**word_index_width-1; + else + if bus_din_vld = '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; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_20130730.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_20130730.vhd new file mode 100644 index 0000000..49141e1 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_20130730.vhd @@ -0,0 +1,696 @@ +-------------------------------------------------------------------------- +-- 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_util_pkg.all; +use work.mips_types.all; +use work.busmaster_types.all; + +ENTITY dcache IS + Generic + ( + cache_size : natural := 2048; -- words + line_size : natural := 8; -- words + write_fifo_size : natural := 4; + CACHE_ALL : boolean := false; + USE_CACHE : boolean := true; + USE_TLB : boolean := false + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + + en : in STD_LOGIC; + we : in STD_LOGIC; + be : in unsigned(3 downto 0); + addr : in word_t; + din : in word_t; + dout : out word_t; + rdy : out STD_LOGIC; + + -- Cache control + ctrl_in : in cache_ctrl_t; + + tlb_query_out : out tlb_query_in_t; + tlb_query_in : in tlb_query_out_t; + + -- busmaster data + bus_din : in word_t; + bus_din_rdy : out std_logic; + bus_din_vld : in std_logic; + + bus_dout : out word_t; + bus_dout_vld : out std_logic; + bus_dout_rdy : in std_logic; + + -- busmaster command + bus_cmd_rdy : in std_logic; + bus_cmd_we : out std_logic; + bus_cmd_cycle_en : out std_logic; + bus_cmd_out : out bm_cmd_t; + bus_cyc_complete : in 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; + + function to_word_index(x : word_t) return unsigned is + variable result : unsigned(word_index_width-1 downto 0); + begin + result := x(word_index_width+1 downto 2); + + return result; + + end to_word_index; + + + function to_cache_index(x : word_t) return unsigned is + variable result : unsigned(cache_index_width-1 downto 0); + begin + result := x(cache_index_width+word_index_width+1 downto word_index_width+2); + + return result; + + end to_cache_index; + + function to_tag(x : word_t) return unsigned is + variable result : unsigned(tag_width-1 downto 0); + begin + result := x(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + return result; + + end to_tag; + + + type cache_ctrl2_state_t is (init, ready, refill_pre, refill, refill_post, read_nofill); + signal cache_ctrl2_state, cache_ctrl2_state_next : cache_ctrl2_state_t; + + type cache_ctrl_state_t is (init, ready, invalidate, flush, bus_request, mem_read, mem_read_single, mem_read_data, mem_read_data_single, rd_cache, upd_cache, single_valid); + signal cache_ctrl_state, cache_ctrl_state_next : cache_ctrl_state_t; + + signal cache_req_rd : std_logic; + signal cache_req_wr : std_logic; + signal single_read_reg_vld : std_logic; + signal cache_rdy : std_logic; + signal cache_hit : std_logic; + signal read_rdy : std_logic; + signal tag_match : std_logic; + signal cache_hit_inv : std_logic; + signal tag_match_inv : std_logic; + signal request_va : word_t; + signal request_pa : word_t; + + 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 data_ram_addr : unsigned(lg2(cache_size)-1 downto 0); + signal data_ram_dout : word_t; + signal data_reg : word_t; + signal be_reg : unsigned(3 downto 0); + signal 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 data_ram_we : unsigned(3 downto 0); + signal single_read_reg : word_t; + + 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 write_hit : std_logic; + signal instant_raw : std_logic; + signal ram_write_en : std_logic; + signal fill_word_index : unsigned(word_index_width-1 downto 0); + signal req_word_index : unsigned(word_index_width-1 downto 0); + signal hit_cache_index : unsigned(cache_index_width-1 downto 0); + + signal write_fifo_din : unsigned(67 downto 0); + signal write_fifo_dout : unsigned(67 downto 0); + signal write_fifo_re : std_logic; + signal write_fifo_we : std_logic; + signal write_fifo_full : std_logic; + signal write_fifo_empty : std_logic; + signal write_data_avail : std_logic; + signal read_write_buffer : std_logic; + + alias write_fifo_addr_in is write_fifo_din(31 downto 0); + alias write_fifo_data_in is write_fifo_din(63 downto 32); + alias write_fifo_sel_in is write_fifo_din(67 downto 64); + alias write_fifo_addr_out is write_fifo_dout(31 downto 0); + alias write_fifo_data_out is write_fifo_dout(63 downto 32); + alias write_fifo_sel_out is write_fifo_dout(67 downto 64); + + signal write_through_en : std_logic; + signal tlb_exc_miss : std_logic; + signal tlb_exc_read : std_logic; + signal tlb_exc_write : std_logic; + signal tlb_exc_dirty : std_logic; + signal tlb_exc : std_logic; + signal tlb_exc_type : unsigned(1 downto 0); + + signal tlb_result_no_translate : tlb_query_out_t; + signal tlb_result : tlb_query_out_t; + +begin + + cache_index_inv <= ctrl_in.inv_addr(cache_index_width+word_index_width+1 downto word_index_width+2); + tag_inv <= ctrl_in.inv_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + bus_din_rdy <= bus_din_vld; + bus_cmd_out.addr <= write_fifo_addr_out when read_write_buffer = '1' else to_tag(request_pa) & to_cache_index(request_pa) & req_word_index & "00"; + bus_cmd_out.rw <= read_write_buffer; + bus_cmd_out.sel <= write_fifo_sel_out when read_write_buffer = '1' else (others => '1'); + bus_dout <= write_fifo_data_out; + bus_dout_vld <= read_write_buffer and write_data_avail; + + tlb_query_out.vaddr <= request_va; + tlb_result_no_translate.paddr <= request_va; + tlb_result_no_translate.hit_idx <= (others => '0'); + tlb_result_no_translate.hit <= '1'; + tlb_result_no_translate.flags.N <= '0' when CACHE_ALL = true else '1' when USE_CACHE = false else '1' when request_va(31 downto 29) = "101" else '0'; + tlb_result_no_translate.flags.D <= '0'; + tlb_result_no_translate.flags.V <= '1'; + + tlb_result <= tlb_query_in when USE_TLB else tlb_result_no_translate; + request_pa <= tlb_result.paddr; + + tlb_exc_miss <= not tlb_result.hit; + tlb_exc_read <= tlb_exc_miss and not we_reg; + tlb_exc_write <= tlb_exc_miss and we_reg; + tlb_exc_dirty <= tlb_result.hit and tlb_result.flags.D and we_reg; + tlb_exc <= tlb_exc_read or tlb_exc_write or tlb_exc_dirty; + + tlb_exc_type <= "10" when tlb_exc_read = '1' else "01" when tlb_exc_write = '1' else "11" when tlb_exc_dirty = '1' else "00"; + + write_hit <= we_reg and cache_hit and not tlb_exc; + +-- Instantiate synchronous FIFO +inst_write_fifo: entity work.fifo_sync + GENERIC MAP + ( + addr_width => lg2(write_fifo_size), + data_width => 68, + do_last_read_update => true + ) + PORT MAP + ( + rst => rst, + clk => clk, + we => write_fifo_we, + re => write_fifo_re, + fifo_full => write_fifo_full, + fifo_empty => write_fifo_empty, + fifo_afull => open, + fifo_aempty => open, + data_w => write_fifo_din, + data_r => write_fifo_dout + ); + + write_fifo_data_in <= data_reg; + write_fifo_addr_in <= request_pa; + write_fifo_sel_in <= be_reg; + write_fifo_re <= read_write_buffer and bus_dout_rdy and bus_cmd_rdy; + write_fifo_we <= write_through_en; + write_data_avail <= not write_fifo_empty; + +fill_address_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' and en = '1' then + request_va <= addr; + fill_word_index <= to_word_index(addr); + elsif bus_din_vld = '1' and fill_count_en = '1' then + fill_word_index <= fill_word_index + 1; + end if; + end if; + end process; + +request_address_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' and en = '1' then + req_word_index <= to_word_index(addr); + elsif request_count_en = '1' and bus_cmd_rdy = '1' then + req_word_index <= req_word_index + 1; + end if; + end if; + end process; + +single_read_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then +-- single_read_reg_vld <= '0'; + elsif bus_din_vld = '1' then + single_read_reg <= bus_din; +-- single_read_reg_vld <= tlb_result.flags.N; + elsif cache_rdy = '1' then +-- single_read_reg_vld <= '0'; + end if; + end if; + end process; + +request_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' then + cache_req_rd <= en and not we; + cache_req_wr <= en and we; + end if; + end if; + end process; + +request_register2: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + we_reg <= '0'; + elsif cache_rdy = '1' then + data_reg <= din; + be_reg <= be; + we_reg <= we; + if we = '1' then + hit_cache_index <= to_cache_index(addr); + end if; + end if; + end if; + end process; + +instant_raw_logic: + process(clk) + begin + if rising_edge(clk) then + instant_raw <= '0'; + if to_word_index(addr) = fill_word_index and to_cache_index(addr) = hit_cache_index then + instant_raw <= write_hit and en and not 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, + clk_b => clk, + 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, + clk_b => clk, + en_a => '1', + en_b => '1', + we_a => data_ram_we(i), + we_b => ctrl_data_ram_we(i), + addr_a => ctrl_data_ram_addr, + addr_b => data_ram_addr, + din_a => data_reg(8*(i+1)-1 downto 8*i), + din_b => bus_din(8*(i+1)-1 downto 8*i), + dout_a => open, + dout_b => data_ram_dout(8*(i+1)-1 downto 8*i) + ); + end generate; + + + +cache_invalidate_request: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' or ctrl_in.inv_all = '1' or ctrl_in.inv_at = '1' then + invalidate_req <= '1'; + invalidate_all <= ctrl_in.inv_all or rst; + 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) + begin + if rising_edge(clk) then + if rst = '1' then + cache_ctrl_state <= init; + cache_ctrl2_state <= init; + else + cache_ctrl_state <= cache_ctrl_state_next; + cache_ctrl2_state <= cache_ctrl2_state_next; + end if; + end if; + end process; + + cache_rdy <= read_rdy and not write_fifo_full and not instant_raw; + rdy <= cache_rdy; + + dout <= single_read_reg when single_read_reg_vld = '1' else data_ram_dout; + + tag_match <= '1' when to_tag(request_pa) = 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 to_cache_index(request_va); + tag_ram_addr_rd <= to_cache_index(addr) when (was_miss = '0' and instant_raw = '0') else to_cache_index(request_va); + + cache_entry_out <= to_dcache_entry(tag_ram_dout); + cache_entry_out_inv <= to_dcache_entry(tag_ram_dout_inv); + + data_ram_addr <= (to_cache_index(addr) & to_word_index(addr)) when (was_miss = '0' and instant_raw = '0' and fill_count_en = '0') else (to_cache_index(request_va) & fill_word_index); + ctrl_data_ram_addr <= to_cache_index(request_va) & fill_word_index; + ctrl_data_ram_we <= (others => ram_write_en and bus_din_vld); + data_ram_we <= be_reg when (write_hit = '1') else (others => '0'); + + write_through_en <= cache_req_wr and tlb_result.hit and not tlb_result.flags.D; + +cache_ctrl_state_fsm: + process(cache_ctrl_state, cache_req_rd, cache_hit, single_read_reg_vld, tlb_result, flush_count_rdy, fill_count_rdy, request_count_rdy, request_pa, bus_cmd_rdy, bus_din_vld, invalidate_req, invalidate_all, write_data_avail) + begin + read_rdy <= '0'; + tag_ram_we <= '0'; + flush_count_en <= '0'; + flush_count_rst <= '0'; + invalidate_en <= '0'; + request_count_en <= '0'; + fill_count_en <= '0'; + ram_write_en <= '0'; + bus_cmd_cycle_en <= '0'; + bus_cmd_we <= '0'; + was_miss <= '0'; + invalidate_ack <= '0'; + single_read_reg_vld <= '0'; + read_write_buffer <= '0'; + + cache_entry_in.tv_p <= (others => '0'); + cache_entry_in.tag <= to_tag(request_pa); + cache_entry_in.valid <= '0'; + cache_ctrl_state_next <= cache_ctrl_state; + + case cache_ctrl_state is + + when init => + cache_ctrl_state_next <= ready; + read_rdy <= '1'; + + when ready => + read_rdy <= (tlb_result.hit and (not tlb_result.flags.N or cache_hit)) or not cache_req_rd; + if invalidate_req = '1' then + cache_ctrl_state_next <= invalidate; + invalidate_en <= '1'; + elsif write_data_avail = '1' then + cache_ctrl_state_next <= ready; + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + read_write_buffer <= '1'; + bus_cmd_we <= '1'; + end if; + elsif cache_req_rd = '1' then + if tlb_result.hit = '1' then + if tlb_result.flags.N = '1' or cache_hit = '0' then + read_rdy <= '0'; + cache_ctrl_state_next <= bus_request; + bus_cmd_cycle_en <= '1'; + end if; + end if; + end if; + + when invalidate => + cache_ctrl_state_next <= rd_cache; + invalidate_en <= '1'; + invalidate_ack <= '1'; + if invalidate_all = '1' then + cache_ctrl_state_next <= 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'); + cache_ctrl_state_next <= 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'; + cache_ctrl_state_next <= rd_cache; + end if; + + when bus_request => + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + cache_ctrl_state_next <= mem_read; + if tlb_result.flags.N = '1' then + cache_ctrl_state_next <= mem_read_single; + end if; + end if; + + when mem_read => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + request_count_en <= '1'; + ram_write_en <= '1'; + fill_count_en <= '1'; + if request_count_rdy = '1' then + bus_cmd_we <= '0'; + cache_ctrl_state_next <= mem_read_data; + end if; + + when mem_read_single => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_ctrl_state_next <= mem_read_data_single; + + when mem_read_data => + bus_cmd_cycle_en <= '1'; + fill_count_en <= '1'; + ram_write_en <= '1'; + if fill_count_rdy = '1' then + tag_ram_we <= '1'; + cache_entry_in.valid <= '1'; + cache_ctrl_state_next <= rd_cache; + end if; + + when mem_read_data_single => + bus_cmd_cycle_en <= '1'; + if bus_din_vld = '1' then + cache_ctrl_state_next <= single_valid; + end if; + + when single_valid => + read_rdy <= '1'; + single_read_reg_vld <= '1'; + cache_ctrl_state_next <= ready; + + when rd_cache => + was_miss <= '1'; + cache_ctrl_state_next <= ready; + + when others => + cache_ctrl_state_next <= ready; + end case; + + end process; + +flush_counter: + process(clk) + begin + if rising_edge(clk) then + if ctrl_in.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) + begin + if rising_edge(clk) then + if request_count_en = '0' then + request_count_rdy <= '0'; + request_count <= 2**word_index_width-1; + else + if bus_cmd_rdy = '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) + begin + if rising_edge(clk) then + if fill_count_en = '0' then + fill_count_rdy <= '0'; + fill_count <= 2**word_index_width-1; + else + if bus_din_vld = '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; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_new.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_new.vhd new file mode 100644 index 0000000..58e074b --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_dcache_new.vhd @@ -0,0 +1,782 @@ +-------------------------------------------------------------------------- +-- 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_util_pkg.all; +use work.mips_types.all; +use work.busmaster_types.all; + +ENTITY dcache IS + Generic + ( + CACHE_SIZE : natural := 1024; -- words + LINE_SIZE : natural := 8; -- words + WRITE_FIFO_SIZE : natural := 4; + CACHE_ALL : boolean := false; + USE_CACHE : boolean := true; + USE_TLB : boolean := false + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + + en : in STD_LOGIC; + we : in STD_LOGIC; + be : in unsigned(3 downto 0); + addr : in word_t; + din : in word_t; + dout : out word_t; + rdy : out STD_LOGIC; + + -- Cache control + ctrl_in : in cache_ctrl_t; + + tlb_query_out : out tlb_query_in_t; + tlb_query_in : in tlb_query_out_t; + + -- busmaster data + bus_din : in word_t; + bus_din_rdy : out std_logic; + bus_din_vld : in std_logic; + + bus_dout : out word_t; + bus_dout_vld : out std_logic; + bus_dout_rdy : in std_logic; + + -- busmaster command + bus_cmd_rdy : in std_logic; + bus_cmd_we : out std_logic; + bus_cmd_cycle_en : out std_logic; + bus_cmd_out : out bm_cmd_t; + bus_cyc_complete : in 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; + + function to_word_index(x : word_t) return unsigned is + variable result : unsigned(word_index_width-1 downto 0); + begin + result := x(word_index_width+1 downto 2); + + return result; + + end to_word_index; + + + function to_cache_index(x : word_t) return unsigned is + variable result : unsigned(cache_index_width-1 downto 0); + begin + result := x(cache_index_width+word_index_width+1 downto word_index_width+2); + + return result; + + end to_cache_index; + + function to_tag(x : word_t) return unsigned is + variable result : unsigned(tag_width-1 downto 0); + begin + result := x(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + return result; + + end to_tag; + + + type cache_busy_state_t is (init, ready, busy); + signal cache_busy_state, cache_busy_state_next : cache_busy_state_t; + + type cache_ctrl_state_t is (init, ready, invalidate, invalidate_post, flush, bus_request, mem_read, mem_read_single, mem_read_data, mem_read_data_single, rd_cache, upd_cache); + signal cache_ctrl_state, cache_ctrl_state_next : cache_ctrl_state_t; + + type debug_t is record + ready : std_logic; + req_strobe : std_logic; + req_read : std_logic; + req_write : std_logic; + req_va : word_t; + req_pa : word_t; + dout_reg : word_t; + din_reg : word_t; + end record; + + signal debug : debug_t; + + signal req_strobe : std_logic; + signal cache_req_rd : std_logic; + signal cache_req_wr : std_logic; + signal cache_rdy : std_logic; + signal cache_hit : std_logic; + signal read_rdy : std_logic; + signal write_rdy : std_logic; + signal request_va : word_t; + signal request_pa : word_t; + + signal tag_match : std_logic; + signal cache_hit_inv : std_logic; + signal tag_match_inv : std_logic; + + signal mem_fetch_req : std_logic; + signal mem_fetch_ack : std_logic; + signal single_read_en : std_logic; + signal single_read_set : std_logic; + signal single_read_reg_vld : std_logic; + + 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 data_ram_addr : unsigned(lg2(CACHE_SIZE)-1 downto 0); + signal data_ram_dout : word_t; + signal din_reg : word_t; + signal be_reg : unsigned(3 downto 0); + signal ctrl_data_ram_addr : unsigned(lg2(CACHE_SIZE)-1 downto 0); + signal ctrl_data_ram_we : unsigned(3 downto 0); + signal data_ram_we : unsigned(3 downto 0); + signal single_read_reg : word_t; + signal cache_dout : word_t; + + 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 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; + signal write_hit : std_logic; + signal instant_raw : std_logic; + signal ram_write_en : std_logic; + signal fill_word_index : unsigned(word_index_width-1 downto 0); + signal req_word_index : unsigned(word_index_width-1 downto 0); + signal hit_cache_index : unsigned(cache_index_width-1 downto 0); + + signal write_fifo_din : unsigned(67 downto 0); + signal write_fifo_dout : unsigned(67 downto 0); + signal write_fifo_re : std_logic; + signal write_fifo_we : std_logic; + signal write_fifo_full : std_logic; + signal write_fifo_empty : std_logic; + signal write_data_avail : std_logic; + signal read_write_buffer : std_logic; + + alias write_fifo_addr_in is write_fifo_din(31 downto 0); + alias write_fifo_data_in is write_fifo_din(63 downto 32); + alias write_fifo_sel_in is write_fifo_din(67 downto 64); + alias write_fifo_addr_out is write_fifo_dout(31 downto 0); + alias write_fifo_data_out is write_fifo_dout(63 downto 32); + alias write_fifo_sel_out is write_fifo_dout(67 downto 64); + + signal write_through_en : std_logic; + signal tlb_exc_miss : std_logic; + signal tlb_exc_read : std_logic; + signal tlb_exc_write : std_logic; + signal tlb_exc_dirty : std_logic; + signal tlb_exc : std_logic; + signal tlb_exc_type : unsigned(1 downto 0); + + signal tlb_result_no_translate : tlb_query_out_t; + signal tlb_result : tlb_query_out_t; + +begin + + cache_index_inv <= ctrl_in.inv_addr(cache_index_width+word_index_width+1 downto word_index_width+2); + tag_inv <= ctrl_in.inv_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + bus_din_rdy <= fill_count_en or single_read_en; + bus_cmd_out.addr <= write_fifo_addr_out when read_write_buffer = '1' else to_tag(request_pa) & to_cache_index(request_pa) & req_word_index & "00"; + bus_cmd_out.rw <= read_write_buffer; + bus_cmd_out.sel <= write_fifo_sel_out when read_write_buffer = '1' else (others => '1'); + bus_dout <= write_fifo_data_out; + bus_dout_vld <= read_write_buffer and write_data_avail; + + tlb_query_out.vaddr <= request_va; + tlb_result_no_translate.paddr <= request_va; + tlb_result_no_translate.hit_idx <= (others => '0'); + tlb_result_no_translate.hit <= '1'; + tlb_result_no_translate.flags.N <= '0' when CACHE_ALL = true else '1' when USE_CACHE = false else '1' when request_va(31 downto 29) = "101" else '0'; + tlb_result_no_translate.flags.D <= '0'; + tlb_result_no_translate.flags.V <= '1'; + + tlb_result <= tlb_query_in when USE_TLB else tlb_result_no_translate; + request_pa <= tlb_result.paddr; + + tlb_exc_miss <= not tlb_result.hit; + tlb_exc_read <= tlb_exc_miss and not cache_req_wr; + tlb_exc_write <= tlb_exc_miss and cache_req_wr; + tlb_exc_dirty <= tlb_result.hit and tlb_result.flags.D and cache_req_wr; + tlb_exc <= tlb_exc_read or tlb_exc_write or tlb_exc_dirty; + + tlb_exc_type <= "10" when tlb_exc_read = '1' else "01" when tlb_exc_write = '1' else "11" when tlb_exc_dirty = '1' else "00"; + + write_hit <= cache_req_wr and cache_hit and not tlb_exc; + + debug.req_strobe <= req_strobe; + debug.req_read <= cache_req_rd; + debug.req_write <= cache_req_wr; + debug.req_va <= request_va; + debug.req_pa <= request_pa; + debug.din_reg <= din_reg; + debug.dout_reg <= cache_dout; + debug.ready <= cache_rdy; + +------------------------------------------------- +-- ctrl +------------------------------------------------- +req_strobe <= cache_rdy and en; + +request_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + cache_req_rd <= '0'; + cache_req_wr <= '0'; + elsif cache_rdy = '1' then + cache_req_rd <= en and not we; + cache_req_wr <= en and we; + if we = '1' then + hit_cache_index <= to_cache_index(addr); + end if; + end if; + end if; + end process; + +request_address_register: + process(clk) + begin + if rising_edge(clk) then + if req_strobe = '1' then + din_reg <= din; + be_reg <= be; + request_va <= addr; + end if; + end if; + end process; + +instant_raw_logic: + process(clk) + begin + if rising_edge(clk) then + instant_raw <= '0'; + if to_word_index(addr) = fill_word_index and to_cache_index(addr) = hit_cache_index then + instant_raw <= write_hit and en and not we; + end if; + end if; + end process; + +------------------------------------------------- +-- Instantiate synchronous FIFO +inst_write_fifo: entity work.fifo_sync + GENERIC MAP + ( + addr_width => lg2(WRITE_FIFO_SIZE), + data_width => 68, + do_last_read_update => true + ) + PORT MAP + ( + rst => rst, + clk => clk, + we => write_fifo_we, + re => write_fifo_re, + fifo_full => write_fifo_full, + fifo_empty => write_fifo_empty, + fifo_afull => open, + fifo_aempty => open, + data_w => write_fifo_din, + data_r => write_fifo_dout + ); + + write_fifo_data_in <= din_reg; + write_fifo_addr_in <= request_pa; + write_fifo_sel_in <= be_reg; + write_fifo_re <= read_write_buffer and bus_dout_rdy and bus_cmd_rdy; + write_fifo_we <= write_through_en; + write_data_avail <= not write_fifo_empty; + +fill_address_register: + process(clk) + begin + if rising_edge(clk) then + if req_strobe = '1' then + fill_word_index <= to_word_index(addr); + elsif bus_din_vld = '1' and fill_count_en = '1' then + fill_word_index <= fill_word_index + 1; + end if; + end if; + end process; + +bus_request_address_register: + process(clk) + begin + if rising_edge(clk) then + if req_strobe = '1' then + req_word_index <= to_word_index(addr); + elsif request_count_en = '1' and bus_cmd_rdy = '1' then + req_word_index <= req_word_index + 1; + end if; + end if; + end process; + +single_read_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + single_read_en <= '0'; + single_read_reg_vld <= '0'; + elsif single_read_en = '0' then + if single_read_set = '1' then + single_read_en <= '1'; + end if; + elsif single_read_reg_vld = '1' then + if en = '1' then + single_read_en <= '0'; + single_read_reg_vld <= '0'; + end if; + elsif bus_din_vld = '1' and single_read_en = '1' then + single_read_reg <= bus_din; + single_read_reg_vld <= '1'; + 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, + clk_b => clk, + 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_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, + clk_b => clk, + en_a => '1', + en_b => ram_read_en, + we_a => data_ram_we(i), + we_b => ctrl_data_ram_we(i), + addr_a => ctrl_data_ram_addr, + addr_b => data_ram_addr, + din_a => din_reg(8*(i+1)-1 downto 8*i), + din_b => bus_din(8*(i+1)-1 downto 8*i), + dout_a => open, + dout_b => data_ram_dout(8*(i+1)-1 downto 8*i) + ); + end generate; + + + +cache_invalidate_request: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' or ctrl_in.inv_all = '1' or ctrl_in.inv_at = '1' then + invalidate_req <= '1'; + invalidate_all <= ctrl_in.inv_all or rst; + 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) + begin + if rising_edge(clk) then + if rst = '1' then + cache_ctrl_state <= init; + cache_busy_state <= init; + else + cache_ctrl_state <= cache_ctrl_state_next; + cache_busy_state <= cache_busy_state_next; + end if; + end if; + end process; + + cache_rdy <= read_rdy and not instant_raw and write_rdy; + write_rdy <= not (write_fifo_full and cache_req_wr); + rdy <= cache_rdy; + + cache_dout <= single_read_reg when single_read_en = '1' else data_ram_dout; + dout <= cache_dout; + + tag_match <= '1' when to_tag(request_pa) = 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 to_cache_index(request_va); + tag_ram_addr_rd <= to_cache_index(addr) when (was_miss = '0' and instant_raw = '0') else to_cache_index(request_va); + + cache_entry_out <= to_dcache_entry(tag_ram_dout); + cache_entry_out_inv <= to_dcache_entry(tag_ram_dout_inv); + + ram_read_en <= en or was_miss or fill_count_en; + data_ram_addr <= (to_cache_index(addr) & to_word_index(addr)) when (was_miss = '0' and instant_raw = '0' and fill_count_en = '0') else (to_cache_index(request_va) & fill_word_index); + ctrl_data_ram_addr <= to_cache_index(request_va) & fill_word_index; + ctrl_data_ram_we <= (others => ram_write_en and bus_din_vld); + data_ram_we <= be_reg when (write_hit = '1') else (others => '0'); + + write_through_en <= cache_rdy and cache_req_wr and tlb_result.hit and not tlb_result.flags.D; + +cache_busy_state_fsm: + process(cache_busy_state, tlb_result, cache_req_rd, cache_hit, mem_fetch_ack, single_read_en, single_read_reg_vld) + begin + + read_rdy <= '0'; + mem_fetch_req <= '0'; + single_read_set <= '0'; + cache_busy_state_next <= cache_busy_state; + + case cache_busy_state is + + when init => + cache_busy_state_next <= ready; + + when ready => + if single_read_en = '1' then + read_rdy <= single_read_reg_vld; + else + read_rdy <= '1'; + if cache_req_rd = '1' then + if tlb_result.hit = '1' then + if tlb_result.flags.N = '1' or cache_hit = '0' then + mem_fetch_req <= '1'; + read_rdy <= '0'; + cache_busy_state_next <= busy; + end if; + end if; + end if; + end if; + + when busy => + mem_fetch_req <= '1'; + if mem_fetch_ack = '1' then + single_read_set <= tlb_result.flags.N; + cache_busy_state_next <= ready; + end if; + + when others => + cache_busy_state_next <= ready; + + end case; + + end process; + +cache_ctrl_state_fsm: + process(cache_ctrl_state, mem_fetch_req, tlb_result, flush_count_rdy, fill_count_rdy, request_count_rdy, request_pa, bus_cmd_rdy, bus_din_vld, invalidate_req, invalidate_all, write_data_avail) + begin + tag_ram_we <= '0'; + flush_count_en <= '0'; + flush_count_rst <= '0'; + invalidate_en <= '0'; + request_count_en <= '0'; + fill_count_en <= '0'; + ram_write_en <= '0'; + bus_cmd_cycle_en <= '0'; + bus_cmd_we <= '0'; + was_miss <= '0'; + invalidate_ack <= '0'; + mem_fetch_ack <= '0'; + read_write_buffer <= '0'; + + cache_entry_in.tv_p <= (others => '0'); + cache_entry_in.tag <= to_tag(request_pa); + cache_entry_in.valid <= '0'; + cache_ctrl_state_next <= cache_ctrl_state; + + case cache_ctrl_state is + + when init => + cache_ctrl_state_next <= ready; + + when ready => + if invalidate_req = '1' then + cache_ctrl_state_next <= invalidate; + invalidate_en <= '1'; + elsif mem_fetch_req = '1' or write_data_avail = '1' then + cache_ctrl_state_next <= bus_request; + bus_cmd_cycle_en <= '1'; + end if; + + when invalidate => + cache_ctrl_state_next <= rd_cache; + invalidate_en <= '1'; + invalidate_ack <= '1'; + if invalidate_all = '1' then + cache_ctrl_state_next <= 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'); + cache_ctrl_state_next <= invalidate_post; + 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'; + cache_ctrl_state_next <= invalidate_post; + end if; + + when invalidate_post => + was_miss <= '1'; + cache_ctrl_state_next <= ready; + + when bus_request => + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + if write_data_avail = '1' then + read_write_buffer <= '1'; + bus_cmd_we <= '1'; + elsif mem_fetch_req = '1' then + if tlb_result.flags.N = '1' then + cache_ctrl_state_next <= mem_read_single; + else + cache_ctrl_state_next <= mem_read; + end if; + else + cache_ctrl_state_next <= ready; + end if; + end if; + + when mem_read => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + request_count_en <= '1'; + ram_write_en <= '1'; + fill_count_en <= '1'; + if request_count_rdy = '1' then + bus_cmd_we <= '0'; + cache_ctrl_state_next <= mem_read_data; + end if; + + when mem_read_single => + mem_fetch_ack <= '1'; + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_ctrl_state_next <= mem_read_data_single; + + when mem_read_data => + bus_cmd_cycle_en <= '1'; + fill_count_en <= '1'; + ram_write_en <= '1'; + if fill_count_rdy = '1' then + tag_ram_we <= '1'; + cache_entry_in.valid <= '1'; + cache_ctrl_state_next <= rd_cache; + end if; + + when mem_read_data_single => + bus_cmd_cycle_en <= '1'; + if bus_din_vld = '1' then + cache_ctrl_state_next <= ready; + end if; + + when rd_cache => + mem_fetch_ack <= '1'; + was_miss <= '1'; + cache_ctrl_state_next <= ready; + + when others => + cache_ctrl_state_next <= ready; + end case; + + end process; + +flush_counter: + process(clk) + begin + if rising_edge(clk) then + if ctrl_in.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) + begin + if rising_edge(clk) then + if request_count_en = '0' then + request_count_rdy <= '0'; + request_count <= 2**word_index_width-1; + else + if bus_cmd_rdy = '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) + begin + if rising_edge(clk) then + if fill_count_en = '0' then + fill_count_rdy <= '0'; + fill_count <= 2**word_index_width-1; + else + if bus_din_vld = '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; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache.vhd new file mode 100644 index 0000000..57cc03d --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache.vhd @@ -0,0 +1,583 @@ +-------------------------------------------------------------------------- +-- 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_util_pkg.all; +use work.mips_types.all; +use work.busmaster_types.all; + +ENTITY icache IS + Generic + ( + cache_size : natural := 2048; -- words + line_size : natural := 8; -- words + CACHE_ALL : boolean := true; + USE_CACHE : boolean := true; + USE_TLB : boolean := false + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + + -- CPU control/data + en : in STD_LOGIC; + addr : in word_t; + dout : out word_t; + rdy : out STD_LOGIC; + + -- Cache control + ctrl_in : in cache_ctrl_t; + + tlb_query_out : out tlb_query_in_t; + tlb_query_in : in tlb_query_out_t; + + -- busmaster data + bus_din : in word_t; + bus_din_rdy : out std_logic; + bus_din_vld : in std_logic; + + -- busmaster command + bus_cmd_rdy : in std_logic; + bus_cmd_we : out std_logic; + bus_cmd_cycle_en : out std_logic; + bus_cmd_out : out bm_cmd_t; + bus_cyc_complete : in std_logic + ); +END icache; + +ARCHITECTURE behavior OF icache IS + +COMPONENT dpram_1w1r2c_ra + 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; + 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_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 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; + + 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; + + function to_word_index(x : word_t) return unsigned is + variable result : unsigned(word_index_width-1 downto 0); + begin + result := x(word_index_width+1 downto 2); + + return result; + + end to_word_index; + + + function to_cache_index(x : word_t) return unsigned is + variable result : unsigned(cache_index_width-1 downto 0); + begin + result := x(cache_index_width+word_index_width+1 downto word_index_width+2); + + return result; + + end to_cache_index; + + function to_tag(x : word_t) return unsigned is + variable result : unsigned(tag_width-1 downto 0); + begin + result := x(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + return result; + + end to_tag; + + type cache_ctrl_state_t is (init, ready, invalidate, flush, bus_request, mem_read, mem_read_single, mem_read_data, mem_read_data_single, rd_cache, upd_cache, single_valid); + signal cache_ctrl_state, cache_ctrl_state_next : cache_ctrl_state_t; + + signal cache_req_rd : std_logic; + signal single_read_reg_vld : std_logic; + signal cache_rdy : 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_va : word_t; + signal request_pa : word_t; + + 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 : 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 single_read_reg : word_t; + + 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 ram_write_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; + + signal fill_word_index : unsigned(word_index_width-1 downto 0); + signal req_word_index : unsigned(word_index_width-1 downto 0); + + signal tlb_exc : std_logic; + signal tlb_exc_type : unsigned(1 downto 0); + + signal tlb_result_no_translate : tlb_query_out_t; + signal tlb_result : tlb_query_out_t; + +begin + + tlb_query_out.vaddr <= request_va; + tlb_result_no_translate.paddr <= request_va; + tlb_result_no_translate.hit_idx <= (others => '0'); + tlb_result_no_translate.hit <= '1'; + tlb_result_no_translate.flags.N <= '0' when CACHE_ALL = true else '1' when USE_CACHE = false else '1' when request_va(31 downto 29) = "101" else '0'; + tlb_result_no_translate.flags.D <= '0'; + tlb_result_no_translate.flags.V <= '1'; + + ram_read_en <= en or was_miss; + cache_index_inv <= ctrl_in.inv_addr(cache_index_width+word_index_width+1 downto word_index_width+2); + tag_inv <= ctrl_in.inv_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + tlb_result <= tlb_query_in when USE_TLB else tlb_result_no_translate; + request_pa <= tlb_result.paddr; + + tlb_exc <= cache_req_rd and not tlb_result.hit; + tlb_exc_type <= "10" when tlb_exc = '1' else "00"; + +fill_address_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' and en = '1' then + request_va <= addr; + fill_word_index <= to_word_index(addr); + elsif bus_din_vld = '1' and fill_count_en = '1' then + fill_word_index <= fill_word_index + 1; + end if; + end if; + end process; + +request_address_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' and en = '1' then + req_word_index <= to_word_index(addr); + elsif request_count_en = '1' and bus_cmd_rdy = '1' then + req_word_index <= req_word_index + 1; + end if; + end if; + end process; + +single_read_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then +-- single_read_reg_vld <= '0'; + elsif bus_din_vld = '1' then + single_read_reg <= bus_din; +-- single_read_reg_vld <= tlb_result.flags.N; + elsif cache_rdy = '1' then +-- single_read_reg_vld <= '0'; + end if; + end if; + end process; + +request_register: + process(clk) + begin + if rising_edge(clk) then + cache_req_rd <= en; + end if; + end process; + +cache_invalidate_request: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' or ctrl_in.inv_all = '1' or ctrl_in.inv_at = '1' then + invalidate_req <= '1'; + invalidate_all <= ctrl_in.inv_all or rst; + 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_2w2r2c_ra + GENERIC MAP + ( + addr_width => tag_ram_addr_width, + data_width => tag_ram_data_width + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + 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_1w1r2c_ra + GENERIC MAP + ( + addr_width => lg2(cache_size), + data_width => word_t'length + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + 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) + begin + if rising_edge(clk) then + if rst = '1' then + cache_ctrl_state <= init; + else + cache_ctrl_state <= cache_ctrl_state_next; + end if; + end if; + end process; + + bus_din_rdy <= bus_din_vld; + bus_cmd_out.addr <= to_tag(request_pa) & to_cache_index(request_pa) & req_word_index & "00"; + bus_cmd_out.rw <= '0'; + bus_cmd_out.sel <= (others => '1'); + + rdy <= cache_rdy; + dout <= single_read_reg when single_read_reg_vld = '1' else data_ram_data_rd; + + cache_entry_out <= to_icache_entry(tag_ram_data_rd); + tag_match <= '1' when to_tag(request_pa) = cache_entry_out.tag else '0'; + cache_hit <= 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_hit_inv <= 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 to_cache_index(request_va); + tag_ram_addr_rd <= to_cache_index(addr) when was_miss = '0' else to_cache_index(request_va); + data_ram_addr_rd <= (to_cache_index(addr) & to_word_index(addr)) when was_miss = '0' else (to_cache_index(request_va) & fill_word_index); + data_ram_addr_wr <= to_cache_index(request_va) & fill_word_index; + data_ram_data_wr <= bus_din; + data_ram_we <= ram_write_en and bus_din_vld; + +cache_ctrl_state_fsm: + process(cache_ctrl_state, cache_req_rd, tlb_result, bus_din_vld, cache_hit, cache_hit_inv, flush_count_rdy, fill_count_rdy, request_count_rdy, request_pa, bus_cmd_rdy, invalidate_req, invalidate_all) + begin + cache_rdy <= '0'; + tag_ram_we <= '0'; + flush_count_en <= '0'; + flush_count_rst <= '0'; + invalidate_en <= '0'; + request_count_en <= '0'; + fill_count_en <= '0'; + ram_write_en <= '0'; + bus_cmd_cycle_en <= '0'; + bus_cmd_we <= '0'; + was_miss <= '0'; + invalidate_ack <= '0'; + single_read_reg_vld <= '0'; + + cache_entry_in.tv_p <= (others => '0'); + cache_entry_in.tag <= to_tag(request_pa); + cache_entry_in.valid <= '0'; + cache_ctrl_state_next <= cache_ctrl_state; + + case cache_ctrl_state is + + when init => + cache_ctrl_state_next <= ready; + cache_rdy <= '1'; + + when ready => + cache_rdy <= tlb_result.hit and (not tlb_result.flags.N or cache_hit); + if invalidate_req = '1' then + cache_ctrl_state_next <= invalidate; + invalidate_en <= '1'; + elsif cache_req_rd = '1' then + if tlb_result.hit = '1' then + if tlb_result.flags.N = '1' or cache_hit = '0' then + cache_rdy <= '0'; + cache_ctrl_state_next <= bus_request; + bus_cmd_cycle_en <= '1'; + end if; + end if; + end if; + + when invalidate => + cache_ctrl_state_next <= rd_cache; + invalidate_en <= '1'; + invalidate_ack <= '1'; + if invalidate_all = '1' then + cache_ctrl_state_next <= 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'); + cache_ctrl_state_next <= 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'; + cache_ctrl_state_next <= rd_cache; + end if; + + when bus_request => + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + cache_ctrl_state_next <= mem_read; + if tlb_result.flags.N = '1' then + cache_ctrl_state_next <= mem_read_single; + end if; + end if; + + when mem_read => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + request_count_en <= '1'; + ram_write_en <= '1'; + fill_count_en <= '1'; + if request_count_rdy = '1' then + bus_cmd_we <= '0'; + cache_ctrl_state_next <= mem_read_data; + end if; + + when mem_read_single => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_ctrl_state_next <= mem_read_data_single; + + when mem_read_data => + bus_cmd_cycle_en <= '1'; + fill_count_en <= '1'; + ram_write_en <= '1'; + if fill_count_rdy = '1' then + tag_ram_we <= '1'; + cache_entry_in.valid <= '1'; + cache_ctrl_state_next <= rd_cache; + end if; + + when mem_read_data_single => + bus_cmd_cycle_en <= '1'; + if bus_din_vld = '1' then + cache_ctrl_state_next <= single_valid; + end if; + + when single_valid => + cache_rdy <= '1'; + single_read_reg_vld <= '1'; + cache_ctrl_state_next <= ready; + + when rd_cache => + was_miss <= '1'; + cache_ctrl_state_next <= ready; + + when others => + cache_ctrl_state_next <= ready; + end case; + + end process; + +flush_counter: + process(clk) + begin + if rising_edge(clk) then + if ctrl_in.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) + begin + if rising_edge(clk) then + if request_count_en = '0' then + request_count_rdy <= '0'; + request_count <= 2**word_index_width-1; + else + if bus_cmd_rdy = '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) + begin + if rising_edge(clk) then + if fill_count_en = '0' then + fill_count_rdy <= '0'; + fill_count <= 2**word_index_width-1; + else + if bus_din_vld = '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; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_20130728.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_20130728.vhd new file mode 100644 index 0000000..afa2147 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_20130728.vhd @@ -0,0 +1,637 @@ +-------------------------------------------------------------------------- +-- 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_util_pkg.all; +use work.mips_types.all; +use work.busmaster_types.all; + +ENTITY icache IS + Generic + ( + cache_size : natural := 2048; -- words + line_size : natural := 8; -- words + CACHE_ALL : boolean := false; + USE_CACHE : boolean := true; + USE_TLB : boolean := false + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + + -- CPU control/data + en : in STD_LOGIC; + addr : in word_t; + dout : out word_t; + rdy : out STD_LOGIC; + + -- Cache control + ctrl_in : in cache_ctrl_t; + + tlb_query_out : out tlb_query_in_t; + tlb_query_in : in tlb_query_out_t; + + -- busmaster data + bus_din : in word_t; + bus_din_rdy : out std_logic; + bus_din_vld : in std_logic; + + -- busmaster command + bus_cmd_rdy : in std_logic; + bus_cmd_we : out std_logic; + bus_cmd_cycle_en : out std_logic; + bus_cmd_out : out bm_cmd_t; + bus_cyc_complete : in std_logic + ); +END icache; + +ARCHITECTURE behavior OF icache IS + +COMPONENT dpram_1w1r2c_ra + 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; + 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_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 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; + + 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; + + function to_word_index(x : word_t) return unsigned is + variable result : unsigned(word_index_width-1 downto 0); + begin + result := x(word_index_width+1 downto 2); + + return result; + + end to_word_index; + + + function to_cache_index(x : word_t) return unsigned is + variable result : unsigned(cache_index_width-1 downto 0); + begin + result := x(cache_index_width+word_index_width+1 downto word_index_width+2); + + return result; + + end to_cache_index; + + function to_tag(x : word_t) return unsigned is + variable result : unsigned(tag_width-1 downto 0); + begin + result := x(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + return result; + + end to_tag; + + + type cache_ctrl_state_t is (init, ready, refill_pre, refill, refill_post, read_nofill); + signal cache_ctrl_state, cache_ctrl_state_next : cache_ctrl_state_t; + + type cache_mgmt_state_t is (init, ready, invalidate, flush, bus_request, mem_read, mem_read_single, mem_read_data, mem_read_data_single, rd_cache, upd_cache); + signal cache_mgmt_state, cache_mgmt_state_next : cache_mgmt_state_t; + + signal cache_req_rd : std_logic; + signal cache_refill_en : std_logic; + signal cache_refill_bsy : std_logic; + signal single_read_reg_vld : std_logic; + signal cache_rdy : 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_va : word_t; + signal request_pa : word_t; + + 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 : 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 single_read_reg : word_t; + + 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 ram_write_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; + + signal fill_word_index : unsigned(word_index_width-1 downto 0); + signal req_word_index : unsigned(word_index_width-1 downto 0); + + signal tlb_exc : std_logic; + signal tlb_exc_type : unsigned(1 downto 0); + + signal tlb_result_no_translate : tlb_query_out_t; + signal tlb_result : tlb_query_out_t; + +begin + + tlb_query_out.vaddr <= request_va; + tlb_result_no_translate.paddr <= request_va; + tlb_result_no_translate.hit_idx <= (others => '0'); + tlb_result_no_translate.hit <= '1'; + tlb_result_no_translate.flags.N <= '0' when CACHE_ALL = true else '1' when USE_CACHE = false else '1' when request_va(31 downto 29) = "101" else '0'; + tlb_result_no_translate.flags.D <= '0'; + tlb_result_no_translate.flags.V <= '1'; + + ram_read_en <= en or was_miss; + cache_index_inv <= ctrl_in.inv_addr(cache_index_width+word_index_width+1 downto word_index_width+2); + tag_inv <= ctrl_in.inv_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + tlb_result <= tlb_query_in when USE_TLB else tlb_result_no_translate; + request_pa <= tlb_result.paddr; + + tlb_exc <= cache_req_rd and not tlb_result.hit; + tlb_exc_type <= "10" when tlb_exc = '1' else "00"; + +fill_address_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' then + request_va <= addr; + fill_word_index <= to_word_index(addr); + elsif bus_din_vld = '1' and fill_count_en = '1' then + fill_word_index <= fill_word_index + 1; + end if; + end if; + end process; + +request_address_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' then + req_word_index <= to_word_index(addr); + elsif request_count_en = '1' and bus_cmd_rdy = '1' then + req_word_index <= req_word_index + 1; + end if; + end if; + end process; + +single_read_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + single_read_reg_vld <= '0'; + elsif bus_din_vld = '1' then + single_read_reg <= bus_din; + single_read_reg_vld <= tlb_result.flags.N; + elsif cache_rdy = '1' then + single_read_reg_vld <= '0'; + end if; + end if; + end process; + +request_register: + process(clk) + begin + if rising_edge(clk) then + cache_req_rd <= en; + end if; + end process; + +cache_invalidate_request: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' or ctrl_in.inv_all = '1' or ctrl_in.inv_at = '1' then + invalidate_req <= '1'; + invalidate_all <= ctrl_in.inv_all or rst; + 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_2w2r2c_ra + GENERIC MAP + ( + addr_width => tag_ram_addr_width, + data_width => tag_ram_data_width + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + 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_1w1r2c_ra + GENERIC MAP + ( + addr_width => lg2(cache_size), + data_width => word_t'length + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + 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) + begin + if rising_edge(clk) then + if rst = '1' then + cache_mgmt_state <= init; + cache_ctrl_state <= init; + else + cache_mgmt_state <= cache_mgmt_state_next; + cache_ctrl_state <= cache_ctrl_state_next; + end if; + end if; + end process; + + bus_din_rdy <= bus_din_vld; + bus_cmd_out.addr <= to_tag(request_pa) & to_cache_index(request_pa) & req_word_index & "00"; + bus_cmd_out.rw <= '0'; + bus_cmd_out.sel <= (others => '1'); + + rdy <= cache_rdy; + dout <= single_read_reg when single_read_reg_vld = '1' else data_ram_data_rd; + + cache_entry_out <= to_icache_entry(tag_ram_data_rd); + tag_match <= '1' when to_tag(request_pa) = cache_entry_out.tag else '0'; + cache_hit <= 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_hit_inv <= 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 to_cache_index(request_va); + tag_ram_addr_rd <= to_cache_index(addr) when was_miss = '0' else to_cache_index(request_va); + data_ram_addr_rd <= (to_cache_index(addr) & to_word_index(addr)) when was_miss = '0' else (to_cache_index(request_va) & fill_word_index); + data_ram_addr_wr <= to_cache_index(request_va) & fill_word_index; + data_ram_data_wr <= bus_din; + data_ram_we <= ram_write_en and bus_din_vld; + +cache_ctrl_state_fsm: + process(cache_ctrl_state, tlb_result, cache_req_rd, cache_hit, cache_refill_bsy) + begin + + cache_rdy <= '0'; + cache_refill_en <= '0'; + cache_ctrl_state_next <= cache_ctrl_state; + + case cache_ctrl_state is + + when init => + cache_rdy <= '1'; + cache_ctrl_state_next <= ready; + + when ready => + cache_rdy <= '1'; + if cache_req_rd = '1' then + if tlb_result.hit = '1' then + if tlb_result.flags.N = '1' then + cache_rdy <= '0'; + cache_ctrl_state_next <= refill_pre; + elsif cache_hit = '0' then + cache_rdy <= '0'; + cache_ctrl_state_next <= refill_pre; + end if; + end if; + end if; + + when refill_pre => + cache_refill_en <= '1'; + if cache_refill_bsy = '1' then + cache_ctrl_state_next <= refill; + end if; + + when refill => + if cache_refill_bsy = '0' then + cache_ctrl_state_next <= refill_post; + end if; + + when refill_post => + cache_rdy <= '1'; + cache_ctrl_state_next <= ready; + + when read_nofill => + cache_rdy <= '1'; + cache_ctrl_state_next <= ready; + + when others => + cache_ctrl_state_next <= ready; + + end case; + + end process; + +cache_mgmt_state_fsm: + process(cache_mgmt_state, cache_refill_en, tlb_result, bus_din_vld, cache_hit_inv, flush_count_rdy, fill_count_rdy, request_count_rdy, request_pa, bus_cmd_rdy, invalidate_req, invalidate_all) + begin + cache_refill_bsy <= '0'; + tag_ram_we <= '0'; + flush_count_en <= '0'; + flush_count_rst <= '0'; + invalidate_en <= '0'; + request_count_en <= '0'; + fill_count_en <= '0'; + ram_write_en <= '0'; + bus_cmd_cycle_en <= '0'; + bus_cmd_we <= '0'; + was_miss <= '0'; + invalidate_ack <= '0'; + + cache_entry_in.tv_p <= (others => '0'); + cache_entry_in.tag <= to_tag(request_pa); + cache_entry_in.valid <= '0'; + cache_mgmt_state_next <= cache_mgmt_state; + + case cache_mgmt_state is + + when init => + cache_mgmt_state_next <= ready; + + when ready => + if invalidate_req = '1' then + cache_mgmt_state_next <= invalidate; + invalidate_en <= '1'; + elsif cache_refill_en = '1' then + cache_mgmt_state_next <= bus_request; + bus_cmd_cycle_en <= '1'; + end if; + + when invalidate => + cache_mgmt_state_next <= rd_cache; + invalidate_en <= '1'; + invalidate_ack <= '1'; + if invalidate_all = '1' then + cache_mgmt_state_next <= 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'); + cache_mgmt_state_next <= 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'; + cache_mgmt_state_next <= rd_cache; + end if; + + when bus_request => + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + cache_mgmt_state_next <= mem_read; + if tlb_result.flags.N = '1' then + cache_refill_bsy <= '1'; + cache_mgmt_state_next <= mem_read_single; + end if; + end if; + + when mem_read => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_refill_bsy <= '1'; + request_count_en <= '1'; + ram_write_en <= '1'; + fill_count_en <= '1'; + if request_count_rdy = '1' then + bus_cmd_we <= '0'; + cache_mgmt_state_next <= mem_read_data; + end if; + + when mem_read_single => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_refill_bsy <= '1'; + cache_mgmt_state_next <= mem_read_data_single; + + when mem_read_data => + bus_cmd_cycle_en <= '1'; + cache_refill_bsy <= '1'; + fill_count_en <= '1'; + ram_write_en <= '1'; + if fill_count_rdy = '1' then + tag_ram_we <= '1'; + cache_entry_in.valid <= '1'; + cache_mgmt_state_next <= rd_cache; + end if; + + when mem_read_data_single => + bus_cmd_cycle_en <= '1'; + cache_refill_bsy <= '1'; + if bus_din_vld = '1' then + cache_mgmt_state_next <= ready; + end if; + + when rd_cache => + was_miss <= '1'; + cache_mgmt_state_next <= ready; + + when others => + cache_mgmt_state_next <= ready; + end case; + + end process; + +flush_counter: + process(clk) + begin + if rising_edge(clk) then + if ctrl_in.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) + begin + if rising_edge(clk) then + if request_count_en = '0' then + request_count_rdy <= '0'; + request_count <= 2**word_index_width-1; + else + if bus_cmd_rdy = '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) + begin + if rising_edge(clk) then + if fill_count_en = '0' then + fill_count_rdy <= '0'; + fill_count <= 2**word_index_width-1; + else + if bus_din_vld = '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; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_20130730.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_20130730.vhd new file mode 100644 index 0000000..0e14c0d --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_20130730.vhd @@ -0,0 +1,583 @@ +-------------------------------------------------------------------------- +-- 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_util_pkg.all; +use work.mips_types.all; +use work.busmaster_types.all; + +ENTITY icache IS + Generic + ( + cache_size : natural := 2048; -- words + line_size : natural := 8; -- words + CACHE_ALL : boolean := false; + USE_CACHE : boolean := false; + USE_TLB : boolean := false + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + + -- CPU control/data + en : in STD_LOGIC; + addr : in word_t; + dout : out word_t; + rdy : out STD_LOGIC; + + -- Cache control + ctrl_in : in cache_ctrl_t; + + tlb_query_out : out tlb_query_in_t; + tlb_query_in : in tlb_query_out_t; + + -- busmaster data + bus_din : in word_t; + bus_din_rdy : out std_logic; + bus_din_vld : in std_logic; + + -- busmaster command + bus_cmd_rdy : in std_logic; + bus_cmd_we : out std_logic; + bus_cmd_cycle_en : out std_logic; + bus_cmd_out : out bm_cmd_t; + bus_cyc_complete : in std_logic + ); +END icache; + +ARCHITECTURE behavior OF icache IS + +COMPONENT dpram_1w1r2c_ra + 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; + 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_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 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; + + 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; + + function to_word_index(x : word_t) return unsigned is + variable result : unsigned(word_index_width-1 downto 0); + begin + result := x(word_index_width+1 downto 2); + + return result; + + end to_word_index; + + + function to_cache_index(x : word_t) return unsigned is + variable result : unsigned(cache_index_width-1 downto 0); + begin + result := x(cache_index_width+word_index_width+1 downto word_index_width+2); + + return result; + + end to_cache_index; + + function to_tag(x : word_t) return unsigned is + variable result : unsigned(tag_width-1 downto 0); + begin + result := x(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + return result; + + end to_tag; + + type cache_ctrl_state_t is (init, ready, invalidate, flush, bus_request, mem_read, mem_read_single, mem_read_data, mem_read_data_single, rd_cache, upd_cache, single_valid); + signal cache_ctrl_state, cache_ctrl_state_next : cache_ctrl_state_t; + + signal cache_req_rd : std_logic; + signal single_read_reg_vld : std_logic; + signal cache_rdy : 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_va : word_t; + signal request_pa : word_t; + + 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 : 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 single_read_reg : word_t; + + 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 ram_write_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; + + signal fill_word_index : unsigned(word_index_width-1 downto 0); + signal req_word_index : unsigned(word_index_width-1 downto 0); + + signal tlb_exc : std_logic; + signal tlb_exc_type : unsigned(1 downto 0); + + signal tlb_result_no_translate : tlb_query_out_t; + signal tlb_result : tlb_query_out_t; + +begin + + tlb_query_out.vaddr <= request_va; + tlb_result_no_translate.paddr <= request_va; + tlb_result_no_translate.hit_idx <= (others => '0'); + tlb_result_no_translate.hit <= '1'; + tlb_result_no_translate.flags.N <= '0' when CACHE_ALL = true else '1' when USE_CACHE = false else '1' when request_va(31 downto 29) = "101" else '0'; + tlb_result_no_translate.flags.D <= '0'; + tlb_result_no_translate.flags.V <= '1'; + + ram_read_en <= en or was_miss; + cache_index_inv <= ctrl_in.inv_addr(cache_index_width+word_index_width+1 downto word_index_width+2); + tag_inv <= ctrl_in.inv_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + tlb_result <= tlb_query_in when USE_TLB else tlb_result_no_translate; + request_pa <= tlb_result.paddr; + + tlb_exc <= cache_req_rd and not tlb_result.hit; + tlb_exc_type <= "10" when tlb_exc = '1' else "00"; + +fill_address_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' and en = '1' then + request_va <= addr; + fill_word_index <= to_word_index(addr); + elsif bus_din_vld = '1' and fill_count_en = '1' then + fill_word_index <= fill_word_index + 1; + end if; + end if; + end process; + +request_address_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' and en = '1' then + req_word_index <= to_word_index(addr); + elsif request_count_en = '1' and bus_cmd_rdy = '1' then + req_word_index <= req_word_index + 1; + end if; + end if; + end process; + +single_read_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then +-- single_read_reg_vld <= '0'; + elsif bus_din_vld = '1' then + single_read_reg <= bus_din; +-- single_read_reg_vld <= tlb_result.flags.N; + elsif cache_rdy = '1' then +-- single_read_reg_vld <= '0'; + end if; + end if; + end process; + +request_register: + process(clk) + begin + if rising_edge(clk) then + cache_req_rd <= en; + end if; + end process; + +cache_invalidate_request: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' or ctrl_in.inv_all = '1' or ctrl_in.inv_at = '1' then + invalidate_req <= '1'; + invalidate_all <= ctrl_in.inv_all or rst; + 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_2w2r2c_ra + GENERIC MAP + ( + addr_width => tag_ram_addr_width, + data_width => tag_ram_data_width + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + 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_1w1r2c_ra + GENERIC MAP + ( + addr_width => lg2(cache_size), + data_width => word_t'length + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + 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) + begin + if rising_edge(clk) then + if rst = '1' then + cache_ctrl_state <= init; + else + cache_ctrl_state <= cache_ctrl_state_next; + end if; + end if; + end process; + + bus_din_rdy <= bus_din_vld; + bus_cmd_out.addr <= to_tag(request_pa) & to_cache_index(request_pa) & req_word_index & "00"; + bus_cmd_out.rw <= '0'; + bus_cmd_out.sel <= (others => '1'); + + rdy <= cache_rdy; + dout <= single_read_reg when single_read_reg_vld = '1' else data_ram_data_rd; + + cache_entry_out <= to_icache_entry(tag_ram_data_rd); + tag_match <= '1' when to_tag(request_pa) = cache_entry_out.tag else '0'; + cache_hit <= 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_hit_inv <= 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 to_cache_index(request_va); + tag_ram_addr_rd <= to_cache_index(addr) when was_miss = '0' else to_cache_index(request_va); + data_ram_addr_rd <= (to_cache_index(addr) & to_word_index(addr)) when was_miss = '0' else (to_cache_index(request_va) & fill_word_index); + data_ram_addr_wr <= to_cache_index(request_va) & fill_word_index; + data_ram_data_wr <= bus_din; + data_ram_we <= ram_write_en and bus_din_vld; + +cache_ctrl_state_fsm: + process(cache_ctrl_state, cache_req_rd, tlb_result, bus_din_vld, cache_hit, cache_hit_inv, flush_count_rdy, fill_count_rdy, request_count_rdy, request_pa, bus_cmd_rdy, invalidate_req, invalidate_all) + begin + cache_rdy <= '0'; + tag_ram_we <= '0'; + flush_count_en <= '0'; + flush_count_rst <= '0'; + invalidate_en <= '0'; + request_count_en <= '0'; + fill_count_en <= '0'; + ram_write_en <= '0'; + bus_cmd_cycle_en <= '0'; + bus_cmd_we <= '0'; + was_miss <= '0'; + invalidate_ack <= '0'; + single_read_reg_vld <= '0'; + + cache_entry_in.tv_p <= (others => '0'); + cache_entry_in.tag <= to_tag(request_pa); + cache_entry_in.valid <= '0'; + cache_ctrl_state_next <= cache_ctrl_state; + + case cache_ctrl_state is + + when init => + cache_ctrl_state_next <= ready; + cache_rdy <= '1'; + + when ready => + cache_rdy <= tlb_result.hit and (not tlb_result.flags.N or cache_hit); + if invalidate_req = '1' then + cache_ctrl_state_next <= invalidate; + invalidate_en <= '1'; + elsif cache_req_rd = '1' then + if tlb_result.hit = '1' then + if tlb_result.flags.N = '1' or cache_hit = '0' then + cache_rdy <= '0'; + cache_ctrl_state_next <= bus_request; + bus_cmd_cycle_en <= '1'; + end if; + end if; + end if; + + when invalidate => + cache_ctrl_state_next <= rd_cache; + invalidate_en <= '1'; + invalidate_ack <= '1'; + if invalidate_all = '1' then + cache_ctrl_state_next <= 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'); + cache_ctrl_state_next <= 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'; + cache_ctrl_state_next <= rd_cache; + end if; + + when bus_request => + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + cache_ctrl_state_next <= mem_read; + if tlb_result.flags.N = '1' then + cache_ctrl_state_next <= mem_read_single; + end if; + end if; + + when mem_read => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + request_count_en <= '1'; + ram_write_en <= '1'; + fill_count_en <= '1'; + if request_count_rdy = '1' then + bus_cmd_we <= '0'; + cache_ctrl_state_next <= mem_read_data; + end if; + + when mem_read_single => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_ctrl_state_next <= mem_read_data_single; + + when mem_read_data => + bus_cmd_cycle_en <= '1'; + fill_count_en <= '1'; + ram_write_en <= '1'; + if fill_count_rdy = '1' then + tag_ram_we <= '1'; + cache_entry_in.valid <= '1'; + cache_ctrl_state_next <= rd_cache; + end if; + + when mem_read_data_single => + bus_cmd_cycle_en <= '1'; + if bus_din_vld = '1' then + cache_ctrl_state_next <= single_valid; + end if; + + when single_valid => + cache_rdy <= '1'; + single_read_reg_vld <= '1'; + cache_ctrl_state_next <= ready; + + when rd_cache => + was_miss <= '1'; + cache_ctrl_state_next <= ready; + + when others => + cache_ctrl_state_next <= ready; + end case; + + end process; + +flush_counter: + process(clk) + begin + if rising_edge(clk) then + if ctrl_in.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) + begin + if rising_edge(clk) then + if request_count_en = '0' then + request_count_rdy <= '0'; + request_count <= 2**word_index_width-1; + else + if bus_cmd_rdy = '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) + begin + if rising_edge(clk) then + if fill_count_en = '0' then + fill_count_rdy <= '0'; + fill_count <= 2**word_index_width-1; + else + if bus_din_vld = '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; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_20130801_works.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_20130801_works.vhd new file mode 100644 index 0000000..d9a02cf --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_20130801_works.vhd @@ -0,0 +1,640 @@ +-------------------------------------------------------------------------- +-- 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_util_pkg.all; +use work.mips_types.all; +use work.busmaster_types.all; + +ENTITY icache IS + Generic + ( + cache_size : natural := 2048; -- words + line_size : natural := 8; -- words + CACHE_ALL : boolean := false; + USE_CACHE : boolean := false; + USE_TLB : boolean := false + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + + -- CPU control/data + en : in STD_LOGIC; + addr : in word_t; + dout : out word_t; + rdy : out STD_LOGIC; + + -- Cache control + ctrl_in : in cache_ctrl_t; + + tlb_query_out : out tlb_query_in_t; + tlb_query_in : in tlb_query_out_t; + + -- busmaster data + bus_din : in word_t; + bus_din_rdy : out std_logic; + bus_din_vld : in std_logic; + + -- busmaster command + bus_cmd_rdy : in std_logic; + bus_cmd_we : out std_logic; + bus_cmd_cycle_en : out std_logic; + bus_cmd_out : out bm_cmd_t; + bus_cyc_complete : in std_logic + ); +END icache; + +ARCHITECTURE behavior OF icache IS + +COMPONENT dpram_1w1r2c_ra + 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; + 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_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 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; + + 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; + + function to_word_index(x : word_t) return unsigned is + variable result : unsigned(word_index_width-1 downto 0); + begin + result := x(word_index_width+1 downto 2); + + return result; + + end to_word_index; + + + function to_cache_index(x : word_t) return unsigned is + variable result : unsigned(cache_index_width-1 downto 0); + begin + result := x(cache_index_width+word_index_width+1 downto word_index_width+2); + + return result; + + end to_cache_index; + + function to_tag(x : word_t) return unsigned is + variable result : unsigned(tag_width-1 downto 0); + begin + result := x(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + return result; + + end to_tag; + + type cache_busy_state_t is (init, ready, busy); + signal cache_busy_state, cache_busy_state_next : cache_busy_state_t; + + type cache_ctrl_state_t is (init, ready, invalidate, invalidate_post, flush, bus_request, mem_read, mem_read_single, mem_read_data, mem_read_data_single, rd_cache, upd_cache); + signal cache_ctrl_state, cache_ctrl_state_next : cache_ctrl_state_t; + + signal cache_req_rd : std_logic; + signal mem_fetch_req : std_logic; + signal mem_fetch_ack : std_logic; + signal single_read_en : std_logic; + signal single_read_set : std_logic; + signal single_read_reg_vld : std_logic; + signal read_rdy : std_logic; + signal cache_rdy : 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_va : word_t; + signal request_pa : word_t; + + 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 : 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 single_read_reg : word_t; + + 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 ram_write_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; + + signal fill_word_index : unsigned(word_index_width-1 downto 0); + signal req_word_index : unsigned(word_index_width-1 downto 0); + + signal tlb_exc : std_logic; + signal tlb_exc_type : unsigned(1 downto 0); + + signal tlb_result_no_translate : tlb_query_out_t; + signal tlb_result : tlb_query_out_t; + +begin + + tlb_query_out.vaddr <= request_va; + tlb_result_no_translate.paddr <= request_va; + tlb_result_no_translate.hit_idx <= (others => '0'); + tlb_result_no_translate.hit <= '1'; + tlb_result_no_translate.flags.N <= '0' when CACHE_ALL = true else '1' when USE_CACHE = false else '1' when request_va(31 downto 29) = "101" else '0'; + tlb_result_no_translate.flags.D <= '0'; + tlb_result_no_translate.flags.V <= '1'; + + ram_read_en <= en or was_miss; + cache_index_inv <= ctrl_in.inv_addr(cache_index_width+word_index_width+1 downto word_index_width+2); + tag_inv <= ctrl_in.inv_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + tlb_result <= tlb_query_in when USE_TLB else tlb_result_no_translate; + request_pa <= tlb_result.paddr; + + tlb_exc <= cache_req_rd and not tlb_result.hit; + tlb_exc_type <= "10" when tlb_exc = '1' else "00"; + +fill_address_register: + process(clk) + begin + if rising_edge(clk) then + if (cache_rdy = '1' and en = '1') or (cache_req_rd = '0' and en = '1') then + request_va <= addr; + fill_word_index <= to_word_index(addr); + elsif bus_din_vld = '1' and fill_count_en = '1' then + fill_word_index <= fill_word_index + 1; + end if; + end if; + end process; + +request_address_register: + process(clk) + begin + if rising_edge(clk) then + if (cache_rdy = '1' and en = '1') or (cache_req_rd = '0' and en = '1') then + req_word_index <= to_word_index(addr); + elsif request_count_en = '1' and bus_cmd_rdy = '1' then + req_word_index <= req_word_index + 1; + end if; + end if; + end process; + +single_read_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + single_read_en <= '0'; + single_read_reg_vld <= '0'; + elsif single_read_en = '0' then + if single_read_set = '1' then + single_read_en <= '1'; + end if; + elsif single_read_reg_vld = '1' then + if en = '1' then + single_read_en <= '0'; + single_read_reg_vld <= '0'; + end if; + elsif bus_din_vld = '1' and single_read_en = '1' then + single_read_reg <= bus_din; + single_read_reg_vld <= '1'; + end if; + end if; + end process; + +request_register: + process(clk) + begin + if rising_edge(clk) then + cache_req_rd <= en; + end if; + end process; + +cache_invalidate_request: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' or ctrl_in.inv_all = '1' or ctrl_in.inv_at = '1' then + invalidate_req <= '1'; + invalidate_all <= ctrl_in.inv_all or rst; + 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_2w2r2c_ra + GENERIC MAP + ( + addr_width => tag_ram_addr_width, + data_width => tag_ram_data_width + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + 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_1w1r2c_ra + GENERIC MAP + ( + addr_width => lg2(cache_size), + data_width => word_t'length + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + 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) + begin + if rising_edge(clk) then + if rst = '1' then + cache_ctrl_state <= init; + cache_busy_state <= init; + else + cache_ctrl_state <= cache_ctrl_state_next; + cache_busy_state <= cache_busy_state_next; + end if; + end if; + end process; + + bus_din_rdy <= fill_count_en or single_read_en; + bus_cmd_out.addr <= to_tag(request_pa) & to_cache_index(request_pa) & req_word_index & "00"; + bus_cmd_out.rw <= '0'; + bus_cmd_out.sel <= (others => '1'); + + cache_rdy <= read_rdy; + rdy <= cache_rdy; + + dout <= single_read_reg when single_read_en = '1' else data_ram_data_rd; + + cache_entry_out <= to_icache_entry(tag_ram_data_rd); + tag_match <= '1' when to_tag(request_pa) = cache_entry_out.tag else '0'; + cache_hit <= 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_hit_inv <= 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 to_cache_index(request_va); + tag_ram_addr_rd <= to_cache_index(addr) when was_miss = '0' else to_cache_index(request_va); + data_ram_addr_rd <= (to_cache_index(addr) & to_word_index(addr)) when was_miss = '0' else (to_cache_index(request_va) & fill_word_index); + data_ram_addr_wr <= to_cache_index(request_va) & fill_word_index; + data_ram_data_wr <= bus_din; + data_ram_we <= ram_write_en and bus_din_vld; + +cache_busy_state_fsm: + process(cache_busy_state, tlb_result, cache_req_rd, cache_hit, mem_fetch_ack, single_read_en, single_read_reg_vld) + begin + + read_rdy <= '0'; + mem_fetch_req <= '0'; + single_read_set <= '0'; + cache_busy_state_next <= cache_busy_state; + + case cache_busy_state is + + when init => + cache_busy_state_next <= ready; + + when ready => + if single_read_en = '1' then + read_rdy <= single_read_reg_vld; + else + read_rdy <= '1'; + if cache_req_rd = '1' then + if tlb_result.hit = '1' then + if tlb_result.flags.N = '1' or cache_hit = '0' then + mem_fetch_req <= '1'; + read_rdy <= '0'; + single_read_set <= tlb_result.flags.N; + cache_busy_state_next <= busy; + end if; + end if; + end if; + end if; + + when busy => + mem_fetch_req <= '1'; + if mem_fetch_ack = '1' then + cache_busy_state_next <= ready; + end if; + + when others => + cache_busy_state_next <= ready; + + end case; + + end process; + +cache_ctrl_state_fsm: + process(cache_ctrl_state, mem_fetch_req, tlb_result, bus_din_vld, cache_hit_inv, flush_count_rdy, fill_count_rdy, request_count_rdy, request_pa, bus_cmd_rdy, invalidate_req, invalidate_all) + begin + tag_ram_we <= '0'; + flush_count_en <= '0'; + flush_count_rst <= '0'; + invalidate_en <= '0'; + request_count_en <= '0'; + fill_count_en <= '0'; + ram_write_en <= '0'; + bus_cmd_cycle_en <= '0'; + bus_cmd_we <= '0'; + was_miss <= '0'; + invalidate_ack <= '0'; + mem_fetch_ack <= '0'; + + cache_entry_in.tv_p <= (others => '0'); + cache_entry_in.tag <= to_tag(request_pa); + cache_entry_in.valid <= '0'; + cache_ctrl_state_next <= cache_ctrl_state; + + case cache_ctrl_state is + + when init => + cache_ctrl_state_next <= ready; + + when ready => + if invalidate_req = '1' then + cache_ctrl_state_next <= invalidate; + invalidate_en <= '1'; + elsif mem_fetch_req = '1' then + cache_ctrl_state_next <= bus_request; + bus_cmd_cycle_en <= '1'; + end if; + + when invalidate => + cache_ctrl_state_next <= rd_cache; + invalidate_en <= '1'; + invalidate_ack <= '1'; + if invalidate_all = '1' then + cache_ctrl_state_next <= 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'); + cache_ctrl_state_next <= invalidate_post; + 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'; + cache_ctrl_state_next <= invalidate_post; + end if; + + when invalidate_post => + was_miss <= '1'; + cache_ctrl_state_next <= ready; + + when bus_request => + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + cache_ctrl_state_next <= mem_read; + if tlb_result.flags.N = '1' then + cache_ctrl_state_next <= mem_read_single; + end if; + end if; + + when mem_read => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + request_count_en <= '1'; + ram_write_en <= '1'; + fill_count_en <= '1'; + if request_count_rdy = '1' then + bus_cmd_we <= '0'; + cache_ctrl_state_next <= mem_read_data; + end if; + + when mem_read_single => + mem_fetch_ack <= '1'; + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_ctrl_state_next <= mem_read_data_single; + + when mem_read_data => + bus_cmd_cycle_en <= '1'; + fill_count_en <= '1'; + ram_write_en <= '1'; + if fill_count_rdy = '1' then + tag_ram_we <= '1'; + cache_entry_in.valid <= '1'; + cache_ctrl_state_next <= rd_cache; + end if; + + when mem_read_data_single => + bus_cmd_cycle_en <= '1'; + if bus_din_vld = '1' then + cache_ctrl_state_next <= ready; + end if; + + when rd_cache => + mem_fetch_ack <= '1'; + was_miss <= '1'; + cache_ctrl_state_next <= ready; + + when others => + cache_ctrl_state_next <= ready; + end case; + + end process; + +flush_counter: + process(clk) + begin + if rising_edge(clk) then + if ctrl_in.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) + begin + if rising_edge(clk) then + if request_count_en = '0' then + request_count_rdy <= '0'; + request_count <= 2**word_index_width-1; + else + if bus_cmd_rdy = '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) + begin + if rising_edge(clk) then + if fill_count_en = '0' then + fill_count_rdy <= '0'; + fill_count <= 2**word_index_width-1; + else + if bus_din_vld = '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; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_new.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_new.vhd new file mode 100644 index 0000000..ea470e8 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_new.vhd @@ -0,0 +1,671 @@ +-------------------------------------------------------------------------- +-- 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_util_pkg.all; +use work.mips_types.all; +use work.busmaster_types.all; + +ENTITY icache IS + Generic + ( + CACHE_SIZE : natural := 1024; -- words + LINE_SIZE : natural := 8; -- words + CACHE_ALL : boolean := false; + USE_CACHE : boolean := true; + USE_TLB : boolean := false + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + + -- CPU control/data + en : in STD_LOGIC; + addr : in word_t; + dout : out word_t; + rdy : out STD_LOGIC; + + -- Cache control + ctrl_in : in cache_ctrl_t; + + tlb_query_out : out tlb_query_in_t; + tlb_query_in : in tlb_query_out_t; + + -- busmaster data + bus_din : in word_t; + bus_din_rdy : out std_logic; + bus_din_vld : in std_logic; + + -- busmaster command + bus_cmd_rdy : in std_logic; + bus_cmd_we : out std_logic; + bus_cmd_cycle_en : out std_logic; + bus_cmd_out : out bm_cmd_t; + bus_cyc_complete : in std_logic + ); +END icache; + +ARCHITECTURE behavior OF icache IS + +COMPONENT dpram_1w1r2c_ra + 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; + 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_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 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; + + 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; + + function to_word_index(x : word_t) return unsigned is + variable result : unsigned(word_index_width-1 downto 0); + begin + result := x(word_index_width+1 downto 2); + + return result; + + end to_word_index; + + + function to_cache_index(x : word_t) return unsigned is + variable result : unsigned(cache_index_width-1 downto 0); + begin + result := x(cache_index_width+word_index_width+1 downto word_index_width+2); + + return result; + + end to_cache_index; + + function to_tag(x : word_t) return unsigned is + variable result : unsigned(tag_width-1 downto 0); + begin + result := x(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + return result; + + end to_tag; + + type cache_busy_state_t is (init, ready, busy); + signal cache_busy_state, cache_busy_state_next : cache_busy_state_t; + + type cache_ctrl_state_t is (init, ready, invalidate, invalidate_post, flush, bus_request, mem_read, mem_read_single, mem_read_data, mem_read_data_single, rd_cache, upd_cache); + signal cache_ctrl_state, cache_ctrl_state_next : cache_ctrl_state_t; + + type debug_t is record + ready : std_logic; + req_strobe : std_logic; + req_read : std_logic; + req_va : word_t; + req_pa : word_t; + dout_reg : word_t; + end record; + + signal debug : debug_t; + + signal req_strobe : std_logic; + signal cache_req_rd : std_logic; + signal read_rdy : std_logic; + signal cache_rdy : std_logic; + signal cache_hit : std_logic; + signal request_va : word_t; + signal request_pa : word_t; + + signal tag_match : std_logic; + signal cache_hit_inv : std_logic; + signal tag_match_inv : std_logic; + + signal mem_fetch_req : std_logic; + signal mem_fetch_ack : std_logic; + signal single_read_en : std_logic; + signal single_read_set : std_logic; + signal single_read_reg_vld : std_logic; + + 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 : 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 single_read_reg : word_t; + signal cache_dout : word_t; + + 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 ram_write_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; + + signal fill_word_index : unsigned(word_index_width-1 downto 0); + signal req_word_index : unsigned(word_index_width-1 downto 0); + + signal tlb_exc : std_logic; + signal tlb_exc_type : unsigned(1 downto 0); + + signal tlb_result_no_translate : tlb_query_out_t; + signal tlb_result : tlb_query_out_t; + +begin + + tlb_query_out.vaddr <= request_va; + tlb_result_no_translate.paddr <= request_va; + tlb_result_no_translate.hit_idx <= (others => '0'); + tlb_result_no_translate.hit <= '1'; + tlb_result_no_translate.flags.N <= '0' when CACHE_ALL = true else '1' when USE_CACHE = false else '1' when request_va(31 downto 29) = "101" else '0'; + tlb_result_no_translate.flags.D <= '0'; + tlb_result_no_translate.flags.V <= '1'; + + cache_index_inv <= ctrl_in.inv_addr(cache_index_width+word_index_width+1 downto word_index_width+2); + tag_inv <= ctrl_in.inv_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + tlb_result <= tlb_query_in when USE_TLB else tlb_result_no_translate; + request_pa <= tlb_result.paddr; + + tlb_exc <= cache_req_rd and not tlb_result.hit; + tlb_exc_type <= "10" when tlb_exc = '1' else "00"; + + debug.req_strobe <= req_strobe; + debug.req_read <= cache_req_rd; + debug.req_va <= request_va; + debug.req_pa <= request_pa; + debug.dout_reg <= cache_dout; + debug.ready <= cache_rdy; + +------------------------------------------------- +-- ctrl +------------------------------------------------- +req_strobe <= cache_rdy and en; + +ctrl_request_register: + process(clk) + begin + if rising_edge(clk) then + if cache_rdy = '1' then + cache_req_rd <= en; + end if; + end if; + end process; + +------------------------------------------------- +fill_address_register: + process(clk) + begin + if rising_edge(clk) then + if req_strobe = '1' then + request_va <= addr; + fill_word_index <= to_word_index(addr); + elsif bus_din_vld = '1' and fill_count_en = '1' then + fill_word_index <= fill_word_index + 1; + end if; + end if; + end process; + +bus_request_address_register: + process(clk) + begin + if rising_edge(clk) then + if req_strobe = '1' then + req_word_index <= to_word_index(addr); + elsif request_count_en = '1' and bus_cmd_rdy = '1' then + req_word_index <= req_word_index + 1; + end if; + end if; + end process; + +single_read_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + single_read_en <= '0'; + single_read_reg_vld <= '0'; + elsif single_read_en = '0' then + if single_read_set = '1' then + single_read_en <= '1'; + end if; + elsif single_read_reg_vld = '1' then + if en = '1' then + single_read_en <= '0'; + single_read_reg_vld <= '0'; + end if; + elsif bus_din_vld = '1' and single_read_en = '1' then + single_read_reg <= bus_din; + single_read_reg_vld <= '1'; + end if; + end if; + end process; + +cache_invalidate_request: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' or ctrl_in.inv_all = '1' or ctrl_in.inv_at = '1' then + invalidate_req <= '1'; + invalidate_all <= ctrl_in.inv_all or rst; + 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_2w2r2c_ra + GENERIC MAP + ( + addr_width => tag_ram_addr_width, + data_width => tag_ram_data_width + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + 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_1w1r2c_ra + GENERIC MAP + ( + addr_width => lg2(CACHE_SIZE), + data_width => word_t'length + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + 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) + begin + if rising_edge(clk) then + if rst = '1' then + cache_ctrl_state <= init; + cache_busy_state <= init; + else + cache_ctrl_state <= cache_ctrl_state_next; + cache_busy_state <= cache_busy_state_next; + end if; + end if; + end process; + + bus_din_rdy <= fill_count_en or single_read_en; + bus_cmd_out.addr <= to_tag(request_pa) & to_cache_index(request_pa) & req_word_index & "00"; + bus_cmd_out.rw <= '0'; + bus_cmd_out.sel <= (others => '1'); + + cache_rdy <= read_rdy; + rdy <= cache_rdy; + + cache_dout <= single_read_reg when single_read_en = '1' else data_ram_data_rd; + dout <= cache_dout; + + cache_entry_out <= to_icache_entry(tag_ram_data_rd); + tag_match <= '1' when to_tag(request_pa) = cache_entry_out.tag else '0'; + cache_hit <= 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_hit_inv <= tag_match_inv and cache_entry_out_inv.valid; + + ram_read_en <= en or was_miss; + 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 to_cache_index(request_va); + tag_ram_addr_rd <= to_cache_index(addr) when was_miss = '0' else to_cache_index(request_va); + data_ram_addr_rd <= (to_cache_index(addr) & to_word_index(addr)) when was_miss = '0' else (to_cache_index(request_va) & fill_word_index); + data_ram_addr_wr <= to_cache_index(request_va) & fill_word_index; + data_ram_data_wr <= bus_din; + data_ram_we <= ram_write_en and bus_din_vld; + +cache_busy_state_fsm: + process(cache_busy_state, tlb_result, cache_req_rd, cache_hit, mem_fetch_ack, single_read_en, single_read_reg_vld) + begin + + read_rdy <= '0'; + mem_fetch_req <= '0'; + single_read_set <= '0'; + cache_busy_state_next <= cache_busy_state; + + case cache_busy_state is + + when init => + cache_busy_state_next <= ready; + + when ready => + if single_read_en = '1' then + read_rdy <= single_read_reg_vld; + else + read_rdy <= '1'; + if cache_req_rd = '1' then + if tlb_result.hit = '1' then + if tlb_result.flags.N = '1' or cache_hit = '0' then + mem_fetch_req <= '1'; + read_rdy <= '0'; + single_read_set <= tlb_result.flags.N; + cache_busy_state_next <= busy; + end if; + end if; + end if; + end if; + + when busy => + mem_fetch_req <= '1'; + if mem_fetch_ack = '1' then + cache_busy_state_next <= ready; + end if; + + when others => + cache_busy_state_next <= ready; + + end case; + + end process; + +cache_ctrl_state_fsm: + process(cache_ctrl_state, mem_fetch_req, tlb_result, bus_din_vld, cache_hit_inv, flush_count_rdy, fill_count_rdy, request_count_rdy, request_pa, bus_cmd_rdy, invalidate_req, invalidate_all) + begin + tag_ram_we <= '0'; + flush_count_en <= '0'; + flush_count_rst <= '0'; + invalidate_en <= '0'; + request_count_en <= '0'; + fill_count_en <= '0'; + ram_write_en <= '0'; + bus_cmd_cycle_en <= '0'; + bus_cmd_we <= '0'; + was_miss <= '0'; + invalidate_ack <= '0'; + mem_fetch_ack <= '0'; + + cache_entry_in.tv_p <= (others => '0'); + cache_entry_in.tag <= to_tag(request_pa); + cache_entry_in.valid <= '0'; + cache_ctrl_state_next <= cache_ctrl_state; + + case cache_ctrl_state is + + when init => + cache_ctrl_state_next <= ready; + + when ready => + if invalidate_req = '1' then + cache_ctrl_state_next <= invalidate; + invalidate_en <= '1'; + elsif mem_fetch_req = '1' then + cache_ctrl_state_next <= bus_request; + bus_cmd_cycle_en <= '1'; + end if; + + when invalidate => + cache_ctrl_state_next <= rd_cache; + invalidate_en <= '1'; + invalidate_ack <= '1'; + if invalidate_all = '1' then + cache_ctrl_state_next <= 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'); + cache_ctrl_state_next <= invalidate_post; + 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'; + cache_ctrl_state_next <= invalidate_post; + end if; + + when invalidate_post => + was_miss <= '1'; + cache_ctrl_state_next <= ready; + + when bus_request => + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + cache_ctrl_state_next <= mem_read; + if tlb_result.flags.N = '1' then + cache_ctrl_state_next <= mem_read_single; + end if; + end if; + + when mem_read => + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + request_count_en <= '1'; + ram_write_en <= '1'; + fill_count_en <= '1'; + if request_count_rdy = '1' then + bus_cmd_we <= '0'; + cache_ctrl_state_next <= mem_read_data; + end if; + + when mem_read_single => + mem_fetch_ack <= '1'; + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + cache_ctrl_state_next <= mem_read_data_single; + + when mem_read_data => + bus_cmd_cycle_en <= '1'; + fill_count_en <= '1'; + ram_write_en <= '1'; + if fill_count_rdy = '1' then + tag_ram_we <= '1'; + cache_entry_in.valid <= '1'; + cache_ctrl_state_next <= rd_cache; + end if; + + when mem_read_data_single => + bus_cmd_cycle_en <= '1'; + if bus_din_vld = '1' then + cache_ctrl_state_next <= ready; + end if; + + when rd_cache => + mem_fetch_ack <= '1'; + was_miss <= '1'; + cache_ctrl_state_next <= ready; + + when others => + cache_ctrl_state_next <= ready; + end case; + + end process; + +flush_counter: + process(clk) + begin + if rising_edge(clk) then + if ctrl_in.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) + begin + if rising_edge(clk) then + if request_count_en = '0' then + request_count_rdy <= '0'; + request_count <= 2**word_index_width-1; + else + if bus_cmd_rdy = '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) + begin + if rising_edge(clk) then + if fill_count_en = '0' then + fill_count_rdy <= '0'; + fill_count <= 2**word_index_width-1; + else + if bus_din_vld = '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; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_orig.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_orig.vhd new file mode 100644 index 0000000..5be317f --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_icache_orig.vhd @@ -0,0 +1,513 @@ +-------------------------------------------------------------------------- +-- 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_util_pkg.all; +use work.mips_types.all; +use work.busmaster_types.all; + +ENTITY icache IS + Generic + ( + cache_size : natural := 2048; -- words + line_size : natural := 8 -- words + ); + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + + -- CPU control/data + en : in STD_LOGIC; + addr : in word_t; + dout : out word_t; + rdy : out STD_LOGIC; + + -- Cache control + ctrl_in : in cache_ctrl_t; + + tlb_query_out : out tlb_query_in_t; + tlb_query_in : in tlb_query_out_t; + + -- busmaster arbitration + bus_req : out std_logic; + bus_gnt : in std_logic; + + -- busmaster data + bus_din : in word_t; + bus_din_rdy : out std_logic; + bus_din_vld : in std_logic; + +-- bus_dout : out word_t; +-- bus_dout_vld : out std_logic; +-- bus_dout_rdy : in std_logic; + + -- busmaster command + bus_cmd_rdy : in std_logic; + bus_cmd_we : out std_logic; + bus_cmd_cycle_en : out std_logic; + bus_cmd_out : out bm_cmd_t; + bus_cyc_complete : in std_logic + + ); +END icache; + +ARCHITECTURE behavior OF icache IS + +COMPONENT dpram_1w1r2c_ra + 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; + 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_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 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; + + 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 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 : 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; + + alias cpu_word_index is addr(word_index_width+1 downto 2); + alias cpu_cache_index is addr(cache_index_width+word_index_width+1 downto word_index_width+2); + alias cpu_tag is 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 + + ram_read_en <= en or was_miss; + cache_index_inv <= ctrl_in.inv_addr(cache_index_width+word_index_width+1 downto word_index_width+2); + tag_inv <= ctrl_in.inv_addr(tag_width+cache_index_width+word_index_width+1 downto cache_index_width+word_index_width+2); + + +fill_address_register: + process(clk) + begin + if rising_edge(clk) then + if fill_count_en = '1' then + if bus_din_vld = '1' then + fill_word_index <= fill_word_index + 1; + end if; + elsif cache_busy = '0' then + fill_addr <= addr(addr_width-1 downto 2) & "00"; + end if; + end if; + end process; + +request_address_register: + process(clk) + begin + if rising_edge(clk) then + if request_count_en = '1'then + if bus_cmd_rdy = '1' then + req_word_index <= req_word_index + 1; + end if; + elsif cache_busy = '0' then + request_addr <= addr(addr_width-1 downto 2) & "00"; + end if; + end if; + end process; + +cpu_request_register: + process(clk) + begin + if rising_edge(clk) then + if rst = '1' then + cache_req <= '0'; + elsif 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) + begin + if rising_edge(clk) then + if rst = '1' or ctrl_in.inv_all = '1' or ctrl_in.inv_at = '1' then + invalidate_req <= '1'; + invalidate_all <= ctrl_in.inv_all or rst; + 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_2w2r2c_ra + GENERIC MAP + ( + addr_width => tag_ram_addr_width, + data_width => tag_ram_data_width + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + 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_1w1r2c_ra + GENERIC MAP + ( + addr_width => lg2(cache_size), + data_width => word_t'length + ) + PORT MAP + ( + clk_a => clk, + clk_b => clk, + 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) + begin + if rising_edge(clk) then + if rst = '1' then + s <= init; + else + s <= sn; + end if; + end if; + end process; + + bus_din_rdy <= fill_count_en; + bus_cmd_out.addr <= fill_tag & fill_cache_index & req_word_index & "00"; + bus_cmd_out.rw <= '0'; + bus_cmd_out.sel <= (others => '0'); + + rdy <= not cache_busy; + dout <= data_ram_data_rd; + + cache_entry_out <= to_icache_entry(tag_ram_data_rd); + tag_match <= '1' when fill_tag = 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 fill_cache_index; + tag_ram_addr_rd <= cpu_cache_index when was_miss = '0' else fill_cache_index; + data_ram_addr_rd <= (cpu_cache_index & cpu_word_index) when was_miss = '0' else (fill_cache_index & fill_word_index); + data_ram_addr_wr <= fill_cache_index & fill_word_index; + data_ram_data_wr <= bus_din; + data_ram_we <= fill_count_en and bus_din_vld; + +cache_state: + process(s, cache_req, cache_miss, cache_miss_inv, flush_count_rdy, fill_count_rdy, request_count_rdy, fill_tag, bus_cmd_rdy, 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'; + bus_cmd_cycle_en <= '0'; + bus_cmd_we <= '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'; + elsif cache_req = '1' then + if cache_miss = '1' then + sn <= mem_request; + bus_cmd_cycle_en <= '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 => + bus_cmd_cycle_en <= '1'; + if bus_cmd_rdy = '1' then + sn <= mem_access; + end if; + when mem_access => + request_count_en <= '1'; + fill_count_en <= '1'; + bus_cmd_cycle_en <= '1'; + bus_cmd_we <= '1'; + if request_count_rdy = '1' then + bus_cmd_we <= '0'; + sn <= mem_data; + end if; + when mem_data => + bus_cmd_cycle_en <= '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) + begin + if rising_edge(clk) then + if ctrl_in.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) + begin + if rising_edge(clk) then + if request_count_en = '0' then + request_count_rdy <= '0'; + request_count <= 2**word_index_width-1; + else + if bus_cmd_rdy = '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) + begin + if rising_edge(clk) then + if fill_count_en = '0' then + fill_count_rdy <= '0'; + fill_count <= 2**word_index_width-1; + else + if bus_din_vld = '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; diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_tlb_types.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_tlb_types.vhd new file mode 100644 index 0000000..9e6b1fc --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/old/mips_tlb_types.vhd @@ -0,0 +1,83 @@ +-------------------------------------------------------------------------- +-- Project: JIPS, a portable 32-bit RISC CPU written in VHDL +-- This file: Types, constants and functions for JIPS +-- +-- 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; +use IEEE.MATH_REAL.ALL; + +library work; +use work.mips_types.all; +use work.mips_util_pkg.all; + +-------------------------------------------------------------------------- +package tlb_types is + + +-- constant TLB_PAGE_SIZE : positive := 4096; +-- constant TLB_NUM_ASIDS : positive := 64; +-- constant TLB_NUM_ENTRIES : positive := 32; + + --Types +-- type tlb_flags_t is record +-- N : std_logic; +-- D : std_logic; +-- V : std_logic; +-- end record; +-- +-- type tlb_entry_lo_t is record +-- PFN : unsigned (31 downto lg2(TLB_PAGE_SIZE)); +-- N : std_logic; +-- D : std_logic; +-- V : std_logic; +-- end record; +-- +-- type tlb_entry_hi_t is record +-- VPN : unsigned (31 downto lg2(TLB_PAGE_SIZE)); +-- ASID : unsigned (lg2(TLB_NUM_ASIDS)-1 downto 0); +-- G : std_logic; +-- end record; + + -- Functions + function "or" (a, b : tlb_flags_t) return tlb_flags_t; + +end tlb_types; + +-------------------------------------------------------------------------- +package body tlb_types is + + function "or" (a, b : tlb_flags_t) return tlb_flags_t is + variable result : tlb_flags_t; + begin + result.N := a.N or b.N; + result.D := a.D or b.D; + result.V := a.V or b.V; + + return result; + + end "or"; + +end tlb_types; + +-------------------------------------------------------------------------- + diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/test.vhd b/lib/CPUs/MIPS/src/core/MMU/TLB/test.vhd new file mode 100644 index 0000000..44a4c4a --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/test.vhd @@ -0,0 +1,73 @@ + +entity dpram_1w1r2c_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; + 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 dpram_1w1r2c_ra; + + + + + +------------------------------------------- +-- to_component +------------------------------------------- +-- case insensitive +-- regexp +-- text + +GENERIC +GENERIC + +PORT +PORT + +^ENTITY +COMPONENT + + +IS +{empty} + + +^.*END.* +END COMPONENT; + +------------------------------------------- +-- to_portmap +------------------------------------------- +-- case insensitive +-- regexp +-- text + +GENERIC +GENERIC MAP + +PORT +PORT MAP + +: +.*; +=>, + +: +.* +=> + +\(\<.*[^ \t]*.*\>\)[ \t,]*=> +\1\t=>\t\1 + +^ENTITY[ \t]+\([^ ].*[^ ]\) .*IS +inst_\1: \1 + +^END +.*$ +{empty} diff --git a/lib/CPUs/MIPS/src/core/MMU/TLB/todo_txt.txt b/lib/CPUs/MIPS/src/core/MMU/TLB/todo_txt.txt new file mode 100644 index 0000000..2e42e95 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/TLB/todo_txt.txt @@ -0,0 +1,9 @@ +pipeline: + - privileged_address exc. also for dmem + +cop: +- assign badVaddr for exc. SYS, RI, BP + +libsys.c, xcpt.c +- INT exc. handler nicht hard-codieren +- Bei libsys_init als standard int. exc. handler registrieren \ No newline at end of file diff --git a/lib/CPUs/MIPS/src/core/MMU/mips_biu.vhd b/lib/CPUs/MIPS/src/core/MMU/mips_biu.vhd new file mode 100644 index 0000000..874ac5c --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/mips_biu.vhd @@ -0,0 +1,552 @@ +-------------------------------------------------------------------------- +-- 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 biu is + Generic + ( + icache_size : natural := 2048; -- words + icache_line : natural := 8; -- words + dcache_size : natural := 2048; -- words + dcache_line : 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 unsigned(31 downto 0); + DAT_I : in unsigned(31 downto 0); + DAT_O : out unsigned(31 downto 0); + WE_O : out STD_LOGIC; + SEL_O : out unsigned(3 downto 0); + CYC_O : out STD_LOGIC; + STB_O : out STD_LOGIC; + MRDY_O : out STD_LOGIC; + cop0_ctrl_in : in cop0_ctrl_out_t; + cpu_clk : in STD_LOGIC; + cpu_imem_err : out STD_LOGIC; + cpu_imem_rdy : out STD_LOGIC; + cpu_imem_en : in STD_LOGIC; + cpu_imem_addr : in word_t; + cpu_imem_din : out word_t; + cpu_dmem_err : out STD_LOGIC; + cpu_dmem_rdy : out STD_LOGIC; + cpu_dmem_en : in STD_LOGIC; + cpu_dmem_we : in STD_LOGIC; + cpu_dmem_be : in unsigned(3 downto 0); + cpu_dmem_dout : in word_t; + cpu_dmem_din : out word_t; + cpu_dmem_addr : in word_t + ); +end biu; + +architecture behavior of biu is + + COMPONENT icache + GENERIC + ( + cache_size : natural; -- words + line_size : natural -- 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_addr : in word_t; + cpu_dout : out word_t; + cpu_busy : out STD_LOGIC + ); + END COMPONENT; + + COMPONENT dcache + GENERIC + ( + cache_size : natural; -- words + line_size : natural -- 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 COMPONENT; + + type bus_state_t is (init, ready, icache_bus_access, dcache_bus_access, write_bus, read_bus, read_finish); + + signal s, sn : bus_state_t; + signal bus_idle : std_logic; + signal busy : std_logic; + signal dmem_be : unsigned(3 downto 0); + signal dcache_dout : word_t; + signal dcache_mem_gnt : std_logic; + signal icache_mem_gnt : std_logic; + signal dmem_mem_wr_gnt : std_logic; + signal dmem_mem_rd_gnt : std_logic; + signal dcache_busy : std_logic; + signal icache_busy : std_logic; + signal CYC_O_icache : std_logic; + signal CYC_O_dcache : std_logic; + signal CYC_O_dmem_rd : std_logic; + signal CYC_O_dmem_wr : std_logic; + signal SRDY_I_icache : std_logic; + signal SRDY_I_dcache : std_logic; + signal MRDY_O_icache : std_logic; + signal MRDY_O_dcache : std_logic; + signal ADDR_O_icache : word_t; + signal ADDR_O_dcache : word_t; + signal ADDR_O_dmem_rd : word_t; + signal ADDR_O_dmem_wr : word_t; + signal STB_O_icache : std_logic; + signal STB_O_dcache : std_logic; + signal STB_O_dmem_rd : std_logic; + signal STB_O_dmem_wr : std_logic; + signal DAT_I_dmem_rd : word_t; + signal DAT_O_dmem_wr : word_t; + signal SEL_O_dmem_wr : unsigned(3 downto 0); + signal SEL_O_dmem_rd : unsigned(3 downto 0); + signal dcached : std_logic; + signal dcache_en2 : std_logic; + signal dcache_en : std_logic; + signal uncached_access : std_logic; + signal ACK : std_logic; + signal DAT : unsigned(31 downto 0); + + type timeout_cnt_t is range 0 to 1E5-1; + signal bus_timeout_cnt : timeout_cnt_t; + signal bus_timeout : std_logic; + + signal bout_fifo_din : unsigned(68 downto 0); + signal bout_fifo_dout : unsigned(68 downto 0); + signal bout_fifo_re : std_logic; + signal bout_fifo_we : std_logic; + signal bout_fifo_full : std_logic; + signal bout_fifo_empty : std_logic; + signal bout_rdy : std_logic; + + signal bin_fifo_din : unsigned(31 downto 0); + signal bin_fifo_dout : unsigned(31 downto 0); + signal bin_fifo_re : std_logic; + signal bin_fifo_we : std_logic; + signal bin_fifo_full : std_logic; + signal bin_fifo_empty : std_logic; + + alias bout_fifo_addr_in is bout_fifo_din(31 downto 0); + alias bout_fifo_data_in is bout_fifo_din(63 downto 32); + alias bout_fifo_sel_in is bout_fifo_din(67 downto 64); + alias bout_fifo_we_in is bout_fifo_din(68); + alias bout_fifo_addr_out is bout_fifo_dout(31 downto 0); + alias bout_fifo_data_out is bout_fifo_dout(63 downto 32); + alias bout_fifo_sel_out is bout_fifo_dout(67 downto 64); + alias bout_fifo_we_out is bout_fifo_dout(68); + + signal write_fifo_din : unsigned(67 downto 0); + signal write_fifo_dout : unsigned(67 downto 0); + signal write_fifo_re : std_logic; + signal write_fifo_we : std_logic; + signal write_fifo_full : std_logic; + signal write_fifo_empty : std_logic; + + alias write_fifo_addr_in is write_fifo_din(31 downto 0); + alias write_fifo_data_in is write_fifo_din(63 downto 32); + alias write_fifo_sel_in is write_fifo_din(67 downto 64); + alias write_fifo_addr_out is write_fifo_dout(31 downto 0); + alias write_fifo_data_out is write_fifo_dout(63 downto 32); + alias write_fifo_sel_out is write_fifo_dout(67 downto 64); + + signal read_cycle : std_logic; + +begin + +read_cyc_register: + process(cpu_clk) + begin + if rising_edge(cpu_clk) then + if RST_I = '1' then + read_cycle <= '0'; + else + read_cycle <= (dmem_mem_rd_gnt and CYC_O_dmem_rd) + or (dcache_mem_gnt and CYC_O_dcache) + or (icache_mem_gnt and CYC_O_icache); + end if; + end if; + end process; + + MRDY_O <= not bin_fifo_full; + CYC_O <= not bout_fifo_empty or read_cycle; + STB_O <= not bout_fifo_empty; + ADDR_O <= bout_fifo_addr_out; + DAT_O <= bout_fifo_data_out; + SEL_O <= bout_fifo_sel_out; + WE_O <= bout_fifo_we_out; + + cpu_imem_rdy <= not icache_busy after 4.5 ns; + busy <= CYC_O_dmem_rd or dcache_busy or (write_fifo_full); + cpu_dmem_rdy <= not busy after 4.5 ns; + +inst_icache : icache + GENERIC MAP + ( + cache_size => icache_size, -- words + line_size => icache_line -- words + ) + PORT MAP + ( + CLK_I => cpu_clk, + RST_I => RST_I, + STB_O => STB_O_icache, + CYC_O => CYC_O_icache, + ADDR_O => ADDR_O_icache, + MRDY_O => MRDY_O_icache, + DAT_I => DAT, + ACK_I => ACK, + SRDY_I => SRDY_I_icache, + ctrl => cop0_ctrl_in.icache, + cpu_en => cpu_imem_en, + cpu_addr => cpu_imem_addr, + cpu_dout => cpu_imem_din, + cpu_busy => icache_busy + ); + + SRDY_I_icache <= bout_rdy and icache_mem_gnt; + +inst_dcache : dcache + GENERIC MAP + ( + cache_size => dcache_size, -- words + line_size => dcache_line -- words + ) + PORT MAP + ( + CLK_I => cpu_clk, + RST_I => RST_I, + CYC_O => CYC_O_dcache, + STB_O => STB_O_dcache, + ADDR_O => ADDR_O_dcache, + MRDY_O => MRDY_O_dcache, + DAT_I => DAT, + ACK_I => ACK, + SRDY_I => SRDY_I_dcache, + ctrl => cop0_ctrl_in.dcache, + cpu_en => dcache_en, + cpu_we => cpu_dmem_we, + cpu_be => cpu_dmem_be, + cpu_addr => cpu_dmem_addr, + cpu_din => cpu_dmem_dout, + cpu_dout => dcache_dout, + cpu_busy => dcache_busy + ); + + SRDY_I_dcache <= bout_rdy and dcache_mem_gnt; + + dcached <= '1' when cpu_dmem_addr(31 downto 29) /= "101" else '0'; + cpu_dmem_din <= dcache_dout when uncached_access = '0' else DAT_I_dmem_rd; + dcache_en <= dcached and cpu_dmem_en and dcache_en2; -- or write_fifo_full); + +-- Instantiate synchronous FIFO +inst_bin_fifo: entity work.fifo_async + GENERIC MAP + ( + addr_width => 4, + data_width => 32, + do_last_read_update => true + ) + PORT MAP + ( + rst => RST_I, + clk_w => CLK_I, + clk_r => cpu_clk, + we => bin_fifo_we, + re => bin_fifo_re, + fifo_full => bin_fifo_full, + fifo_empty => bin_fifo_empty, + fifo_afull => open, + fifo_aempty => open, + data_w => bin_fifo_din, + data_r => bin_fifo_dout + ); + + bin_fifo_din <= DAT_I; + DAT <= bin_fifo_dout; + ACK <= not bin_fifo_empty; + bin_fifo_we <= ACK_I; + bin_fifo_re <= dmem_mem_rd_gnt or MRDY_O_icache or MRDY_O_dcache; + +-- Instantiate synchronous FIFO +inst_bout_fifo: entity work.fifo_async + GENERIC MAP + ( + addr_width => 4, + data_width => 69, + do_last_read_update => false + ) + PORT MAP + ( + rst => RST_I, + clk_w => cpu_clk, + clk_r => CLK_I, + we => bout_fifo_we, + re => bout_fifo_re, + fifo_full => bout_fifo_full, + fifo_empty => bout_fifo_empty, + fifo_afull => open, + fifo_aempty => open, + data_w => bout_fifo_din, + data_r => bout_fifo_dout + ); + bout_rdy <= not bout_fifo_full; + bout_fifo_re <= SRDY_I and not bin_fifo_full; + + bout_fifo_we <= STB_O_dmem_wr or STB_O_dmem_rd or STB_O_dcache or STB_O_icache; + + bout_fifo_data_in <= DAT_O_dmem_wr when dmem_mem_wr_gnt = '1' else (others => '-'); + + bout_fifo_addr_in <= ADDR_O_dmem_wr when dmem_mem_wr_gnt = '1' else + ADDR_O_dmem_rd when dmem_mem_rd_gnt = '1' else + ADDR_O_dcache when dcache_mem_gnt = '1' else + ADDR_O_icache when icache_mem_gnt = '1' else (others => '-'); + + bout_fifo_sel_in <= SEL_O_dmem_wr when dmem_mem_wr_gnt = '1' else + SEL_O_dmem_rd when dmem_mem_rd_gnt = '1' else (others => '1'); + + bout_fifo_we_in <= '1' when dmem_mem_wr_gnt = '1' else '0'; + +-- Instantiate synchronous FIFO +inst_write_fifo: entity work.fifo_sync + GENERIC MAP + ( + addr_width => 4, + data_width => 68, + do_last_read_update => true + ) + PORT MAP + ( + rst => RST_I, + clk => cpu_clk, + we => write_fifo_we, + re => write_fifo_re, + fifo_full => write_fifo_full, + fifo_empty => write_fifo_empty, + fifo_afull => open, + fifo_aempty => open, + data_w => write_fifo_din, + data_r => write_fifo_dout + ); + + CYC_O_dmem_wr <= not write_fifo_empty; + DAT_O_dmem_wr <= write_fifo_data_out; + ADDR_O_dmem_wr <= write_fifo_addr_out; + SEL_O_dmem_wr <= write_fifo_sel_out; + + write_fifo_data_in <= cpu_dmem_dout; + write_fifo_addr_in <= cpu_dmem_addr; + write_fifo_sel_in <= cpu_dmem_be; + write_fifo_re <= STB_O_dmem_wr; + write_fifo_we <= cpu_dmem_en and not busy and cpu_dmem_we; + +dmem_rd_flags: + process(cpu_clk) + begin + if rising_edge(cpu_clk) then + uncached_access <= '0'; + if RST_I = '1' then + CYC_O_dmem_rd <= '0'; + dcache_en2 <= '1'; + else + if ACK = '1' and dmem_mem_rd_gnt = '1' then + uncached_access <= '1'; + CYC_O_dmem_rd <= '0'; + dcache_en2 <= '1'; + end if; + if cpu_dmem_en = '1' and busy = '0' and cpu_dmem_we = '0' then + if dcached = '0' then + CYC_O_dmem_rd <= '1'; + dcache_en2 <= '0'; + end if; + end if; + end if; + end if; + end process; + +dmem_rd_data: + process(cpu_clk) + begin + if rising_edge(cpu_clk) then + if RST_I = '1' then + DAT_I_dmem_rd <= (others => '0'); + elsif ACK = '1' and CYC_O_dmem_rd = '1' then + DAT_I_dmem_rd <= DAT; + end if; + end if; + end process; + +dmem_rd_regs: + process(cpu_clk) + begin + if rising_edge(cpu_clk) then + if cpu_dmem_en = '1' and busy = '0' then + ADDR_O_dmem_rd <= cpu_dmem_addr; + SEL_O_dmem_rd <= cpu_dmem_be; + end if; + end if; + end process; + +bus_state_next: + process(cpu_clk) + begin + if rising_edge(cpu_clk) then + if RST_I = '1' then + s <= init; + else + s <= sn; + end if; + end if; + end process; + +bus_state: + process(s, CYC_O_icache, CYC_O_dcache, CYC_O_dmem_wr, CYC_O_dmem_rd, bout_rdy, ACK) + begin + + icache_mem_gnt <= '0'; + dcache_mem_gnt <= '0'; + dmem_mem_rd_gnt <= '0'; + dmem_mem_wr_gnt <= '0'; + STB_O_dmem_rd <= '0'; + STB_O_dmem_wr <= '0'; + bus_idle <= '0'; + sn <= s; + case s is + when init => + sn <= ready; + when ready => + bus_idle <= '1'; + if CYC_O_dmem_wr = '1' then + sn <= write_bus; + elsif CYC_O_dmem_rd = '1' then + sn <= read_bus; + elsif CYC_O_icache = '1' then + sn <= icache_bus_access; + elsif CYC_O_dcache = '1' then + sn <= dcache_bus_access; + end if; + when icache_bus_access => + icache_mem_gnt <= '1'; + if CYC_O_icache = '0' then + sn <= ready; + end if; + when dcache_bus_access => + dcache_mem_gnt <= '1'; + if CYC_O_dcache = '0' then + sn <= ready; + end if; + when write_bus => + dmem_mem_wr_gnt <= '1'; + if CYC_O_dmem_wr = '1' then + if bout_rdy = '1' then + STB_O_dmem_wr <= '1'; + end if; + else + sn <= ready; + end if; + when read_bus => + dmem_mem_rd_gnt <= '1'; + if bout_rdy = '1' then + STB_O_dmem_rd <= '1'; + sn <= read_finish; + end if; + when read_finish => + dmem_mem_rd_gnt <= '1'; + if ACK = '1' then + sn <= ready; + end if; + when others => + sn <= ready; + end case; + + end process; + +bus_timeout_counter: + process(cpu_clk) + begin + if rising_edge(cpu_clk) then + if bus_idle = '0' then + if bus_timeout_cnt /= 0 then + bus_timeout_cnt <= bus_timeout_cnt - 1; + else + bus_timeout <= '1'; + end if; + else + bus_timeout_cnt <= timeout_cnt_t'high; + bus_timeout <= '0'; + end if; + end if; + end process; + +bus_err: + process(cpu_clk) + begin + if rising_edge(cpu_clk) then + if RST_I = '1' then + cpu_imem_err <= '0'; + cpu_dmem_err <= '0'; + elsif bus_timeout = '1' then + cpu_imem_err <= icache_mem_gnt; + cpu_dmem_err <= dcache_mem_gnt or dmem_mem_wr_gnt or dmem_mem_rd_gnt; + end if; + end if; + end process; + +------------------------------------------------------------------- + +end behavior; diff --git a/lib/CPUs/MIPS/src/core/MMU/mips_dcache.vhd b/lib/CPUs/MIPS/src/core/MMU/mips_dcache.vhd new file mode 100644 index 0000000..61aca40 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/mips_dcache.vhd @@ -0,0 +1,513 @@ +-------------------------------------------------------------------------- +-- 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 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; diff --git a/lib/CPUs/MIPS/src/core/MMU/mips_icache.vhd b/lib/CPUs/MIPS/src/core/MMU/mips_icache.vhd new file mode 100644 index 0000000..0d93e18 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/mips_icache.vhd @@ -0,0 +1,488 @@ +-------------------------------------------------------------------------- +-- 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; + 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_addr : in word_t; + cpu_dout : out word_t; + cpu_busy : out STD_LOGIC + ); +END icache; + +ARCHITECTURE behavior OF icache IS + +COMPONENT dpram_1w1r2c_ra + 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; + 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_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 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; + + 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 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 : 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; + + 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 + + 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); + + +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 + 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_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 => 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_1w1r2c_ra + GENERIC MAP + ( + addr_width => lg2(cache_size), + data_width => word_t'length + ) + PORT MAP + ( + clk_a => CLK_I, + clk_b => 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; + + MRDY_O <= fill_count_en; + ADDR_O <= fill_tag & fill_cache_index & req_word_index & "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 fill_tag = 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 fill_cache_index; + tag_ram_addr_rd <= cpu_cache_index when was_miss = '0' else fill_cache_index; + data_ram_addr_rd <= (cpu_cache_index & cpu_word_index) when was_miss = '0' else (fill_cache_index & fill_word_index); + data_ram_addr_wr <= fill_cache_index & fill_word_index; + 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, fill_tag, 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 <= 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'; + 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; diff --git a/lib/CPUs/MIPS/src/core/MMU/mips_pipeline_nofwd.vhd b/lib/CPUs/MIPS/src/core/MMU/mips_pipeline_nofwd.vhd new file mode 100644 index 0000000..6477d95 --- /dev/null +++ b/lib/CPUs/MIPS/src/core/MMU/mips_pipeline_nofwd.vhd @@ -0,0 +1,867 @@ +-------------------------------------------------------------------------- +-- Project: JIPS, a portable 32-bit RISC CPU written in VHDL +-- This file: The pipeline +-- +-- 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; +use work.mips_instr.all; + +entity pipeline is + Port + ( + rst : in STD_LOGIC; + clk : in STD_LOGIC; + ce : in STD_LOGIC; + imem_err : in STD_LOGIC; + imem_rdy : in STD_LOGIC; + imem_en : out STD_LOGIC; + imem_addr : out word_t; + imem_data : in word_t; + dmem_err : in STD_LOGIC; + dmem_rdy : in STD_LOGIC; + dmem_en : out STD_LOGIC; + dmem_we : out STD_LOGIC; + dmem_be : out unsigned(3 downto 0); + dmem_addr : out word_t; + dmem_din : in word_t; + dmem_dout : out word_t; + cop_ir : out word_t; + cop_ir_en : out STD_LOGIC; + cop_din : in word_t; + cop_dout : out word_t; + c0_ctrl_out : out cop0_ctrl_in_t; + c0_ctrl_in : in cop0_ctrl_out_t + + ); +end pipeline; + +architecture Behavioral of pipeline is + +-------------------------------------------------------------------------- + COMPONENT reg_dual is + Generic + ( + addr_width : integer := 3; + data_width : integer := 8 + ); + Port + ( + clk_w : in STD_LOGIC; + en : in STD_LOGIC; + we : in STD_LOGIC; + wptr : in unsigned (addr_width-1 downto 0); + din : in unsigned (data_width-1 downto 0); + rptr_a : in unsigned (addr_width-1 downto 0); + rptr_b : in unsigned (addr_width-1 downto 0); + dout_a : out unsigned (data_width-1 downto 0); + dout_b : out unsigned (data_width-1 downto 0) + ); + END COMPONENT; + +-------------------------------------------------------------------------- + COMPONENT shifter is + Generic + ( + data_width : integer + ); + Port + ( + shift_ctrl : in shift_ctrl_t; + din : in unsigned (data_width-1 downto 0); + dout : out unsigned (data_width-1 downto 0) + ); + END COMPONENT; + +-------------------------------------------------------------------------- + COMPONENT alu is + Generic + ( + data_width : integer + ); + Port + ( + op1_in : in unsigned (data_width-1 downto 0); + op2_in : in unsigned (data_width-1 downto 0); + op2_shifted : in unsigned (data_width-1 downto 0); + ctrl : in alu_ctrl_t; + result : out unsigned (data_width-1 downto 0); + flags : out alu_flags_t + ); + END COMPONENT; + +-------------------------------------------------------------------------- + COMPONENT bcu is + Generic + ( + data_width : integer + ); + Port + ( + op1_in : in unsigned (data_width-1 downto 0); + op2_in : in unsigned (data_width-1 downto 0); + flags : out bcu_flags_t + ); + END COMPONENT; + +-------------------------------------------------------------------------- + COMPONENT muldiv is + Port + ( + rst : in std_logic; + clk : in std_logic; + hilo_we : in std_logic; + din_hi : in word_t; + din_lo : in word_t; + mul_divn : in std_logic; + start : in std_logic; + s_un : in std_logic; + hilo_sel : in std_logic; + busy : out std_logic; + dout : out word_t + ); + END COMPONENT; + +-------------------------------------------------------------------------- + signal ID_stage : ID_t; + signal EX_stage : EX_t; + signal MEM_stage : MEM_t; + signal WB_stage : WB_t; + signal clk_2, clk_1 : STD_LOGIC; + signal hdu : hdu_t; + signal sdu : sdu_t; + signal reg_a : word_t; + signal reg_b : word_t; + signal branch_ce : STD_LOGIC; + signal cpu_run : STD_LOGIC; + signal alu_result : word_t; + signal mul_result : word_t; + signal mul_busy : STD_LOGIC; + signal EX_events_instr : event_t; + signal EX_events_alu : event_t; + signal EX_events_mem : event_t; + signal EX_events : event_t; + signal MEM_events : event_t; + signal bcu_op_a : word_t; + signal bcu_op_b : word_t; + signal bcu_flags : bcu_flags_t; + signal vaddr : word_t; + signal dmem_src : word_t; + signal stage_rst : unsigned(3 downto 0); + signal pipe_rst : STD_LOGIC; + + signal pc : pc_t; + +-------------------------------------------------------------------------- +begin + + clk_1 <= clk; + clk_2 <= not clk; + cpu_run <= ce; + + -- Stall Detection Unit --------------------------------------------------- + sdu.ID_nop <= sdu.imem_dep or c0_ctrl_in.exc_pending or ID_stage.exc or EX_stage.exc or MEM_stage.exc; + sdu.EX_nop <= sdu.mul_dep or ID_stage.nop or ID_stage.exc; + sdu.MEM_nop <= EX_stage.nop or EX_stage.exc; + sdu.WB_nop <= sdu.dmem_dep or MEM_stage.nop; + + sdu.stall_all <= sdu.dmem_dep; + sdu.ID_stall <= sdu.stall_all or sdu.imem_dep or sdu.mul_dep or c0_ctrl_in.exc_exit; + sdu.EX_stall <= sdu.stall_all; + sdu.MEM_stall <= sdu.stall_all; + sdu.WB_stall <= '0'; + + sdu.mul_dep <= ID_stage.ctrl.mul_access and (EX_stage.ctrl.mul_start or mul_busy); + sdu.imem_dep <= not imem_rdy; + sdu.dmem_dep <= not dmem_rdy and MEM_stage.ctrl.dmem_en; + --------------------------------------------------------------------------- + imem_en <= cpu_run and not (sdu.mul_dep or sdu.dmem_dep or c0_ctrl_in.exc_commit); + +-------------------------------------------------------------------------- +-- Coprocessor assignments +-------------------------------------------------------------------------- + c0_ctrl_out.bd_wb <= WB_stage.bd; + c0_ctrl_out.epc_mem <= MEM_stage.pcn; + c0_ctrl_out.epc_wb <= MEM_stage.epc; + c0_ctrl_out.imem_addr <= EX_stage.epc; + c0_ctrl_out.dmem_addr <= MEM_stage.va; + c0_ctrl_out.sdu <= sdu; + c0_ctrl_out.events <= WB_stage.events; + c0_ctrl_out.exc_req <= MEM_stage.exc; + c0_ctrl_out.exc_ack <= WB_stage.exc; + cop_ir <= ID_stage.IR; + cop_ir_en <= ID_stage.ctrl.cop_instr_en; + cop_dout <= dmem_din when MEM_stage.ctrl.dmem_en = '1' else MEM_stage.ex_result; + +-------------------------------------------------------------------------- +-- Muldiv +-------------------------------------------------------------------------- +inst_muldiv: muldiv + PORT MAP + ( + rst => rst, + clk => clk, + hilo_we => EX_stage.ctrl.mul_hilo_we, + din_hi => EX_stage.reg_a, + din_lo => EX_stage.reg_b, + mul_divn => EX_stage.ctrl.mul_mul_divn, + start => EX_stage.ctrl.mul_start, + s_un => EX_stage.ctrl.mul_s_un, + hilo_sel => EX_stage.ctrl.mul_hilo_sel, + busy => mul_busy, + dout => mul_result + ); + +-------------------------------------------------------------------------- +-- IF stage +-------------------------------------------------------------------------- + imem_addr <= pc.curr; + + pipe_rst <= stage_rst(0); + +proc_stage_reset: + process(clk_1) + begin + if rising_edge(clk_1) then + if rst = '1' then + stage_rst <= (others => '1'); + else + stage_rst <= stage_rst(stage_rst'left-1 downto 0) & '0'; + end if; + end if; + end process; + +proc_stage_pc: + process(pc) + begin + if pc.branch_take = '1' then + pc.curr <= pc.pc_branch after 2 ns; + else + pc.curr <= pc.nxt after 2 ns; + end if; + end process; + +proc_stage_pc_branch: + process(clk_1) + begin + if rising_edge(clk_1) then + if sdu.ID_stall = '0' then + pc.pc_branch <= pc.curr + ID_stage.bimm18; + end if; + end if; + end process; + +proc_stage_branch_ce: + process(clk_1) + begin + if rising_edge(clk_1) then + if pipe_rst = '1' then + branch_ce <= '1'; + else + branch_ce <= c0_ctrl_in.exc_pending; + if sdu.ID_stall = '0' and c0_ctrl_in.exc_inject = '0' then + branch_ce <= '1'; + end if; + end if; + end if; + end process; + +proc_stage_pc_next: + process(clk_1) + begin + if rising_edge(clk_1) then + if c0_ctrl_in.exc_inject = '1' then + pc.nxt <= c0_ctrl_in.exc_vec; + elsif sdu.ID_stall = '0' then + pc.last <= pc.curr; + if ID_stage.ctrl.jump = '1' then + pc.nxt <= ID_stage.jimm32; + elsif ID_stage.ctrl.jump_long = '1' then + pc.nxt <= ID_stage.reg_a; + else + pc.nxt <= pc.curr + 4; + end if; + end if; + end if; + end process; + +proc_stage_branch: + process(clk_2) + begin + if rising_edge(clk_2) and branch_ce = '1' then + pc.branch_take <= '0'; + if EX_stage.ctrl.branch = '1' then + + case EX_stage.ctrl.bc_src is + + when bc_eq_ne => + pc.branch_take <= EX_stage.ctrl.bc_not xor bcu_flags.eq; + + when bc_lez_gtz => + pc.branch_take <= EX_stage.ctrl.bc_not xor (bcu_flags.eq or bcu_flags.ltz); + + when bc_ltz_gez => + pc.branch_take <= EX_stage.ctrl.bc_not xor bcu_flags.ltz; + + when others => null; + end case; + end if; + end if; + end process; + +-------------------------------------------------------------------------- +-- ID stage +-------------------------------------------------------------------------- + ID_stage.IR <= to_01(imem_data); + ID_stage.pcn <= pc.curr; + ID_stage.op <= decode_op(ID_stage.IR); + ID_stage.jimm32 <= extract_jimm32(ID_stage.IR, ID_stage.pcn); + ID_stage.bimm18 <= extract_bimm18(ID_stage.IR, ID_stage.pcn); + ID_stage.shamt <= extract_shamt(ID_stage.IR); + ID_stage.reg_a_rptr <= extract_rs(ID_stage.IR); + ID_stage.reg_b_rptr <= extract_rt(ID_stage.IR); + ID_stage.ctrl <= opcode_ctrl_lines(ID_stage.IR) when sdu.ID_nop = '0' else ctrl_lines_default; + ID_stage.reg_write <= ID_stage.ctrl.reg_write; + ID_stage.epc <= pc.last; + ID_stage.exc <= event_is_active(ID_stage.events); -- Todo: works + ID_stage.nop <= sdu.ID_nop; + +proc_ID_except: + process(ID_stage, c0_ctrl_in, pc) + begin + ID_stage.events <= events_clr; + ID_stage.events.inst_load_err <= not c0_ctrl_in.exc_pending and (pc.nxt(1) or pc.nxt(0)); + ID_stage.events.inst_priv_addr <= not c0_ctrl_in.exc_pending and (pc.nxt(word_t'left) and c0_ctrl_in.user_mode); + end process; + +proc_stage_hdu: + process(ID_stage, EX_stage, MEM_stage, WB_stage) + variable read_a, read_b : boolean; + variable raw_a_EX, raw_a_MEM, raw_a_WB : boolean; + variable raw_b_EX, raw_b_MEM, raw_b_WB : boolean; + variable reg_ptr_a : reg_ptr_t; + variable reg_ptr_b : reg_ptr_t; + begin + + reg_ptr_a := ID_stage.reg_a_rptr; + reg_ptr_b := ID_stage.reg_b_rptr; + raw_a_EX := reg_ptr_a = EX_stage.reg_wptr and EX_stage.wreg_we = '1'; + raw_a_MEM := reg_ptr_a = MEM_stage.reg_wptr and MEM_stage.wreg_we = '1'; + raw_a_WB := reg_ptr_a = WB_stage.reg_wptr and WB_stage.wreg_we = '1'; + raw_b_EX := reg_ptr_b = EX_stage.reg_wptr and EX_stage.wreg_we = '1'; + raw_b_MEM := reg_ptr_b = MEM_stage.reg_wptr and MEM_stage.wreg_we = '1'; + raw_b_WB := reg_ptr_b = WB_stage.reg_wptr and WB_stage.wreg_we = '1'; + + hdu.alu_fwd_a_ex <= raw_a_EX after 1 ns; + hdu.alu_fwd_a_mem <= raw_a_MEM after 1 ns; + hdu.alu_fwd_a_wb <= raw_a_WB after 1 ns; + hdu.alu_fwd_b_ex <= raw_b_EX after 1 ns; + hdu.alu_fwd_b_mem <= raw_b_MEM after 1 ns; + hdu.alu_fwd_b_wb <= raw_b_WB after 1 ns; + + end process; + +proc_stage_fwd_a: + process(reg_a, hdu, EX_stage, MEM_stage, WB_stage) + variable data : word_t; + begin + data := reg_a; +-- if hdu.alu_fwd_a_ex then +-- data := EX_stage.result; +-- elsif hdu.alu_fwd_a_mem then +-- data := MEM_stage.data; +-- elsif hdu.alu_fwd_a_wb then +-- data := WB_stage.data; +-- end if; + + for i in 0 to 31 loop + if data(i) = '1' then + ID_stage.reg_a(i) <= '1' after 2 ns; + else + ID_stage.reg_a(i) <= '0' after 2 ns; + end if; + end loop; + + end process; + +proc_stage_fwd_b: + process(reg_b, hdu, EX_stage, MEM_stage, WB_stage) + variable data : word_t; + begin + data := reg_b; +-- if hdu.alu_fwd_b_ex then +-- data := EX_stage.result; +-- elsif hdu.alu_fwd_b_mem then +-- data := MEM_stage.data; +-- elsif hdu.alu_fwd_b_wb then +-- data := WB_stage.data; +-- end if; + + for i in 0 to 31 loop + if data(i) = '1' then + ID_stage.reg_b(i) <= '1' after 2 ns; + else + ID_stage.reg_b(i) <= '0' after 2 ns; + end if; + end loop; + + end process; + +proc_imm_mux: + process(ID_stage) + variable data : word_t; + begin + data := extract_uimm16(ID_stage.IR); + + case ID_stage.ctrl.imm_src is + + when src_imm32 => + data := extract_simm32(ID_stage.IR); + + when src_imm16 => + data := extract_uimm16(ID_stage.IR); + + when src_imm16_high => + data := ID_stage.IR(word_t'length/2-1 downto 0) & (word_t'length/2-1 downto 0 => '0'); + + when others => null; + + end case; + + ID_stage.imm <= data after 2 ns; + + end process; + +-------------------------------------------------------------------------- +inst_reg_dual: reg_dual + GENERIC MAP + ( + addr_width => reg_ptr_t'length, + data_width => word_t'length + ) + PORT MAP + ( + clk_w => clk_1, + we => WB_stage.wreg_we, + en => '1', + wptr => WB_stage.reg_wptr, + din => WB_stage.data, + rptr_a => ID_stage.reg_a_rptr, + rptr_b => ID_stage.reg_b_rptr, + dout_a => reg_a, + dout_b => reg_b + ); + +-------------------------------------------------------------------------- +-- EX stage +-------------------------------------------------------------------------- + EX_stage.reg_a_rptr <= extract_rs(EX_stage.IR); + EX_stage.reg_b_rptr <= extract_rt(EX_stage.IR); + EX_stage.result <= alu_result when EX_stage.ctrl.mul_access = '0' else mul_result; + +proc_stage_ID_EX_1: + process(clk_1) + begin + if rising_edge(clk_1) then + if stage_rst(1) = '1' then + EX_stage.op <= NOP; + EX_stage.IR <= (others => '0'); + EX_stage.ctrl <= ctrl_lines_default; + EX_stage.reg_write <= '0'; + EX_stage.epc <= (others => '0'); + EX_stage.pcn <= (others => '0'); + EX_stage.events_in <= events_clr; + elsif sdu.EX_stall = '0' then + EX_stage.reg_a <= ID_stage.reg_a; + EX_stage.reg_b <= ID_stage.reg_b; + EX_stage.events_in <= ID_stage.events; + EX_stage.op <= ID_stage.op; + EX_stage.ctrl <= ID_stage.ctrl; + EX_stage.reg_write <= ID_stage.reg_write; + EX_stage.nop <= sdu.EX_nop; + EX_stage.IR <= ID_stage.IR; + EX_stage.pcn <= ID_stage.pcn; + EX_stage.epc <= ID_stage.epc; + if sdu.EX_nop = '1' then + EX_stage.op <= NOP; + EX_stage.IR <= (others => '0'); + EX_stage.ctrl <= ctrl_lines_default; + EX_stage.reg_write <= '0'; + end if; + end if; + end if; + end process; + +proc_stage_EX_instr_except: + process(EX_stage) + begin + EX_events_instr <= events_clr; + EX_events_instr.illegal <= EX_stage.ctrl.exc_illegal; + EX_events_instr.break <= EX_stage.ctrl.exc_break; + EX_events_instr.syscall <= EX_stage.ctrl.exc_syscall; + end process; + +proc_stage_EX_alu_except: + process(EX_stage) + begin + EX_events_alu <= events_clr; + if EX_stage.ctrl.alu_exc_en = '1' then + if EX_stage.alu_flags.ovf = '1' then + EX_events_alu.alu_ovf <= '1'; + end if; + if EX_stage.alu_flags.uvf = '1' then + EX_events_alu.alu_uvf <= '1'; + end if; + end if; + end process; + + vaddr <= ID_stage.reg_a + extract_simm32(ID_stage.IR); + +proc_stage_EX_mem_except: + process(clk_1) + begin + if rising_edge(clk_1) then + if pipe_rst = '1' then + dmem_en <= '0'; + elsif sdu.EX_stall = '0' then + EX_events_mem <= events_clr; + dmem_en <= '0'; + if ID_stage.ctrl.dmem_en = '1' then + dmem_en <= '1'; + if ID_stage.ctrl.except_en = '1' then + if ID_stage.ctrl.word2_en = '1' then + if vaddr(0) = '1' then + EX_events_mem.data_load_err <= not ID_stage.ctrl.dmem_we; + EX_events_mem.data_store_err <= ID_stage.ctrl.dmem_we; + dmem_en <= '0'; + end if; + elsif vaddr(1 downto 0) /= "00" then + EX_events_mem.data_load_err <= not ID_stage.ctrl.dmem_we; + EX_events_mem.data_store_err <= ID_stage.ctrl.dmem_we; + dmem_en <= '0'; + end if; + end if; + end if; + end if; + end if; + end process; + +proc_stage_DMEM_ADDR: + process(clk_1) + begin + if rising_edge(clk_1) then + if sdu.EX_stall = '0' then + EX_stage.va <= vaddr; + if c0_ctrl_in.EB = '1' then + if ID_stage.ctrl.word2_en = '0' then + EX_stage.pa_off <= not vaddr(1 downto 0); + else + EX_stage.pa_off <= not vaddr(1) & vaddr(0); + end if; + else + EX_stage.pa_off <= vaddr(1 downto 0); + end if; + end if; + end if; + end process; + dmem_src <= cop_din when EX_stage.ctrl.cop_instr_en = '1' else EX_stage.reg_b; + dmem_be <= store_be(EX_stage.pa_off, EX_stage.ctrl.dmem_en, EX_stage.ctrl.word2_en, EX_stage.ctrl.word4_en, EX_stage.ctrl.align_left, EX_stage.ctrl.shift_byp) after 1 ns; + dmem_we <= EX_stage.ctrl.dmem_we; + dmem_dout <= store_shift(dmem_src, EX_stage.pa_off, EX_stage.ctrl.shift_offset, EX_stage.ctrl.shift_byp) after 1ns; + dmem_addr <= EX_stage.va; + EX_stage.events <= EX_events_instr or EX_events_mem or EX_events_alu or EX_stage.events_in; + EX_stage.exc <= event_is_active(EX_stage.events); + +-------------------------------------------------------------------------- +proc_wptr_mux: + process(EX_stage) + variable opclass : opcode_t; + variable reg_wptr : reg_ptr_t; + begin + + opclass := extract_opc(EX_stage.IR); + case opclass is + when "000000" => + reg_wptr := extract_rd(EX_stage.IR); + when others => + reg_wptr := extract_rt(EX_stage.IR); + end case; + + EX_stage.wreg_we <= EX_stage.reg_write after 1 ns; + if reg_wptr = "00000" then + EX_stage.wreg_we <= '0' after 1 ns; + end if; + + EX_stage.reg_wptr <= reg_wptr after 1 ns; + case EX_stage.ctrl.wptr_srcsel is + when wptr_src_imm => + EX_stage.reg_wptr <= reg_wptr after 1 ns; + + when wptr_src_const => + EX_stage.reg_wptr <= to_unsigned(31, reg_ptr_t'length) after 1 ns; + EX_stage.wreg_we <= '1' after 1 ns; + + when others => null; + end case; + + end process; + +-------------------------------------------------------------------------- +proc_stage_bcu_op: + process(clk_1) + begin + if rising_edge(clk_1) and sdu.EX_stall = '0' then + bcu_op_a <= ID_stage.reg_a; + bcu_op_b <= ID_stage.reg_b; + end if; + end process; + +EX_stage.alu_op1 <= EX_stage.reg_a; + +alu_op2_mux: + process(clk_1) + variable data : word_t; + begin + + if rising_edge(clk_1) and sdu.EX_stall = '0' then + data := ID_stage.reg_b; + + case ID_stage.ctrl.alu.op2_src is + + when alu_src_reg => + data := ID_stage.reg_b; + + when alu_src_imm => + data := ID_stage.imm; + + when others => null; + + end case; + + EX_stage.alu_op2 <= data; + end if; + + end process; + +shifter_sa_mux: + process(clk_1) + variable data : shamt_t; + variable data_inv : shamt_t; + + begin + if rising_edge(clk_1) and sdu.EX_stall = '0' then + data := ID_stage.reg_a(4 downto 0); + case ID_stage.ctrl.shamt2_srcsel is + + when sa_src_reg => + data := ID_stage.reg_a(4 downto 0); + + when sa_src_imm => + data := ID_stage.shamt; + + when others => null; + + end case; + data_inv := not data + 1; + if ID_stage.ctrl.alu.shift_right = '0' then + EX_stage.shift_ctrl.shamt_rnd <= data_inv after 2 ns; + else + EX_stage.shift_ctrl.shamt_rnd <= data after 2 ns; + end if; + EX_stage.shift_ctrl.shamt_nrm <= data after 1 ns; + EX_stage.shift_ctrl.shift_right <= ID_stage.ctrl.alu.shift_right; + EX_stage.shift_ctrl.shift_arith <= ID_stage.ctrl.alu.shift_arith; + end if; + + end process; + +-------------------------------------------------------------------------- +inst_shifter: shifter + GENERIC MAP + ( + data_width => word_t'length + ) + PORT MAP + ( + shift_ctrl => EX_stage.shift_ctrl, + din => EX_stage.reg_b, + dout => EX_stage.alu_op2_s + ); + +inst_alu: alu + GENERIC MAP + ( + data_width => word_t'length + ) + PORT MAP + ( + op1_in => EX_stage.alu_op1, + op2_in => EX_stage.alu_op2, + op2_shifted => EX_stage.alu_op2_s, + ctrl => EX_stage.ctrl.alu, + result => alu_result, + flags => EX_stage.alu_flags + ); + +inst_bcu: bcu + GENERIC MAP + ( + data_width => word_t'length + ) + PORT MAP + ( + op1_in => bcu_op_a, + op2_in => bcu_op_b, + flags => bcu_flags + ); + +-------------------------------------------------------------------------- +-- MEM stage +-------------------------------------------------------------------------- +proc_stage_MEM_n: + process(clk_1) + begin + if rising_edge(clk_1) then + if stage_rst(2) = '1' then + MEM_stage.op <= NOP; + MEM_stage.wreg_we <= '0'; + MEM_stage.ctrl <= ctrl_lines_default; + MEM_stage.pa_off <= (others => '0'); + MEM_stage.events_in <= events_clr; + elsif sdu.MEM_stall = '0' then + MEM_stage.events_in <= EX_stage.events; + MEM_stage.op <= EX_stage.op; + MEM_stage.wreg_we <= EX_stage.wreg_we; + MEM_stage.ctrl <= EX_stage.ctrl; + if EX_stage.ctrl.dmem_en = '1' then + MEM_stage.va <= EX_stage.va; + end if; + MEM_stage.pa_off <= EX_stage.pa_off; + MEM_stage.reg_wptr <= EX_stage.reg_wptr; + MEM_stage.nop <= sdu.MEM_nop; + if MEM_stage.reg_wptr = EX_stage.reg_wptr then + if (EX_stage.ctrl.dmem_en and MEM_stage.ctrl.dmem_en) = '1' then + MEM_stage.ex_result <= MEM_stage.data; + end if; + else + MEM_stage.ex_result <= EX_stage.reg_b; + end if; + if EX_stage.ctrl.cop_instr_en = '1' then + if EX_stage.ctrl.cop_read = '1' then + MEM_stage.ex_result <= cop_din; + end if; + elsif EX_stage.ctrl.dmem_en = '0' then + MEM_stage.ex_result <= EX_stage.result; + end if; + if sdu.MEM_nop = '1' then + MEM_stage.op <= NOP; + MEM_stage.wreg_we <= '0'; + MEM_stage.ctrl <= ctrl_lines_default; + else + MEM_stage.pcn <= EX_stage.pcn; + MEM_stage.epc <= EX_stage.epc; + end if; + end if; + end if; + end process; + +proc_stage_MEM_mux: + process(MEM_stage, dmem_din) + variable temp1 : word_t; + variable temp2 : word_t; + variable data : word_t; + variable be : unsigned(3 downto 0); + begin + data := MEM_stage.ex_result; + if MEM_stage.ctrl.reg_link = '1' then + data := MEM_stage.epc + 8; + elsif MEM_stage.ctrl.dmem_en = '1' then + temp1 := load_shift(dmem_din, MEM_stage.pa_off, MEM_stage.ctrl.shift_offset, MEM_stage.ctrl.shift_byp); + temp2 := load_sign_ext(temp1, MEM_stage.ctrl.sign_ext_byp, MEM_stage.ctrl.load_signed, MEM_stage.ctrl.word2_en, MEM_stage.ctrl.word4_en); + be := load_be(MEM_stage.pa_off, MEM_stage.ctrl.align_left, MEM_stage.ctrl.byte_en_byp); + if be(0) = '1' then + data(7 downto 0) := temp2(7 downto 0); + end if; + if be(1) = '1' then + data(15 downto 8) := temp2(15 downto 8); + end if; + if be(2) = '1' then + data(23 downto 16) := temp2(23 downto 16); + end if; + if be(3) = '1' then + data(31 downto 24) := temp2(31 downto 24); + end if; + end if; + MEM_stage.data <= data after 1 ns; + + end process; + +proc_stage_MEM_except: + process(MEM_stage, c0_ctrl_in) + begin + MEM_stage.events <= MEM_stage.events_in; + MEM_stage.events.Int <= c0_ctrl_in.int; + MEM_stage.events.NMI <= c0_ctrl_in.NMI; + end process; + + MEM_stage.exc <= event_is_active(MEM_stage.events); + +-------------------------------------------------------------------------- +-- WB stage +-------------------------------------------------------------------------- +proc_stage_WB_p: + process(clk_1) + begin + if rising_edge(clk_1) then + if stage_rst(3) = '1' then + WB_stage.op <= NOP; + WB_stage.wreg_we <= '1'; + WB_stage.reg_wptr <= (others => '0'); + WB_stage.data <= (others => '0'); + WB_stage.events <= events_clr; + elsif sdu.WB_stall = '0' then + WB_stage.op <= MEM_stage.op; + WB_stage.wreg_we <= MEM_stage.wreg_we; + WB_stage.reg_wptr <= MEM_stage.reg_wptr; + WB_stage.data <= MEM_stage.data; + WB_stage.exc <= '0'; + if sdu.stall_all = '0' then + WB_stage.exc <= MEM_stage.exc; + if MEM_stage.exc = '1' then + WB_stage.events <= MEM_stage.events; + end if; + end if; + WB_stage.nop <= sdu.WB_nop; + if sdu.WB_nop = '1' then + WB_stage.op <= NOP; + WB_stage.wreg_we <= '0'; + else + WB_stage.bd <= MEM_stage.ctrl.branch or MEM_stage.ctrl.jump or MEM_stage.ctrl.jump_long; -- Todo: works + end if; + end if; + end if; + end process; + +-------------------------------------------------------------------------- +end Behavioral;