-- Model Name : Utility Modeling - Cache -- Author : Armita Peymandoust -- Last Updated : 09 / 15 / 1996 -- This document is © copyrighted by the Author.

    
    -- Cache version 2.0
    
    -- This is a completely configurable cache in the sense of cache line and
    -- cache size
    -- 1, 2, or 4-way set associative
    -- Pseudo LRU replacement technique is used
    
    LIBRARY IEEE;
    USE IEEE.std_logic_1164.ALL;
    
    LIBRARY WORK;
    USE WORK.cache_pack.ALL;
    
    ENTITY cache IS
      
      GENERIC ( NoOfAddressBits : POSITIVE := 12;  -- physical memory's size
                Log2LineSize    : POSITIVE := 2;  -- by byte 
                Associativity      : POSITIVE := 2 ;
                NoOfCacheAddressBits: POSITIVE := 8  -- this is the size of each associativity
              );
    
      PORT ( AddressIn  : IN std_logic_vector(NoOfAddressBits-1 DOWNTO 0):=(others=>'Z');
             AddressOut : OUT std_logic_vector(NoOfAddressBits-1 DOWNTO 0):=(others=>'Z');
             ReadIn     : IN std_logic := '0';
             ReadOut    : OUT std_logic := '0';
             WriteIn    : IN std_logic := '0';
             WriteOut   : OUT std_logic := '0';
             DataIn     : INOUT std_logic_vector(7 DOWNTO 0):=(OTHERS=>'Z');
             DataOut    : INOUT std_logic_vector(7 DOWNTO 0):=(OTHERS=>'Z');
             DumpIn     : IN std_logic := '0';
             DumpOut    : OUT std_logic := '0';
             ready      : OUT std_logic := '0'
           );
    
      CONSTANT TagSize : NATURAL := NoOfAddressBits - Log2LineSize - NoOfCacheAddressBits;
    
      SUBTYPE byte IS std_logic_vector(7 DOWNTO 0);
      TYPE LineType IS ARRAY (0 TO (2**Log2LineSize)-1) OF byte;
      TYPE LineRec IS RECORD
        tag   : std_logic_vector(TagSize-1 DOWNTO 0);
        bytes : LineType;
      --  lru   : INTEGER;
        dirty : BOOLEAN;
        valid : BOOLEAN;
      END RECORD;
    
      SUBTYPE bintree IS std_logic_vector(afunc(Associativity) DOWNTO 0);
      Type PseudoLru IS ARRAY (0 TO (2**NoOfCacheAddressBits)-1) OF bintree;
      
      TYPE CacheType IS ARRAY (1 TO Associativity, 0 TO (2**NoOfCacheAddressBits)-1) OF LineRec; --EachAssociativity;
     
    END cache;
    
    ARCHITECTURE general OF cache IS
        
    BEGIN
      
      PROCESS 
    
        VARIABLE TheCache   : CacheType;
        VARIABLE hit        : BOOLEAN := FALSE;                        
        VARIABLE WhichSet   : INTEGER := 0;
        VARIABLE GotALine,GetALine   : LineType;
        VARIABLE LineAddress : INTEGER;
        VARIABLE AddressTag  : std_logic_vector(TagSize-1 DOWNTO 0);
        VARIABLE LineCnt     : std_logic_vector(Log2LineSize-1 DOWNTO 0);
        VARIABLE TheLruTree  : PseudoLru;
        
        PROCEDURE UpdatePseudoLru(UsedSet : IN INTEGER) IS -- THIS NEEDS RIGHT INITIAL VALUES TO WORK!!!!
        BEGIN      
          IF (Associativity=2) THEN
              TheLruTree(LineAddress) := NOT TheLruTree(LineAddress);
          ELSIF (Associativity=4) THEN
            CASE UsedSet IS
              WHEN 0 => 
                TheLruTree(LineAddress) := TheLruTree(LineAddress) OR "110";
              WHEN 1 => 
                TheLruTree(LineAddress) := TheLruTree(LineAddress) OR "100";
                TheLruTree(LineAddress) := TheLruTree(LineAddress) AND "101";
              WHEN 2 => 
                TheLruTree(LineAddress) := TheLruTree(LineAddress) OR "001";
                TheLruTree(LineAddress) := TheLruTree(LineAddress) AND "011";
              WHEN 3 => 
                TheLruTree(LineAddress) := TheLruTree(LineAddress) AND "010";
              WHEN OTHERS=> NULL;
            END CASE;
          END IF;    
        END UpdatePseudoLru;
    
        PROCEDURE ChooseASet(ASet : OUT INTEGER) IS
          VARIABLE temp : bintree := TheLruTree(LineAddress);
        BEGIN
          IF Associativity=2 THEN 
            IF temp = "0" THEN ASet := 1; 
            ELSIF temp ="1" THEN ASet := 0;
            END IF;
          ELSIF Associativity=4 THEN 
            IF temp ="000" THEN ASet := 0; 
            ELSIF temp ="001" THEN ASet := 0;
            ELSIF temp ="010" THEN ASet := 1;
            ELSIF temp ="011" THEN ASet := 1;
            ELSIF temp ="100" THEN ASet := 2;
            ELSIF temp ="101" THEN ASet := 3;
            ELSIF temp ="110" THEN ASet := 2;
            ELSIF temp ="111" THEN ASet := 3;
            END IF;
          END IF;
        END ChooseASet;
       
      BEGIN
        WAIT ON AddressIn, ReadIn, WriteIn, DumpIn;   
        ready <= '0';
                 
        IF DumpIn = '0' THEN
          IF AddressIn'Event THEN
            LineAddress := std_logic2int(AddressIn(NoOfCacheAddressBits+Log2LineSize-1 DOWNTO Log2LineSize));
            AddressTag := AddressIn(NoOfAddressBits-1 DOWNTO NoOfCacheAddressBits+Log2LineSize);
            hit := FALSE:
          END IF;
    
          FOR i IN 1 to Associativity LOOP
            IF (TheCache(i, LineAddress).tag = AddressTag AND TheCache(i, LineAddress).valid) THEN
              WhichSet := i;
              IF Associativity>1 THEN 
                UpdatePseudoLru(i); 
              END IF;
              hit := TRUE;
              ready <='1';
              EXIT;
            END IF;
          END LOOP;
    
          IF (NOT hit) THEN  -- In case of a miss
            FOR i IN 0 TO (2**Log2LineSize)-1 LOOP
              int2std_logic(i, LineCnt);
              AddressOut <= AddressIn(NoOfAddressBits-1 DOWNTO Log2LineSize) & LineCnt;
              ReadOut <= '1', '0' AFTER 1 NS;  
              WAIT ON DataOut;    --OR WAIT UNTIL DATAOUT/="Zs"
              GotAline(i) := DataOut;
            END LOOP;
    
            IF Associativity>1 THEN 
              ChooseASet(WhichSet); 
            ELSE
              WhichSet := 0;
            END IF;
    
          --  write back if dirty
            IF TheCache(WhichSet, LineAddress).dirty THEN
              GetAline := TheCache(WhichSet, LineAddress).bytes;
              FOR i IN 0 TO (2**Log2LineSize)-1 LOOP
                int2std_logic(i, LineCnt);
                AddressOut <= AddressIn(NoOfAddressBits-1 DOWNTO Log2LineSize) & LineCnt;
                DataOut <= GetALine(i);
                WriteOut <= '1', '0' AFTER 1 NS;
                WAIT FOR 2 NS;  
              END LOOP;
            END IF;
    
            TheCache(WhichSet, LineAddress).bytes := GotALine;
            TheCache(WhichSet, LineAddress).valid := TRUE;
            IF Associativity>1 THEN 
              UpdatePseudoLru(WhichSet);  
            END IF;
            
            ready <= '1';
          END IF;
    
          IF ReadIn = '1' THEN
            GotALine := TheCache(WhichSet, LineAddress).bytes;
            DataIn <= GotAline(std_logic2int(AddressIn(Log2LineSize-1 DOWNTO 0))); 
          ELSIF WriteIn = '1' THEN
            GotALine := TheCache(WhichSet, LineAddress).bytes;    
            GotAline(std_logic2int(AddressIn(Log2LineSize-1 DOWNTO 0))) := DataIn;
            TheCache(WhichSet, LineAddress).bytes := GotALine;
            TheCache(WhichSet, LineAddress).dirty := TRUE;
          END IF;
        ELSE  -- write back all the dirty lines and assert DumpOut 
          FOR i IN 1 to Associativity LOOP  
            FOR j IN 0 TO (2**Log2LineSize)-1 LOOP
              IF TheCache(i, j).dirty THEN
                GetAline := TheCache(i, j).bytes;
                FOR i IN 0 TO (2**Log2LineSize)-1 LOOP
                  int2std_logic(i, LineCnt);
                  AddressOut <= AddressIn(NoOfAddressBits-1 DOWNTO Log2LineSize) & LineCnt;
                  DataOut <= GetALine(i);
                  WriteOut <= '1', '0' AFTER 1 NS;
                  WAIT FOR 1 NS;  
                END LOOP;
              END IF;
            END LOOP;
          END LOOP;
        END IF;
      END PROCESS;
    
    END general;