punk@1: // The MIT License punk@1: punk@1: // Copyright (c) 2009 Massachusetts Institute of Technology punk@1: punk@1: // Permission is hereby granted, free of charge, to any person obtaining a copy punk@1: // of this software and associated documentation files (the "Software"), to deal punk@1: // in the Software without restriction, including without limitation the rights punk@1: // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell punk@1: // copies of the Software, and to permit persons to whom the Software is punk@1: // furnished to do so, subject to the following conditions: punk@1: punk@1: // The above copyright notice and this permission notice shall be included in punk@1: // all copies or substantial portions of the Software. punk@1: punk@1: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR punk@1: // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, punk@1: // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE punk@1: // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER punk@1: // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, punk@1: // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN punk@1: // THE SOFTWARE. punk@1: punk@1: import Connectable::*; punk@1: import GetPut::*; punk@1: import ClientServer::*; punk@1: import RegFile::*; punk@1: import FIFO::*; punk@1: import FIFOF::*; punk@1: punk@1: import BFIFO::*; punk@1: import MemTypes::*; punk@1: import ProcTypes::*; punk@1: import Trace::*; punk@1: punk@1: interface DCacheStats; punk@1: interface Get#(Stat) num_accesses; punk@1: interface Get#(Stat) num_misses; punk@1: interface Get#(Stat) num_writebacks; punk@1: endinterface punk@1: punk@1: interface DCache#( type req_t, type resp_t ); punk@1: punk@1: // Interface from processor to cache punk@1: interface Server#(req_t,resp_t) proc_server; punk@1: punk@1: // Interface from cache to main memory punk@1: interface Client#(MainMemReq,MainMemResp) mmem_client; punk@1: punk@1: // Interface for enabling/disabling statistics punk@1: interface Put#(Bool) statsEn_put; punk@1: punk@1: // Interface for collecting statistics punk@1: interface DCacheStats stats; punk@1: punk@1: endinterface punk@1: punk@1: punk@1: //---------------------------------------------------------------------- punk@1: // Cache Types punk@1: //---------------------------------------------------------------------- punk@1: punk@1: typedef 10 CacheLineIndexSz; punk@1: typedef 20 CacheLineTagSz; punk@1: typedef 32 CacheLineSz; punk@1: punk@1: typedef Bit#(CacheLineIndexSz) CacheLineIndex; punk@1: typedef Bit#(CacheLineTagSz) CacheLineTag; punk@1: typedef Bit#(CacheLineSz) CacheLine; punk@1: punk@1: typedef enum punk@1: { punk@1: Init, punk@1: Access, punk@1: RefillReq, punk@1: RefillResp punk@1: } punk@1: CacheStage punk@1: deriving (Eq,Bits); punk@1: punk@1: //---------------------------------------------------------------------- punk@1: // Helper functions punk@1: //---------------------------------------------------------------------- punk@1: punk@1: function Bit#(AddrSz) getAddr( DataReq req ); punk@1: punk@1: Bit#(AddrSz) addr = ?; punk@1: case ( req ) matches punk@1: tagged LoadReq .ld : addr = ld.addr; punk@1: tagged StoreReq .st : addr = st.addr; punk@1: endcase punk@1: punk@1: return addr; punk@1: punk@1: endfunction punk@1: punk@1: function CacheLineIndex getCacheLineIndex( DataReq req ); punk@1: Bit#(AddrSz) addr = getAddr(req); punk@1: Bit#(CacheLineIndexSz) index = truncate( addr >> 2 ); punk@1: return index; punk@1: endfunction punk@1: punk@1: function CacheLineTag getCacheLineTag( DataReq req ); punk@1: Bit#(AddrSz) addr = getAddr(req); punk@1: Bit#(CacheLineTagSz) tag = truncate( addr >> fromInteger(valueOf(CacheLineIndexSz)) >> 2 ); punk@1: return tag; punk@1: endfunction punk@1: punk@1: function Bit#(AddrSz) getCacheLineAddr( DataReq req ); punk@1: Bit#(AddrSz) addr = getAddr(req); punk@1: return ((addr >> 2) << 2); punk@1: endfunction punk@1: punk@1: //---------------------------------------------------------------------- punk@1: // Main module punk@1: //---------------------------------------------------------------------- punk@1: punk@1: (* doc = "synthesis attribute ram_style mkDataCache distributed;" *) punk@1: (* synthesize *) punk@1: module mkDataCache( DCache#(DataReq,DataResp) ); punk@1: punk@1: //----------------------------------------------------------- punk@1: // State punk@1: punk@1: Reg#(CacheStage) stage <- mkReg(Init); punk@1: punk@1: RegFile#(CacheLineIndex,Maybe#(CacheLineTag)) cacheTagRam <- mkRegFileFull(); punk@1: RegFile#(CacheLineIndex,CacheLine) cacheDataRam <- mkRegFileFull(); punk@1: punk@1: FIFO#(DataReq) reqQ <- mkFIFO(); punk@1: FIFOF#(DataResp) respQ <- mkBFIFOF1(); punk@1: punk@1: FIFO#(MainMemReq) mainMemReqQ <- mkBFIFO1(); punk@1: FIFO#(MainMemResp) mainMemRespQ <- mkFIFO(); punk@1: punk@1: Reg#(CacheLineIndex) initCounter <- mkReg(1); punk@1: punk@1: // Statistics state punk@1: punk@1: Reg#(Bool) statsEn <- mkReg(False); punk@1: punk@1: Reg#(Stat) num_accesses <- mkReg(0); punk@1: Reg#(Stat) num_misses <- mkReg(0); punk@1: Reg#(Stat) num_writebacks <- mkReg(0); punk@1: punk@1: //----------------------------------------------------------- punk@1: // Name some wires punk@1: punk@1: let req = reqQ.first(); punk@1: let reqIndex = getCacheLineIndex(req); punk@1: let reqTag = getCacheLineTag(req); punk@1: let reqCacheLineAddr = getCacheLineAddr(req); punk@1: punk@1: //----------------------------------------------------------- punk@1: // Initialize punk@1: punk@1: rule init ( stage == Init ); punk@1: traceTiny("mkDataCacheBlocking", "stage","i"); punk@1: initCounter <= initCounter + 1; punk@1: cacheTagRam.upd(initCounter,Invalid); punk@1: if ( initCounter == 0 ) punk@1: stage <= Access; punk@1: endrule punk@1: punk@1: //----------------------------------------------------------- punk@1: // Access cache rule punk@1: punk@1: rule access ( (stage == Access) && respQ.notFull() ); punk@1: punk@1: // Statistics punk@1: punk@1: if ( statsEn ) punk@1: num_accesses <= num_accesses + 1; punk@1: punk@1: punk@1: // Get the corresponding tag from the rams punk@1: punk@1: Maybe#(CacheLineTag) cacheLineTag = cacheTagRam.sub(reqIndex); punk@1: punk@1: // Handle cache hits ... punk@1: punk@1: if ( isValid(cacheLineTag) && ( unJust(cacheLineTag) == reqTag ) ) punk@1: begin punk@1: traceTiny("mkDataCacheBlocking", "hitMiss","h"); punk@1: reqQ.deq(); punk@1: punk@1: case ( req ) matches punk@1: punk@1: tagged LoadReq .ld : punk@1: respQ.enq( LoadResp { tag: ld.tag, data: cacheDataRam.sub(reqIndex) } ); punk@1: punk@1: tagged StoreReq .st : punk@1: begin punk@1: respQ.enq( StoreResp { tag : st.tag } ); punk@1: cacheDataRam.upd(reqIndex,st.data); punk@1: end punk@1: punk@1: endcase punk@1: punk@1: end punk@1: punk@1: // Handle cache misses ... punk@1: punk@1: else punk@1: begin punk@1: traceTiny("mkDataCacheBlocking", "hitMiss","m"); punk@1: if ( statsEn ) punk@1: num_misses <= num_misses + 1; punk@1: punk@1: // Currently we don't use dirty bits so we always writeback the data if it is valid punk@1: punk@1: if ( isValid(cacheLineTag) ) punk@1: begin punk@1: punk@1: if ( statsEn ) punk@1: num_writebacks <= num_writebacks + 1; punk@1: punk@1: MainMemReq wbReq punk@1: = StoreReq { tag : 0, punk@1: addr : { unJust(cacheLineTag), reqIndex, 2'b0 }, punk@1: data : cacheDataRam.sub(reqIndex) }; punk@1: punk@1: mainMemReqQ.enq(wbReq); punk@1: stage <= RefillReq; punk@1: end punk@1: punk@1: // Otherwise we can issue the refill request now punk@1: punk@1: else punk@1: begin punk@1: mainMemReqQ.enq( LoadReq { tag: 0, addr: reqCacheLineAddr } ); punk@1: stage <= RefillResp; punk@1: end punk@1: punk@1: end punk@1: punk@1: endrule punk@1: punk@1: //----------------------------------------------------------- punk@1: // Refill request rule punk@1: punk@1: rule refillReq ( stage == RefillReq ); punk@1: traceTiny("mkDataCacheBlocking", "stage","r"); punk@1: mainMemReqQ.enq( LoadReq { tag: 0, addr: reqCacheLineAddr } ); punk@1: stage <= RefillResp; punk@1: endrule punk@1: punk@1: //----------------------------------------------------------- punk@1: // Refill response rule punk@1: punk@1: rule refillResp ( stage == RefillResp ); punk@1: traceTiny("mkDataCacheBlocking", "stage","R"); punk@1: traceTiny("mkDataCacheBlocking", "refill",mainMemRespQ.first()); punk@1: punk@1: // Write the new data into the cache and update the tag punk@1: punk@1: mainMemRespQ.deq(); punk@1: case ( mainMemRespQ.first() ) matches punk@1: punk@1: tagged LoadResp .ld : punk@1: begin punk@1: cacheTagRam.upd(reqIndex,Valid(reqTag)); punk@1: cacheDataRam.upd(reqIndex,ld.data); punk@1: end punk@1: punk@1: tagged StoreResp .st : punk@1: noAction; punk@1: punk@1: endcase punk@1: punk@1: stage <= Access; punk@1: endrule punk@1: punk@1: //----------------------------------------------------------- punk@1: // Methods punk@1: punk@1: interface Client mmem_client; punk@1: interface Get request = toGet(mainMemReqQ); punk@1: interface Put response = toPut(mainMemRespQ); punk@1: endinterface punk@1: punk@1: interface Server proc_server; punk@1: interface Put request = tracePut("mkDataCacheBlocking", "reqTiny",toPut(reqQ)); punk@1: interface Get response = traceGet("mkDataCacheBlocking", "respTiny",toGet(respQ)); punk@1: endinterface punk@1: punk@1: interface Put statsEn_put = toPut(asReg(statsEn)); punk@1: punk@1: interface DCacheStats stats; punk@1: interface Get num_accesses = toGet(asReg(num_accesses)); punk@1: interface Get num_misses = toGet(asReg(num_misses)); punk@1: interface Get num_writebacks = toGet(asReg(num_writebacks)); punk@1: endinterface punk@1: punk@1: endmodule punk@1: