Files
vhdl/projects/dlx/src/cache-behaviour.vhdl
T
jens bdb8fec295 - added
git-svn-id: http://moon:8086/svn/vhdl/trunk@1424 cc03376c-175c-47c8-b038-4cd826a8556b
2021-03-21 11:37:55 +00:00

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;