rlm@8: // The MIT License
rlm@8: 
rlm@8: // Copyright (c) 2009 Massachusetts Institute of Technology
rlm@8: 
rlm@8: // Permission is hereby granted, free of charge, to any person obtaining a copy
rlm@8: // of this software and associated documentation files (the "Software"), to deal
rlm@8: // in the Software without restriction, including without limitation the rights
rlm@8: // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
rlm@8: // copies of the Software, and to permit persons to whom the Software is
rlm@8: // furnished to do so, subject to the following conditions:
rlm@8: 
rlm@8: // The above copyright notice and this permission notice shall be included in
rlm@8: // all copies or substantial portions of the Software.
rlm@8: 
rlm@8: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
rlm@8: // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
rlm@8: // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
rlm@8: // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
rlm@8: // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
rlm@8: // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
rlm@8: // THE SOFTWARE.
rlm@8: 
rlm@8: import Connectable::*;
rlm@8: import GetPut::*;
rlm@8: import ClientServer::*;
rlm@8: import RegFile::*;
rlm@8: import FIFO::*;
rlm@8: import FIFOF::*;
rlm@8: import RWire::*;
rlm@8: 
rlm@8: // Local includes
rlm@8: `include "asim/provides/low_level_platform_interface.bsh"
rlm@8: `include "asim/provides/soft_connections.bsh"
rlm@8: `include "asim/provides/processor_library.bsh"
rlm@8: `include "asim/provides/fpga_components.bsh"
rlm@8: `include "asim/provides/common_services.bsh"
rlm@8: `include "asim/dict/STATS_INST_CACHE.bsh"
rlm@8: 
rlm@8: interface ICache#( type req_t, type resp_t );
rlm@8: 
rlm@8:   // Interface from processor to cache
rlm@8:   interface Server#(req_t,resp_t) proc_server;
rlm@8: 
rlm@8:   // Interface from cache to main memory
rlm@8:   interface Client#(MainMemReq,MainMemResp) mmem_client;
rlm@8: 
rlm@8:   // Interface for enabling/disabling statistics
rlm@8:   interface Put#(Bool) statsEn_put;
rlm@8: 
rlm@8: endinterface
rlm@8: 
rlm@8: //----------------------------------------------------------------------
rlm@8: // Cache Types
rlm@8: //----------------------------------------------------------------------
rlm@8: 
rlm@8: typedef 10 CacheLineIndexSz;
rlm@8: typedef 20 CacheLineTagSz;
rlm@8: typedef 32 CacheLineSz;
rlm@8: 
rlm@8: typedef Bit#(CacheLineIndexSz) CacheLineIndex;
rlm@8: typedef Bit#(CacheLineTagSz)   CacheLineTag;
rlm@8: typedef Bit#(CacheLineSz)      CacheLine;
rlm@8: 
rlm@8: typedef enum 
rlm@8: { 
rlm@8:   Init,
rlm@8:   Access, 
rlm@8:   Evict, 
rlm@8:   RefillReq, 
rlm@8:   RefillResp 
rlm@8: } 
rlm@8: CacheStage 
rlm@8: deriving (Eq,Bits);
rlm@8: 
rlm@8: //----------------------------------------------------------------------
rlm@8: // Helper functions
rlm@8: //----------------------------------------------------------------------
rlm@8: 
rlm@8: function Bit#(AddrSz) getAddr( InstReq req );
rlm@8: 
rlm@8:   Bit#(AddrSz) addr = ?;
rlm@8:   case ( req ) matches
rlm@8:     tagged LoadReq  .ld : addr = ld.addr;
rlm@8:     tagged StoreReq .st : addr = st.addr;
rlm@8:   endcase
rlm@8: 
rlm@8:   return addr;
rlm@8: 
rlm@8: endfunction
rlm@8: 
rlm@8: function CacheLineIndex getCacheLineIndex( InstReq req );
rlm@8:   Bit#(AddrSz) addr = getAddr(req);
rlm@8:   Bit#(CacheLineIndexSz) index = truncate( addr >> 2 );
rlm@8:   return index;
rlm@8: endfunction
rlm@8: 
rlm@8: function CacheLineTag getCacheLineTag( InstReq req );
rlm@8:   Bit#(AddrSz)         addr = getAddr(req);
rlm@8:   Bit#(CacheLineTagSz) tag  = truncate( addr >> fromInteger(valueOf(CacheLineIndexSz)) >> 2 );
rlm@8:   return tag;
rlm@8: endfunction
rlm@8: 
rlm@8: function Bit#(AddrSz) getCacheLineAddr( InstReq req );
rlm@8:   Bit#(AddrSz) addr = getAddr(req);
rlm@8:   return ((addr >> 2) << 2);
rlm@8: endfunction
rlm@8: 
rlm@8: //----------------------------------------------------------------------
rlm@8: // Main module
rlm@8: //----------------------------------------------------------------------
rlm@8: 
rlm@8: module [CONNECTED_MODULE] mkInstCache( ICache#(InstReq,InstResp) );
rlm@8: 
rlm@8:   //-----------------------------------------------------------
rlm@8:   // State
rlm@8: 
rlm@8:   Reg#(CacheStage) stage <- mkReg(Init);
rlm@8: 
rlm@8:   LUTRAM#(CacheLineIndex,Maybe#(CacheLineTag)) cacheTagRam  <- mkLUTRAMU_RegFile();
rlm@8:   LUTRAM#(CacheLineIndex,CacheLine)            cacheDataRam <- mkLUTRAMU_RegFile();
rlm@8: 
rlm@8:   FIFO#(InstReq)   reqQ  <- mkFIFO();
rlm@8:   FIFOF#(InstResp) respQ <- mkBFIFOF1();
rlm@8: 
rlm@8:   FIFO#(MainMemReq)  mainMemReqQ  <- mkBFIFO1();
rlm@8:   FIFO#(MainMemResp) mainMemRespQ <- mkFIFO();
rlm@8: 
rlm@8:   Reg#(CacheLineIndex) initCounter <- mkReg(1);
rlm@8: 
rlm@8:   // Statistics state
rlm@8: 
rlm@8:   Reg#(Bool)     statsEn        <- mkReg(False);
rlm@8: 
rlm@8:   STAT num_accesses <- mkStatCounter(`STATS_INST_CACHE_NUM_ACCESSES);
rlm@8:   STAT num_misses <- mkStatCounter(`STATS_INST_CACHE_NUM_MISSES);
rlm@8:   STAT num_evictions <- mkStatCounter(`STATS_INST_CACHE_NUM_EVICTIONS);
rlm@8: 
rlm@8:   //-----------------------------------------------------------
rlm@8:   // Name some wires
rlm@8: 
rlm@8:   let req              = reqQ.first();
rlm@8:   let reqIndex         = getCacheLineIndex(req);
rlm@8:   let reqTag           = getCacheLineTag(req);
rlm@8:   let reqCacheLineAddr = getCacheLineAddr(req);
rlm@8:   let refill           = mainMemRespQ.first();
rlm@8: 
rlm@8:   //-----------------------------------------------------------
rlm@8:   // Initialize
rlm@8: 
rlm@8:   rule init ( stage == Init );
rlm@8:     traceTiny("mkInstCacheBlocking", "stage","i");
rlm@8:     initCounter <= initCounter + 1;
rlm@8:     cacheTagRam.upd(initCounter,Invalid);
rlm@8:     if ( initCounter == 0 )
rlm@8:       stage <= Access;
rlm@8:   endrule
rlm@8: 
rlm@8:   //-----------------------------------------------------------
rlm@8:   // Cache access rule
rlm@8: 
rlm@8:   rule access ( (stage == Access) && respQ.notFull() );
rlm@8: 
rlm@8:     // Statistics
rlm@8: 
rlm@8:     if ( statsEn )
rlm@8:       num_accesses.incr();
rlm@8:   
rlm@8:     // Check tag and valid bit to see if this is a hit or a miss
rlm@8: 
rlm@8:     Maybe#(CacheLineTag) cacheLineTag = cacheTagRam.sub(reqIndex);
rlm@8:   
rlm@8:     // Handle cache hits ...
rlm@8: 
rlm@8:     if ( isValid(cacheLineTag) && ( unJust(cacheLineTag) == reqTag ) )
rlm@8:      begin
rlm@8:        traceTiny("mkInstCacheBlocking", "hitMiss","h");
rlm@8:        reqQ.deq();
rlm@8: 
rlm@8:        case ( req ) matches
rlm@8:           
rlm@8:          tagged LoadReq .ld :
rlm@8: 	  respQ.enq( LoadResp { tag  : ld.tag, data : cacheDataRam.sub(reqIndex) } );
rlm@8: 
rlm@8:          tagged StoreReq .st :
rlm@8:           $display( " RTL-ERROR : %m : Stores are not allowed on the inst port!" );
rlm@8:        
rlm@8:        endcase
rlm@8: 
rlm@8:      end
rlm@8: 
rlm@8:     // Handle cache misses - since lines in instruction cache are
rlm@8:     // never dirty we can always immediately issue a refill request
rlm@8:     
rlm@8:     else 
rlm@8:      begin
rlm@8:        traceTiny("mkInstCacheBlocking", "hitMiss","m");
rlm@8:        if ( statsEn )
rlm@8:          num_misses.incr();
rlm@8:        if ( statsEn )
rlm@8:          if ( isJust(cacheLineTag) )
rlm@8: 	   num_evictions.incr();	
rlm@8: 
rlm@8:        MainMemReq rfReq
rlm@8:         = LoadReq { tag  : 0,
rlm@8: 	            addr : reqCacheLineAddr };
rlm@8: 
rlm@8:        mainMemReqQ.enq(rfReq);
rlm@8:        stage <= RefillResp;    
rlm@8:      end
rlm@8: 
rlm@8:   endrule
rlm@8: 
rlm@8:   //-----------------------------------------------------------
rlm@8:   // Refill response rule
rlm@8:   
rlm@8:    rule refillResp ( stage == RefillResp );
rlm@8:     traceTiny("mkInstCacheBlocking", "stage","R");
rlm@8:     traceTiny("mkInstCacheBlocking", "refill",refill);
rlm@8: 
rlm@8:     // Write the new data into the cache and update the tag
rlm@8: 
rlm@8:     mainMemRespQ.deq();
rlm@8:     case ( mainMemRespQ.first() ) matches
rlm@8: 
rlm@8:       tagged LoadResp .ld :
rlm@8:        begin
rlm@8: 	 cacheTagRam.upd(reqIndex,Valid(reqTag));
rlm@8: 	 cacheDataRam.upd(reqIndex,ld.data);	
rlm@8:        end
rlm@8:        
rlm@8:       tagged StoreResp .st :
rlm@8: 	noAction;
rlm@8:      
rlm@8:     endcase
rlm@8: 
rlm@8:     stage <= Access;
rlm@8:   endrule
rlm@8: 
rlm@8:   //-----------------------------------------------------------
rlm@8:   // Methods
rlm@8: 
rlm@8:   interface Client mmem_client;
rlm@8:     interface Get request  = fifoToGet(mainMemReqQ);
rlm@8:     interface Put response = fifoToPut(mainMemRespQ);
rlm@8:   endinterface
rlm@8: 
rlm@8:   interface Server proc_server;
rlm@8:     interface Put request  = tracePut("mkInstCacheBlocking", "reqTiny",fifoToPut(reqQ));
rlm@8:     interface Get response = traceGet("mkInstCacheBlocking", "respTiny",fifofToGet(respQ));
rlm@8:   endinterface
rlm@8: 
rlm@8:   interface Put statsEn_put = regToPut(statsEn);
rlm@8: 
rlm@8: endmodule
rlm@8: