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: // 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_DATA_CACHE.bsh" 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: rlm@8: rlm@8: rlm@8: rlm@8: interface DCache#( 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: //---------------------------------------------------------------------- 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: 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( DataReq 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( DataReq 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( DataReq 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( DataReq 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] mkDataCache( DCache#(DataReq,DataResp) ); 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#(DataReq) reqQ <- mkFIFO(); rlm@8: FIFOF#(DataResp) 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_DATA_CACHE_NUM_ACCESSES); rlm@8: STAT num_misses <- mkStatCounter(`STATS_DATA_CACHE_NUM_MISSES); rlm@8: STAT num_writebacks <- mkStatCounter(`STATS_DATA_CACHE_NUM_WRITEBACKS); 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: rlm@8: //----------------------------------------------------------- rlm@8: // Initialize rlm@8: rlm@8: rule init ( stage == Init ); rlm@8: traceTiny("mkDataCacheBlocking", "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: // Access cache 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: rlm@8: // Get the corresponding tag from the rams 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("mkDataCacheBlocking", "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: begin rlm@8: respQ.enq( StoreResp { tag : st.tag } ); rlm@8: cacheDataRam.upd(reqIndex,st.data); rlm@8: end rlm@8: rlm@8: endcase rlm@8: rlm@8: end rlm@8: rlm@8: // Handle cache misses ... rlm@8: rlm@8: else rlm@8: begin rlm@8: traceTiny("mkDataCacheBlocking", "hitMiss","m"); rlm@8: if ( statsEn ) rlm@8: num_misses.incr(); rlm@8: rlm@8: // Currently we don't use dirty bits so we always writeback the data if it is valid rlm@8: rlm@8: if ( isValid(cacheLineTag) ) rlm@8: begin rlm@8: rlm@8: if ( statsEn ) rlm@8: num_writebacks.incr(); rlm@8: rlm@8: MainMemReq wbReq rlm@8: = StoreReq { tag : 0, rlm@8: addr : { unJust(cacheLineTag), reqIndex, 2'b0 }, rlm@8: data : cacheDataRam.sub(reqIndex) }; rlm@8: rlm@8: mainMemReqQ.enq(wbReq); rlm@8: stage <= RefillReq; rlm@8: end rlm@8: rlm@8: // Otherwise we can issue the refill request now rlm@8: rlm@8: else rlm@8: begin rlm@8: mainMemReqQ.enq( LoadReq { tag: 0, addr: reqCacheLineAddr } ); rlm@8: stage <= RefillResp; rlm@8: end rlm@8: rlm@8: end rlm@8: rlm@8: endrule rlm@8: rlm@8: //----------------------------------------------------------- rlm@8: // Refill request rule rlm@8: rlm@8: rule refillReq ( stage == RefillReq ); rlm@8: traceTiny("mkDataCacheBlocking", "stage","r"); rlm@8: mainMemReqQ.enq( LoadReq { tag: 0, addr: reqCacheLineAddr } ); rlm@8: stage <= RefillResp; rlm@8: endrule rlm@8: rlm@8: //----------------------------------------------------------- rlm@8: // Refill response rule rlm@8: rlm@8: rule refillResp ( stage == RefillResp ); rlm@8: traceTiny("mkDataCacheBlocking", "stage","R"); rlm@8: traceTiny("mkDataCacheBlocking", "refill",mainMemRespQ.first()); 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("mkDataCacheBlocking", "reqTiny",fifoToPut(reqQ)); rlm@8: interface Get response = traceGet("mkDataCacheBlocking", "respTiny",fifofToGet(respQ)); rlm@8: endinterface rlm@8: rlm@8: interface Put statsEn_put = regToPut(statsEn); rlm@8: rlm@8: endmodule