git-svn-id: http://moon:8086/svn/vhdl/trunk@1424 cc03376c-175c-47c8-b038-4cd826a8556b
311 lines
10 KiB
VHDL
311 lines
10 KiB
VHDL
--------------------------------------------------------------------------
|
|
--
|
|
-- Copyright (C) 1993, Peter J. Ashenden
|
|
-- Mail: Dept. Computer Science
|
|
-- University of Adelaide, SA 5005, Australia
|
|
-- e-mail: petera@cs.adelaide.edu.au
|
|
--
|
|
-- 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 1, 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, write to the Free Software
|
|
-- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
--
|
|
--------------------------------------------------------------------------
|
|
--
|
|
-- $RCSfile: cache-behaviour.vhdl,v $ $Revision: 2.1 $ $Date: 1993/11/02 22:13:32 $
|
|
--
|
|
--------------------------------------------------------------------------
|
|
--
|
|
-- Behavioural architecture for cache.
|
|
--
|
|
|
|
|
|
use work.bv_arithmetic.bv_to_natural,
|
|
work.bv_arithmetic.natural_to_bv;
|
|
|
|
|
|
architecture behaviour of cache is
|
|
|
|
begin -- behaviour
|
|
|
|
cache_behaviour : process
|
|
|
|
constant words_per_line : positive := line_size / 4;
|
|
constant number_of_sets : positive := cache_size / line_size / associativity;
|
|
|
|
subtype word_offset_range is natural range 0 to words_per_line-1;
|
|
subtype entry_index_range is natural range 0 to associativity-1;
|
|
subtype set_index_range is natural range 0 to number_of_sets-1;
|
|
|
|
type line is array (word_offset_range) of dlx_word;
|
|
|
|
type entry is record
|
|
tag : natural;
|
|
valid : boolean;
|
|
dirty : boolean;
|
|
data : line;
|
|
end record;
|
|
|
|
type store_array is array (set_index_range, entry_index_range) of entry;
|
|
|
|
variable store : store_array;
|
|
variable cpu_address : natural;
|
|
variable word_offset : word_offset_range;
|
|
variable set_index : set_index_range;
|
|
variable cpu_tag : natural;
|
|
variable entry_index : entry_index_range;
|
|
variable hit : boolean;
|
|
variable next_replacement_entry_index : entry_index_range := 0;
|
|
|
|
|
|
procedure do_read_hit is
|
|
begin
|
|
cpu_d <= store(set_index, entry_index).data(word_offset);
|
|
cpu_ready <= '1' after Tpd_clk_out;
|
|
wait until phi2 = '0';
|
|
cpu_d <= null after Tpd_clk_out;
|
|
cpu_ready <= '0' after Tpd_clk_out;
|
|
end do_read_hit;
|
|
|
|
|
|
procedure do_write_through is
|
|
begin
|
|
wait until phi1 = '1';
|
|
if reset = '1' then
|
|
return;
|
|
end if;
|
|
mem_a <= cpu_a after Tpd_clk_out;
|
|
mem_width <= cpu_width after Tpd_clk_out;
|
|
mem_d <= cpu_d after Tpd_clk_out;
|
|
mem_write <= '1' after Tpd_clk_out;
|
|
mem_burst <= '0' after Tpd_clk_out;
|
|
mem_enable <= '1' after Tpd_clk_out;
|
|
wait until mem_ready = '1' or reset = '1';
|
|
cpu_ready <= mem_ready after Tpd_clk_out;
|
|
wait until phi2 = '0';
|
|
mem_d <= null after Tpd_clk_out;
|
|
mem_write <= '0' after Tpd_clk_out;
|
|
mem_enable <= '0' after Tpd_clk_out;
|
|
cpu_ready <= '0' after Tpd_clk_out;
|
|
end do_write_through;
|
|
|
|
|
|
procedure do_write_hit is
|
|
begin
|
|
case cpu_width is
|
|
when width_word =>
|
|
store(set_index, entry_index).data(word_offset) := cpu_d;
|
|
when width_halfword =>
|
|
if cpu_a(1) = '0' then -- ms half word
|
|
store(set_index, entry_index).data(word_offset)(0 to 15) := cpu_d(0 to 15);
|
|
else -- ls half word
|
|
store(set_index, entry_index).data(word_offset)(16 to 23) := cpu_d(16 to 23);
|
|
end if;
|
|
when width_byte =>
|
|
if cpu_a(1) = '0' then -- ms half word
|
|
if cpu_a(0) = '0' then -- byte 0
|
|
store(set_index, entry_index).data(word_offset)(0 to 7) := cpu_d(0 to 7);
|
|
else -- byte 1
|
|
store(set_index, entry_index).data(word_offset)(8 to 15) := cpu_d(8 to 15);
|
|
end if;
|
|
else -- ls half word
|
|
if cpu_a(0) = '0' then -- byte 2
|
|
store(set_index, entry_index).data(word_offset)(16 to 23) := cpu_d(16 to 23);
|
|
else -- byte 3
|
|
store(set_index, entry_index).data(word_offset)(24 to 31) := cpu_d(24 to 31);
|
|
end if;
|
|
end if;
|
|
end case;
|
|
if write_strategy = copy_back then
|
|
store(set_index, entry_index).dirty := true;
|
|
end if;
|
|
--
|
|
-- if write_through cache, also update main memory
|
|
if write_strategy = write_through then
|
|
do_write_through;
|
|
else -- copy_back cache
|
|
cpu_ready <= '1' after Tpd_clk_out;
|
|
wait until phi2 = '0';
|
|
cpu_ready <= '0' after Tpd_clk_out;
|
|
end if;
|
|
end do_write_hit;
|
|
|
|
|
|
procedure copy_back_line is
|
|
variable next_address : natural;
|
|
variable old_word_offset : natural;
|
|
begin
|
|
next_address := (store(set_index, entry_index).tag * number_of_sets
|
|
+ set_index) * line_size;
|
|
wait until phi1 = '1';
|
|
if reset = '1' then
|
|
return;
|
|
end if;
|
|
mem_width <= width_word after Tpd_clk_out;
|
|
mem_write <= '1' after Tpd_clk_out;
|
|
mem_enable <= '1' after Tpd_clk_out;
|
|
mem_burst <= '1' after Tpd_clk_out;
|
|
old_word_offset := 0;
|
|
burst_loop : loop
|
|
if old_word_offset = words_per_line-1 then
|
|
mem_burst <= '0' after Tpd_clk_out;
|
|
end if;
|
|
mem_a <= natural_to_bv(next_address, mem_a'length) after Tpd_clk_out;
|
|
mem_d <= store(set_index, entry_index).data(old_word_offset) after Tpd_clk_out;
|
|
wait_loop : loop
|
|
wait until phi2 = '0';
|
|
exit burst_loop when reset = '1'
|
|
or (mem_ready = '1' and old_word_offset = words_per_line-1);
|
|
exit wait_loop when mem_ready = '1';
|
|
end loop wait_loop;
|
|
old_word_offset := old_word_offset + 1;
|
|
next_address := next_address + 4;
|
|
end loop burst_loop;
|
|
store(set_index, entry_index).dirty := false;
|
|
mem_d <= null after Tpd_clk_out;
|
|
mem_write <= '0' after Tpd_clk_out;
|
|
mem_enable <= '0' after Tpd_clk_out;
|
|
end copy_back_line;
|
|
|
|
|
|
procedure fetch_line is
|
|
variable next_address : natural;
|
|
variable new_word_offset : natural;
|
|
begin
|
|
next_address := (cpu_address / line_size) * line_size;
|
|
wait until phi1 = '1';
|
|
if reset = '1' then
|
|
return;
|
|
end if;
|
|
mem_width <= width_word after Tpd_clk_out;
|
|
mem_write <= '0' after Tpd_clk_out;
|
|
mem_enable <= '1' after Tpd_clk_out;
|
|
mem_burst <= '1' after Tpd_clk_out;
|
|
new_word_offset := 0;
|
|
burst_loop : loop
|
|
if new_word_offset = words_per_line-1 then
|
|
mem_burst <= '0' after Tpd_clk_out;
|
|
end if;
|
|
mem_a <= natural_to_bv(next_address, mem_a'length) after Tpd_clk_out;
|
|
wait_loop : loop
|
|
wait until phi2 = '0';
|
|
store(set_index, entry_index).data(new_word_offset) := mem_d;
|
|
exit burst_loop when reset = '1'
|
|
or (mem_ready = '1' and new_word_offset = words_per_line-1);
|
|
exit wait_loop when mem_ready = '1';
|
|
end loop wait_loop;
|
|
new_word_offset := new_word_offset + 1;
|
|
next_address := next_address + 4;
|
|
end loop burst_loop;
|
|
store(set_index, entry_index).valid := true;
|
|
store(set_index, entry_index).tag := cpu_tag;
|
|
store(set_index, entry_index).dirty := false;
|
|
mem_enable <= '0' after Tpd_clk_out;
|
|
end fetch_line;
|
|
|
|
|
|
procedure replace_line is
|
|
begin
|
|
-- first chose an entry using "random" number generator
|
|
entry_index := next_replacement_entry_index;
|
|
next_replacement_entry_index
|
|
:= (next_replacement_entry_index + 1) mod associativity;
|
|
if store(set_index, entry_index).dirty then
|
|
copy_back_line;
|
|
end if;
|
|
fetch_line;
|
|
end replace_line;
|
|
|
|
|
|
procedure do_read_miss is
|
|
begin
|
|
replace_line;
|
|
if reset = '1' then
|
|
return;
|
|
end if;
|
|
do_read_hit;
|
|
end do_read_miss;
|
|
|
|
|
|
procedure do_write_miss is
|
|
begin
|
|
-- if write_through cache, just update main memory
|
|
if write_strategy = write_through then
|
|
do_write_through;
|
|
else -- copy_back cache
|
|
replace_line;
|
|
if reset = '1' then
|
|
return;
|
|
end if;
|
|
do_write_hit;
|
|
end if;
|
|
end do_write_miss;
|
|
|
|
|
|
begin -- process cache_behaviour
|
|
-- reset: initialize outputs and the cache store valid bits
|
|
cpu_ready <= '0';
|
|
cpu_d <= null;
|
|
mem_enable <= '0';
|
|
mem_width <= width_word;
|
|
mem_write <= '0';
|
|
mem_burst <= '0';
|
|
mem_a <= X"00000000";
|
|
mem_d <= null;
|
|
for init_set_index in set_index_range loop
|
|
for init_entry_index in entry_index_range loop
|
|
store(init_set_index, init_entry_index).valid := false;
|
|
store(init_set_index, init_entry_index).dirty := false;
|
|
end loop; -- init_entry_index
|
|
end loop; -- init_set_index
|
|
--
|
|
loop
|
|
-- wait for a cpu request
|
|
wait until phi2 = '1' and cpu_enable = '1';
|
|
-- decode address
|
|
cpu_address := bv_to_natural(cpu_a);
|
|
word_offset := (cpu_address mod line_size) / 4;
|
|
set_index := (cpu_address / line_size) mod number_of_sets;
|
|
cpu_tag := cpu_address / line_size / number_of_sets;
|
|
-- check for hit
|
|
hit := false;
|
|
for lookup_entry_index in entry_index_range loop
|
|
if store(set_index, lookup_entry_index).valid
|
|
and store(set_index, lookup_entry_index).tag = cpu_tag then
|
|
hit := true;
|
|
entry_index := lookup_entry_index;
|
|
exit;
|
|
end if;
|
|
end loop; -- lookup_entry
|
|
--
|
|
if hit then
|
|
if cpu_write = '1' then
|
|
do_write_hit;
|
|
else
|
|
do_read_hit;
|
|
end if;
|
|
else
|
|
if cpu_write = '1' then
|
|
do_write_miss;
|
|
else
|
|
do_read_miss;
|
|
end if;
|
|
end if;
|
|
exit when reset = '1';
|
|
end loop;
|
|
-- loop exited on reset: wait until it goes inactive
|
|
-- then start again
|
|
wait until phi2 = '0' and reset = '0';
|
|
end process cache_behaviour;
|
|
|
|
end behaviour;
|