- added
git-svn-id: http://moon:8086/svn/vhdl/trunk@1424 cc03376c-175c-47c8-b038-4cd826a8556b
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
--------------------------------------------------------------------------
|
||||
--
|
||||
-- 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;
|
||||
Reference in New Issue
Block a user