Initial revision
Committed on the Free edition of March Hare Software CVSNT Server. Upgrade to CVS Suite for more features and support: http://march-hare.com/cvsnt/ git-svn-id: http://moon:8086/svn/vhdl/branches/BRANCH_MIPS_TLB@1002 cc03376c-175c-47c8-b038-4cd826a8556b
This commit is contained in:
@@ -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;
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
--
|
||||
-- For questions and ideas, please contact the author at jens@jayfield.org
|
||||
--
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
library IEEE;
|
||||
use IEEE.STD_LOGIC_1164.ALL;
|
||||
use IEEE.NUMERIC_STD.ALL;
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
--
|
||||
-- For questions and ideas, please contact the author at jens@jayfield.org
|
||||
--
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
library IEEE;
|
||||
use IEEE.STD_LOGIC_1164.ALL;
|
||||
use IEEE.NUMERIC_STD.ALL;
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user