------------------------------------
-- Banc Memoire pour processeur RISC
-- THIEBOLT Francois le 08/12/04
------------------------------------

---------------------------------------------------------
-- Lors de la phase RESET, permet la lecture d'un fichier
-- passe en parametre generique.
---------------------------------------------------------

-- Definition des librairies
library IEEE;
library STD;
library WORK;

-- Definition des portee d'utilisation
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use IEEE.std_logic_unsigned.all;
use IEEE.std_logic_textio.all;
use STD.textio.all;
use WORK.cpu_package.all;


-- Definition de l'entite
entity data_cache is

  -- definition des parametres generiques
  generic (
    -- largeur du bus de donnees par defaut
    DBUS_WIDTH  : natural := 32;

    -- largeur du bus adr par defaut
    ABUS_WIDTH  : natural := 32;

    -- nombre de mots dans un bloc
    BLOC_SIZE   : natural := 16;

    -- nombre de bloc dans le cache
    CACHE_SIZE	    : natural := 1024;
    
    -- nombre de blocs par ensemble
    NB_VOIES	: natural := 4;
    
    -- front actif par defaut
    ACTIVE_FRONT : integer := 1
    );

  -- definition des entrees/sorties
  port (
    RST     : in  std_logic;    -- actifs a l'etat bas
    CLK     : in  std_logic;    -- 
    
    -- signaux de controle du cache lies cpu
    RW		: in  std_logic;
    DS      : in  MEM_DS;       -- acces octet, demi-mot, mot...
    Signed  : in  std_logic;    -- extension de signe
    AS      : in  std_logic;    -- Address Strobe (sorte de CS*)
    Ready   : out std_logic;    -- indicateur HIT/MISS

	ADR     : in  std_logic_vector(ABUS_WIDTH-1 downto 0);
    D       : in  std_logic_vector(DBUS_WIDTH-1 downto 0);
	Q       : out std_logic_vector(DBUS_WIDTH-1 downto 0);

    -- signaux de controle lies au controleur memoire
	RW_mem	: out std_logic;
    Ready_mem : in std_logic;   -- indicateur donnee valide sur bus
    Request_mem : out std_logic; -- Address Strobe
    
    -- bus d'adresse du cache
    ADR_mem : out std_logic_vector(ABUS_WIDTH-1 downto 0);
    
    -- Ports entree/sortie du cache
	D_mem   : out std_logic_vector(DBUS_WIDTH-1 downto 0);
    Q_mem   : in  std_logic_vector(DBUS_WIDTH-1 downto 0) );

end data_cache;

-- Definition de l'architecture du banc de registres
architecture behaviour of data_cache is

  -- definition de constantes
  constant BITS_FOR_BYTES : natural := 2;  --log2(DBUS_WIDTH/8); -- nb bits adr pour acceder aux octets d'un mot
  constant BITS_FOR_WORDS : natural := 4;  --log2(BLOC_SIZE);    -- nb bits adr pour acceder aux mots du cache
  constant BITS_FOR_TAGS  : natural := 8;  --log2(NB_BLOC);      -- nb bits adr pour acceder aux blocs du cache

  constant NB_BLOCS		  : natural := CACHE_SIZE / NB_VOIES;


  subtype TAG_ADR  is std_logic_vector(ABUS_WIDTH-1 downto
                                       BITS_FOR_BYTES + BITS_FOR_WORDS + BITS_FOR_TAGS);
  subtype BLOC_ADR is std_logic_vector(BITS_FOR_BYTES + BITS_FOR_WORDS + BITS_FOR_TAGS -1 downto
                                       BITS_FOR_BYTES + BITS_FOR_WORDS);
  subtype WORD_ADR is std_logic_vector(BITS_FOR_BYTES + BITS_FOR_WORDS -1 downto
                                       BITS_FOR_BYTES -1);
  subtype BYTE_ADR is std_logic_vector(BITS_FOR_BYTES + BITS_FOR_WORDS -1 downto 0);
  
  -- definitions de types (index type default is integer)
  type FILE_BLOC   is array (0 to BLOC_SIZE-1) of DATA;
  type FILE_MEM    is array (0 to NB_BLOCS-1)   of FILE_BLOC;

  type FILE_REP_A  is array (0 to NB_BLOCS-1)   of TAG_ADR;
  subtype FILE_REP_V  is std_logic_vector(0 to NB_BLOCS-1);
  type FILE_RANG	is array (0 to NB_BLOCS-1) of integer range 0 to 3; -- NB_VOIES-1
  subtype FILE_LOADING	is std_logic_vector(BLOC_SIZE-1 downto 0);
                           
  -- sous-type pour l'adressage interne des MOTS dans le cache
  subtype I_ADR is std_logic_vector(ABUS_WIDTH-1 downto 2);
  -- sous-type pour intervalle des bits dans un mot (traitement du BERR)
--  subtype I_BITrange is integer range 0 to (________)-1; -- le pire cas étant un access mot depuis le dernier octet d'un mot.

  type VOIE_t is record
	DONNEES	:	FILE_MEM;
	CLES	:	FILE_REP_A;
	VALIDE	:	FILE_REP_V;
	RANG	:	FILE_RANG;
  end record;

  signal cache is array (0 to NB_VOIES-1) of VOIE_t;


  signal loading : std_logic_vector(BLOC_SIZE-1 downto 0);

  
  -- l'adressage de la memoire se faisant par element de taile DBUS_WIDTH, par rapport
  -- au bus d'adresse au format octet il faut enlever les bits d'adresse de poids faible
  -- (octets dans le mot), puis prendre les bits utiles servant a l'acces des mots du cache.
  -- ex.: mots de 32 bits => 2 bits de poids faible pour les octets dans le mot
  --      16 mots memoire => 4 bits necessaire
  -- D'ou I_ADR = ADR (4+2-1 downto 2)
  
begin
------------------------------------------------------------------
-- Affectations dans le domaine combinatoire
-- 
  RW_mem <= RW;
  ADR_mem <=

  for i in 0 to NB_VOIES-1 loop
  	cache(i).hit_miss <= '1' when cache(i).VALIDE(conv_integer(tag))='1' and (adr_tag_in = cache(i).CLES(con_integer(tag))) and AS ='1'
					else '0';
  end loop;


  tag <= ADR(BLOC_ADR'range);
  adr_tag_in <= ADR(TAG_ADR'range);

 -- BLOC <= MEM(conv_integer(tag));
  Q <= Q_tmp(conv_integer(tag))(conv_integer(ADR(WORD_ADR'range)));
  Ready <= tag_ok or loading(conv_integer(ADR(WORD_ADR'range)));
  
------------------------------------------------------------------
-- Process P_CACHE
-- Gere tous signaux sinon il faut des variables partagés entre process
-- Les accès se faisant dans le pipeline à chaque front actif d'horloge
-- tout est cadencé par l'horloge
P_CACHE: process(CLK)

  variable sav_ADR : ADDR;
  variable tmp_Request_mem : std_logic;
  variable tmp_ADR : ADDR;
  
begin
  -- test du front actif d'horloge
  if (CLK=conv_integer_to_logic(ACTIVE_FRONT)) then -- rising_edge(CLK)
    -- test du reset
    if RST='0' then
      -- raz de la sortie
      Q <= (others => 'Z');
      Ready <= '1';
      for i in 0 to NB_VOIES-1 loop
	  	cache(i).VALIDE <= (others => '0');
		cache(i).CLES <= (others => '0');
		cache(i).RANG <= (others => '0');
	  end loop;
    elsif AS = '1' then
      if RW = 1 then
	      if (tmp_Request_mem ='0') then
	        sav_ADR := ADR;
	        tmp_ADR := ADR;
	      end if;
	      if hit_miss = conv_std_logic_vector(0, NB_VOIES) then
	        -- defaut memoire
	        tmp_Request_mem := '1';
	        ADR_mem <= tmp_ADR;
	        if (Ready_mem = '1') then
	        for i in 0 to NB_VOIES-1 loop
	          if cache(i).RANG(conv_integer(tag) = 0 then
		          cache(i).DONNEES(conv_integer(tag))(conv_integer(tmp_ADR(WORD_ADR'range))) <= Q_mem;
		          loading(conv_integer(tmp_ADR(WORD_ADR'range))) <='1';
		          tmp_ADR(WORD_ADR'range) := tmp_ADR(WORD_ADR'range) +1;
		      else
		      end if;
		          if (tmp_ADR(WORD_ADR'range)=sav_ADR(WORD_ADR'range)) then
		            -- Chargement bloc fini
		            REP_V(conv_integer(tag))<='1';   -- Validation du Bloc
	        end loop;
	            loading <= (others => '0');  -- Initialisation pour le prochain
	                                             -- chargement
	            tmp_Request_mem :='0';
	          end if;
	        end if;
	      end if;
	    else
	      Q <= (others =>'Z');
	      Ready <= '1';
	    end if;
	  else
	  end if;
  end if;
  Request_mem <= tmp_Request_mem;
end process P_CACHE;

  
end behaviour;
