diff --git a/core/CPUFactories.hpp b/core/CPUFactories.hpp index 333b5931..82dbd1db 100644 --- a/core/CPUFactories.hpp +++ b/core/CPUFactories.hpp @@ -14,6 +14,7 @@ #include "MMU.hpp" #include "SimpleTLB.hpp" #include "BIU.hpp" +#include "L2Cache.hpp" #include "MSS.hpp" #include "ROB.hpp" #include "FlushManager.hpp" @@ -71,6 +72,9 @@ namespace olympia{ sparta::ResourceFactory lsu_rf; + //! \brief Resouce Factory to build a L2Cache Unit + sparta::ResourceFactory l2cache_rf; //! \brief Resouce Factory to build a BIU Unit sparta::ResourceFactorylsu_rf }, + { + "l2cache", + "cpu.core*", + "L2Cache Unit", + sparta::TreeNode::GROUP_NAME_NONE, + sparta::TreeNode::GROUP_IDX_NONE, + &factories->l2cache_rf + }, { "biu", "cpu.core*", @@ -202,12 +210,28 @@ olympia::CoreTopologySimple::CoreTopologySimple(){ "cpu.core*.lsu.ports.in_cache_free_req" }, { - "cpu.core*.dcache.ports.out_biu_req", + "cpu.core*.dcache.ports.out_l2cache_req", + "cpu.core*.l2cache.ports.in_dcache_l2cache_req" + }, + { + "cpu.core*.dcache.ports.in_l2cache_ack", + "cpu.core*.l2cache.ports.out_l2cache_dcache_ack" + }, + { + "cpu.core*.dcache.ports.in_l2cache_resp", + "cpu.core*.l2cache.ports.out_l2cache_dcache_resp" + }, + { + "cpu.core*.l2cache.ports.out_l2cache_biu_req", "cpu.core*.biu.ports.in_biu_req" }, { "cpu.core*.biu.ports.out_biu_ack", - "cpu.core*.dcache.ports.in_biu_ack" + "cpu.core*.l2cache.ports.in_biu_l2cache_ack" + }, + { + "cpu.core*.biu.ports.out_biu_resp", + "cpu.core*.l2cache.ports.in_biu_l2cache_resp" }, { "cpu.core*.lsu.ports.out_mmu_lookup_req", diff --git a/core/DCache.cpp b/core/DCache.cpp index 86833621..70761c22 100644 --- a/core/DCache.cpp +++ b/core/DCache.cpp @@ -6,13 +6,16 @@ namespace olympia { DCache::DCache(sparta::TreeNode *n, const CacheParameterSet *p) : sparta::Unit(n), l1_always_hit_(p->l1_always_hit), - cache_latency_(p->cache_latency){ + cache_latency_(p->cache_latency) { in_lsu_lookup_req_.registerConsumerHandler (CREATE_SPARTA_HANDLER_WITH_DATA(DCache, getInstsFromLSU_, MemoryAccessInfoPtr)); - in_biu_ack_.registerConsumerHandler - (CREATE_SPARTA_HANDLER_WITH_DATA(DCache, getAckFromBIU_, InstPtr)); + in_l2cache_ack_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(DCache, getAckFromL2Cache_, uint32_t)); + + in_l2cache_resp_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(DCache, getRespFromL2Cache_, InstPtr)); // DL1 cache config const uint32_t l1_line_size = p->l1_line_size; @@ -75,20 +78,34 @@ namespace olympia { memory_access_info_ptr->setCacheState(MemoryAccessInfo::CacheState::HIT); }else{ memory_access_info_ptr->setCacheState(MemoryAccessInfo::CacheState::MISS); + // Poll on dcache_l2cache_credits_ > 0 which means + // that L2Cache can accept requests from DCache. + // Provide a corresponsing backpressure mechanism up the pipeline. if(!busy_) { busy_ = true; cache_pending_inst_ = memory_access_info_ptr; - out_biu_req_.send(cache_pending_inst_->getInstPtr()); + out_l2cache_req_.send(cache_pending_inst_->getInstPtr()); + + // Set the --dcache_l2cache_credits_ here. } } out_lsu_lookup_ack_.send(memory_access_info_ptr); } - void DCache::getAckFromBIU_(const InstPtr &inst_ptr) { + void DCache::getRespFromL2Cache_(const InstPtr &inst_ptr) { out_lsu_lookup_req_.send(cache_pending_inst_); reloadCache_(inst_ptr->getRAdr()); cache_pending_inst_.reset(); busy_ = false; } + void DCache::getAckFromL2Cache_(const uint32_t &ack) { + // When DCache sends the request to L2Cache for a miss, + // This bool will be set to false, and Dcache should wait for ack from + // L2Cache notifying DCache that there is space in it's dcache request buffer + // + // Set it to true so that the following misses from DCache can be sent out to L2Cache. + dcache_l2cache_credits_ = ack; + } + } diff --git a/core/DCache.hpp b/core/DCache.hpp index 520bca4b..d0c9698d 100644 --- a/core/DCache.hpp +++ b/core/DCache.hpp @@ -35,7 +35,9 @@ namespace olympia { void getInstsFromLSU_(const MemoryAccessInfoPtr &memory_access_info_ptr); - void getAckFromBIU_(const InstPtr &inst_ptr); + void getAckFromL2Cache_(const uint32_t &ack); + + void getRespFromL2Cache_(const InstPtr &inst_ptr); using L1Handle = CacheFuncModel::Handle; L1Handle l1_cache_; @@ -45,14 +47,20 @@ namespace olympia { // Keep track of the instruction that causes current outstanding cache miss MemoryAccessInfoPtr cache_pending_inst_ = nullptr; + // Credit bool for sending miss request to L2Cache + uint32_t dcache_l2cache_credits_ = 0; + //////////////////////////////////////////////////////////////////////////////// // Input Ports //////////////////////////////////////////////////////////////////////////////// sparta::DataInPort in_lsu_lookup_req_ {&unit_port_set_, "in_lsu_lookup_req", 0}; - sparta::DataInPort in_biu_ack_ - {&unit_port_set_, "in_biu_ack", 1}; + sparta::DataInPort in_l2cache_ack_ + {&unit_port_set_, "in_l2cache_ack", 1}; + + sparta::DataInPort in_l2cache_resp_ + {&unit_port_set_, "in_l2cache_resp", 1}; //////////////////////////////////////////////////////////////////////////////// // Output Ports @@ -66,8 +74,8 @@ namespace olympia { sparta::DataOutPort out_lsu_lookup_req_ {&unit_port_set_, "out_lsu_lookup_req", 1}; - sparta::DataOutPort out_biu_req_ - {&unit_port_set_, "out_biu_req"}; + sparta::DataOutPort out_l2cache_req_ + {&unit_port_set_, "out_l2cache_req", 0}; //////////////////////////////////////////////////////////////////////////////// // Events diff --git a/core/LSU.hpp b/core/LSU.hpp index 9173a727..88a98f0c 100644 --- a/core/LSU.hpp +++ b/core/LSU.hpp @@ -399,8 +399,36 @@ namespace olympia }; - inline std::ostream & operator<<(std::ostream & os, - const olympia::MemoryAccessInfo::MMUState & mmu_access_state){ + inline std::ostream& operator<<(std::ostream & os, + const olympia::MemoryAccessInfo::ArchUnit & unit) { + switch(unit) { + case olympia::MemoryAccessInfo::ArchUnit::NO_ACCESS: + os << "NO_ACCESS"; + break; + case olympia::MemoryAccessInfo::ArchUnit::ICACHE: + os << "ICACHE"; + break; + case olympia::MemoryAccessInfo::ArchUnit::LSU: + os << "LSU"; + break; + case olympia::MemoryAccessInfo::ArchUnit::DCACHE: + os << "DCACHE"; + break; + case olympia::MemoryAccessInfo::ArchUnit::L2CACHE: + os << "L2CACHE"; + break; + case olympia::MemoryAccessInfo::ArchUnit::BIU: + os << "BIU"; + break; + case olympia::MemoryAccessInfo::ArchUnit::NUM_UNITS: + os << "NUM_UNITS"; + break; + } + return os; + } + + inline std::ostream& operator<<(std::ostream & os, + const olympia::MemoryAccessInfo::MMUState & mmu_access_state){ switch(mmu_access_state){ case olympia::MemoryAccessInfo::MMUState::NO_ACCESS: os << "no_access"; @@ -417,12 +445,15 @@ namespace olympia return os; } - inline std::ostream & operator<<(std::ostream & os, - const olympia::MemoryAccessInfo::CacheState & cache_access_state){ + inline std::ostream& operator<<(std::ostream & os, + const olympia::MemoryAccessInfo::CacheState & cache_access_state){ switch(cache_access_state){ case olympia::MemoryAccessInfo::CacheState::NO_ACCESS: os << "no_access"; break; + case olympia::MemoryAccessInfo::CacheState::RELOAD: + os << "reload"; + break; case olympia::MemoryAccessInfo::CacheState::MISS: os << "miss"; break; @@ -436,7 +467,7 @@ namespace olympia } inline std::ostream& operator<<(std::ostream& os, - const olympia::LSU::LoadStoreInstInfo::IssuePriority& rank){ + const olympia::LSU::LoadStoreInstInfo::IssuePriority& rank){ switch(rank){ case LSU::LoadStoreInstInfo::IssuePriority::HIGHEST: os << "(highest)"; @@ -466,7 +497,7 @@ namespace olympia } inline std::ostream& operator<<(std::ostream& os, - const olympia::LSU::LoadStoreInstInfo::IssueState& state){ + const olympia::LSU::LoadStoreInstInfo::IssueState& state){ // Print instruction issue state switch(state){ case LSU::LoadStoreInstInfo::IssueState::READY: diff --git a/core/MemoryAccessInfo.hpp b/core/MemoryAccessInfo.hpp index 28203d4d..f2416a09 100644 --- a/core/MemoryAccessInfo.hpp +++ b/core/MemoryAccessInfo.hpp @@ -13,6 +13,11 @@ namespace olympia { class MemoryAccessInfoPairDef; + class MemoryAccessInfo; + + using MemoryAccessInfoPtr = sparta::SpartaSharedPointer; + using MemoryAccessInfoAllocator = sparta::SpartaSharedPointerAllocator; + class MemoryAccessInfo { public: @@ -33,12 +38,25 @@ namespace olympia { enum class CacheState : std::uint64_t { NO_ACCESS = 0, __FIRST = NO_ACCESS, + RELOAD, MISS, HIT, NUM_STATES, __LAST = NUM_STATES }; + enum class ArchUnit : std::uint32_t { + NO_ACCESS = 0, + __FIRST = NO_ACCESS, + ICACHE, + LSU, + DCACHE, + L2CACHE, + BIU, + NUM_UNITS, + __LAST = NUM_UNITS + }; + MemoryAccessInfo() = delete; MemoryAccessInfo( @@ -48,7 +66,9 @@ namespace olympia { mmu_access_state_(MMUState::NO_ACCESS), // Construct the State object here - cache_access_state_(CacheState::NO_ACCESS) {} + cache_access_state_(CacheState::NO_ACCESS), + src_(ArchUnit::NO_ACCESS), + dest_(ArchUnit::NO_ACCESS) {} virtual ~MemoryAccessInfo() {} @@ -67,6 +87,15 @@ namespace olympia { bool getPhyAddrStatus() const { return phy_addr_ready_; } + void setSrcUnit(const ArchUnit & src_unit) { src_ = src_unit; } + const ArchUnit & getSrcUnit() const { return src_; } + + void setDestUnit(const ArchUnit & dest_unit) { dest_ = dest_unit; } + const ArchUnit & getDestUnit() const { return dest_; } + + void setNextReq(const MemoryAccessInfoPtr & nextReq) { next_req_ = nextReq; } + const MemoryAccessInfoPtr & getNextReq() { return next_req_; } + MMUState getMMUState() const { return mmu_access_state_; } @@ -100,6 +129,15 @@ namespace olympia { // DCache access status CacheState cache_access_state_; + // Src and destination unit name for the packet + ArchUnit src_ = ArchUnit::NO_ACCESS; + ArchUnit dest_ = ArchUnit::NO_ACCESS; + + // Pointer to next request for DEBUG/TRACK + // (Note : Currently used only to track request with same cacheline in L2Cache + // Not for functional/performance purpose) + MemoryAccessInfoPtr next_req_ = nullptr; + // Scoreboards using ScoreboardViews = std::array, core_types::N_REGFILES>; ScoreboardViews scoreboard_views_; @@ -128,7 +166,4 @@ namespace olympia { SPARTA_ADDPAIR("cache", &MemoryAccessInfo::getCacheState), SPARTA_FLATTEN(&MemoryAccessInfo::getInstPtr)) }; - - using MemoryAccessInfoPtr = sparta::SpartaSharedPointer; - using MemoryAccessInfoAllocator = sparta::SpartaSharedPointerAllocator; }; diff --git a/mss/BIU.cpp b/mss/BIU.cpp index 7d0f1773..bc0b1933 100644 --- a/mss/BIU.cpp +++ b/mss/BIU.cpp @@ -19,13 +19,13 @@ namespace olympia_mss biu_latency_(p->biu_latency) { in_biu_req_.registerConsumerHandler - (CREATE_SPARTA_HANDLER_WITH_DATA(BIU, getReqFromLSU_, olympia::InstPtr)); + (CREATE_SPARTA_HANDLER_WITH_DATA(BIU, receiveReqFromL2Cache_, olympia::InstPtr)); in_mss_ack_sync_.registerConsumerHandler (CREATE_SPARTA_HANDLER_WITH_DATA(BIU, getAckFromMSS_, bool)); in_mss_ack_sync_.setPortDelay(static_cast(1)); - + sparta::StartupEvent(node, CREATE_SPARTA_HANDLER(BIU, sendInitialCredits_)); ILOG("BIU construct: #" << node->getGroupIdx()); } @@ -34,8 +34,14 @@ namespace olympia_mss // Callbacks //////////////////////////////////////////////////////////////////////////////// - // Receive new BIU request from LSU - void BIU::getReqFromLSU_(const olympia::InstPtr & inst_ptr) + // Sending Initial credits to L2Cache + void BIU::sendInitialCredits_() { + out_biu_ack_.send(biu_req_queue_size_); + ILOG("Sending initial credits to L2Cache : " << biu_req_queue_size_); + } + + // Receive new BIU request from L2Cache + void BIU::receiveReqFromL2Cache_(const olympia::InstPtr & inst_ptr) { appendReqQueue_(inst_ptr); @@ -63,14 +69,23 @@ namespace olympia_mss biu_busy_ = true; out_mss_req_sync_.send(biu_req_queue_.front(), biu_latency_); + if (biu_req_queue_.size() < biu_req_queue_size_) { + // Send out the ack to L2Cache if there is space in biu_req_queue_ + ev_handle_biu_l2cache_ack_.schedule(sparta::Clock::Cycle(0)); + } + ILOG("BIU request is sent to MSS!"); } // Handle MSS Ack void BIU::handle_MSS_Ack_() { - out_biu_ack_.send(biu_req_queue_.front()); + out_biu_resp_.send(biu_req_queue_.front(), biu_latency_); + biu_req_queue_.pop_front(); + + // Send out the ack to L2Cache through , we just created space in biu_req_queue_ + ev_handle_biu_l2cache_ack_.schedule(sparta::Clock::Cycle(0)); biu_busy_ = false; // Schedule BIU request handling event only when: @@ -97,6 +112,14 @@ namespace olympia_mss sparta_assert(false, "MSS is NOT done!"); } + // Handle ack backto L2Cache + void BIU::handle_BIU_L2Cache_Ack_() + { + uint32_t available_slots = biu_req_queue_size_ - biu_req_queue_.size(); + out_biu_ack_.send(available_slots); + + ILOG("BIU->L2Cache : Ack is sent."); + } //////////////////////////////////////////////////////////////////////////////// // Regular Function/Subroutine Call diff --git a/mss/BIU.hpp b/mss/BIU.hpp index 5798907e..f494ff64 100644 --- a/mss/BIU.hpp +++ b/mss/BIU.hpp @@ -67,9 +67,12 @@ namespace olympia_mss // Output Ports //////////////////////////////////////////////////////////////////////////////// - sparta::DataOutPort out_biu_ack_ + sparta::DataOutPort out_biu_ack_ {&unit_port_set_, "out_biu_ack"}; + sparta::DataOutPort out_biu_resp_ + {&unit_port_set_, "out_biu_resp"}; + sparta::SyncOutPort out_mss_req_sync_ {&unit_port_set_, "out_mss_req_sync", getClock()}; @@ -91,7 +94,7 @@ namespace olympia_mss // Event Handlers //////////////////////////////////////////////////////////////////////////////// - // Event to handle BIU request from LSU + // Event to handle BIU request from L2Cache sparta::UniqueEvent<> ev_handle_biu_req_ {&unit_event_set_, "handle_biu_req", CREATE_SPARTA_HANDLER(BIU, handle_BIU_Req_)}; @@ -99,13 +102,17 @@ namespace olympia_mss sparta::UniqueEvent<> ev_handle_mss_ack_ {&unit_event_set_, "handle_mss_ack", CREATE_SPARTA_HANDLER(BIU, handle_MSS_Ack_)}; + // Event to handleBIU ack for L2Cache + sparta::UniqueEvent<> ev_handle_biu_l2cache_ack_ + {&unit_event_set_, "ev_handle_biu_l2cache_ack", CREATE_SPARTA_HANDLER(BIU, handle_BIU_L2Cache_Ack_)}; + //////////////////////////////////////////////////////////////////////////////// // Callbacks //////////////////////////////////////////////////////////////////////////////// - // Receive new BIU request from LSU - void getReqFromLSU_(const olympia::InstPtr &); + // Receive new BIU request from L2Cache + void receiveReqFromL2Cache_(const olympia::InstPtr &); // Handle BIU request void handle_BIU_Req_(); @@ -113,10 +120,15 @@ namespace olympia_mss // Handle MSS Ack void handle_MSS_Ack_(); + // Handle ack backto L2Cache + void handle_BIU_L2Cache_Ack_(); + // Receive MSS access acknowledge // Q: Does the argument list has to be "const DataType &" ? void getAckFromMSS_(const bool &); + // Sending initial credits to L2Cache + void sendInitialCredits_(); //////////////////////////////////////////////////////////////////////////////// // Regular Function/Subroutine Call @@ -124,7 +136,5 @@ namespace olympia_mss // Append BIU request queue void appendReqQueue_(const olympia::InstPtr &); - - }; } diff --git a/mss/CMakeLists.txt b/mss/CMakeLists.txt index 706261d4..1f2739e8 100644 --- a/mss/CMakeLists.txt +++ b/mss/CMakeLists.txt @@ -1,6 +1,7 @@ project (mss) add_library(mss BIU.cpp + L2Cache.cpp MSS.cpp ) get_property(SPARTA_INCLUDE_PROP TARGET SPARTA::sparta PROPERTY INTERFACE_INCLUDE_DIRECTORIES) diff --git a/mss/L2Cache.cpp b/mss/L2Cache.cpp new file mode 100644 index 00000000..f5cb3efe --- /dev/null +++ b/mss/L2Cache.cpp @@ -0,0 +1,678 @@ +// -*- C++ -*- + +#include "sparta/utils/SpartaAssert.hpp" +#include "sparta/utils/LogUtils.hpp" + +#include "L2Cache.hpp" + +#include "OlympiaAllocators.hpp" + +namespace olympia_mss +{ + const char L2Cache::name[] = "l2cache"; + + //////////////////////////////////////////////////////////////////////////////// + // Constructor + //////////////////////////////////////////////////////////////////////////////// + + L2Cache::L2Cache(sparta::TreeNode *node, const L2CacheParameterSet *p) : + sparta::Unit(node), + num_reqs_from_dcache_(&unit_stat_set_, + "num_reqs_from_dcache", + "The total number of instructions received by L2Cache from DCache", + sparta::Counter::COUNT_NORMAL), + num_reqs_from_icache_(&unit_stat_set_, + "num_reqs_from_icache", + "The total number of instructions received by L2Cache from ICache", + sparta::Counter::COUNT_NORMAL), + num_reqs_to_biu_(&unit_stat_set_, + "num_reqs_to_biu", + "The total number of instructions forwarded from L2Cache to BIU", + sparta::Counter::COUNT_NORMAL), + num_acks_from_biu_(&unit_stat_set_, + "num_acks_from_biu", + "The total number of instructions received from BIU into L2Cache", + sparta::Counter::COUNT_NORMAL), + num_acks_to_icache_(&unit_stat_set_, + "num_acks_to_icache", + "The total number of instructions forwarded from L2Cache to ICache", + sparta::Counter::COUNT_NORMAL), + num_acks_to_dcache_(&unit_stat_set_, + "num_acks_to_dcache", + "The total number of instructions forwarded from L2Cache to DCache", + sparta::Counter::COUNT_NORMAL), + num_resps_from_biu_(&unit_stat_set_, + "num_resps_from_biu", + "The total number of instructions received from BIU into L2Cache", + sparta::Counter::COUNT_NORMAL), + num_resps_to_icache_(&unit_stat_set_, + "num_resps_to_icache", + "The total number of instructions forwarded from L2Cache to ICache", + sparta::Counter::COUNT_NORMAL), + num_resps_to_dcache_(&unit_stat_set_, + "num_resps_to_dcache", + "The total number of instructions forwarded from L2Cache to DCache", + sparta::Counter::COUNT_NORMAL), + l2_cache_hits_(&unit_stat_set_, + "l2_cache_hits", + "The total number L2 Cache Hits", + sparta::Counter::COUNT_NORMAL), + l2_cache_misses_(&unit_stat_set_, + "l2_cache_misses", + "The total number L2 Cache Misses", + sparta::Counter::COUNT_NORMAL), + dcache_req_queue_size_(p->dcache_req_queue_size), + icache_req_queue_size_(p->icache_req_queue_size), + biu_req_queue_size_(p->biu_req_queue_size), + biu_resp_queue_size_(p->biu_resp_queue_size), + dcache_resp_queue_size_(p->dcache_resp_queue_size), + icache_resp_queue_size_(p->icache_resp_queue_size), + stages_(p->l2cache_latency), + l2cache_pipeline_("L2CachePipeline", stages_.NUM_STAGES, getClock()), + pipeline_req_queue_("Pipeline_Request_Queue", + p->pipeline_req_queue_size, + node->getClock()), + miss_pending_buffer_("Miss_Pending_Buffer", + p->miss_pending_buffer_size, + node->getClock(), + &unit_stat_set_), + miss_pending_buffer_size_(p->miss_pending_buffer_size), + l2_lineSize_(p->l2_line_size), + shiftBy_(log2(l2_lineSize_)), + l2_always_hit_(p->l2_always_hit), + l2cache_latency_(p->l2cache_latency), + is_icache_connected_(p->is_icache_connected), + is_dcache_connected_(p->is_dcache_connected), + memory_access_allocator_(sparta::notNull(olympia::OlympiaAllocators::getOlympiaAllocators(node))-> + memory_access_allocator) { + + // In Port Handler registration + in_dcache_l2cache_req_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(L2Cache, getReqFromDCache_, olympia::InstPtr)); + + in_icache_l2cache_req_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(L2Cache, getReqFromICache_, olympia::InstPtr)); + + in_biu_resp_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(L2Cache, getRespFromBIU_, olympia::InstPtr)); + + in_biu_ack_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(L2Cache, getAckFromBIU_, uint32_t)); + + // Pipeline collection config + l2cache_pipeline_.enableCollection(node); + // Allow the pipeline to create events and schedule work + l2cache_pipeline_.performOwnUpdates(); + + // There can be situations where NOTHING is going on in the + // simulator but forward progression of the pipeline elements. + // In this case, the internal event for the pipeline will + // be the only event keeping simulation alive. Sparta + // supports identifying non-essential events (by calling + // setContinuing to false on any event). + l2cache_pipeline_.setContinuing(true); + + l2cache_pipeline_.registerHandlerAtStage(stages_.CACHE_LOOKUP, CREATE_SPARTA_HANDLER(L2Cache, handleCacheAccessRequest_)); + + l2cache_pipeline_.registerHandlerAtStage(stages_.HIT_MISS_HANDLING, CREATE_SPARTA_HANDLER(L2Cache, handleCacheAccessResult_)); + + // L2 cache config + const uint32_t l2_size_kb = p->l2_size_kb; + const uint32_t l2_associativity = p->l2_associativity; + std::unique_ptr repl(new sparta::cache::TreePLRUReplacement + (l2_associativity)); + l2_cache_.reset(new olympia::CacheFuncModel( getContainer(), l2_size_kb, l2_lineSize_, *repl)); + + sparta::StartupEvent(node, CREATE_SPARTA_HANDLER(L2Cache, sendInitialCredits_)); + ILOG("L2Cache construct: #" << node->getGroupIdx()); + } + + + //////////////////////////////////////////////////////////////////////////////// + // Callbacks + //////////////////////////////////////////////////////////////////////////////// + // Sending Initial credits to I/D-Cache + void L2Cache::sendInitialCredits_() { + if (is_icache_connected_) { + out_l2cache_icache_ack_.send(icache_req_queue_size_); + ILOG("Sending initial credits to ICache : " << icache_req_queue_size_); + } + + if (is_dcache_connected_) { + out_l2cache_dcache_ack_.send(dcache_req_queue_size_); + ILOG("Sending initial credits to DCache : " << dcache_req_queue_size_); + } + } + + // Receive new L2Cache request from DCache + void L2Cache::getReqFromDCache_(const olympia::InstPtr & inst_ptr) { + + ILOG("Request received from DCache on the port"); + + appendDCacheReqQueue_(inst_ptr); + + ev_handle_dcache_l2cache_req_.schedule(sparta::Clock::Cycle(0)); + ++num_reqs_from_dcache_; + } + + // Receive new L2Cache request from ICache + void L2Cache::getReqFromICache_(const olympia::InstPtr & inst_ptr) { + + ILOG("Request received from ICache on the port"); + + appendICacheReqQueue_(inst_ptr); + + ev_handle_icache_l2cache_req_.schedule(sparta::Clock::Cycle(0)); + ++num_reqs_from_icache_; + } + + // Handle BIU resp + void L2Cache::getRespFromBIU_(const olympia::InstPtr & inst_ptr) { + + ILOG("Response received from BIU on the port"); + + appendBIURespQueue_(inst_ptr); + + // Schedule BIU resp handling event only when: + // Request queue is not empty + if (biu_resp_queue_.size() <= biu_resp_queue_size_) { + ev_handle_biu_l2cache_resp_.schedule(sparta::Clock::Cycle(0)); + ++num_resps_from_biu_; + } + else { + sparta_assert(false, "This request cannot be serviced right now, L2Cache input buffer from DCache is already full!"); + } + } + + // Handle BIU ack + void L2Cache::getAckFromBIU_(const uint32_t & ack) { + + // Update the biu credits + l2cache_biu_credits_ = ack; + + // Kickstart the pipeline issueing + ev_issue_req_.schedule(1); + + ILOG("Ack received from BIU on the port : Current BIU credit available = " << l2cache_biu_credits_); + } + + // Handle L2Cache request from DCache + void L2Cache::handle_DCache_L2Cache_Req_() { + if (!dcache_req_queue_.empty()) { + ev_create_req_.schedule(sparta::Clock::Cycle(0)); + } + } + + // Handle L2Cache request from ICache + void L2Cache::handle_ICache_L2Cache_Req_() { + if (!icache_req_queue_.empty()) { + ev_create_req_.schedule(sparta::Clock::Cycle(0)); + } + } + + // Handle BIU->L2Cache response + void L2Cache::handle_BIU_L2Cache_Resp_() { + if (!biu_resp_queue_.empty()) { + ev_create_req_.schedule(sparta::Clock::Cycle(0)); + } + } + + // Handle L2Cache request to BIU + void L2Cache::handle_L2Cache_BIU_Req_() { + + if (l2cache_biu_credits_ > 0 && !biu_req_queue_.empty()) { + out_biu_req_.send(biu_req_queue_.front()); + --l2cache_biu_credits_; + + biu_req_queue_.erase(biu_req_queue_.begin()); + + ++num_reqs_to_biu_; + + ILOG("L2Cache Request sent to BIU : Current BIU credit available = " <DCache : Ack is sent."); + } + + // Returning resp to ICache + void L2Cache::handle_L2Cache_ICache_Ack_() { + uint32_t available_slots = icache_req_queue_size_ - icache_req_queue_.size(); + out_l2cache_icache_ack_.send(available_slots); + ++num_acks_to_icache_; + + ILOG("L2Cache->ICache : Ack is sent."); + } + + // Returning resp to DCache + void L2Cache::handle_L2Cache_DCache_Resp_() { + out_l2cache_dcache_resp_.send(dcache_resp_queue_.front()); + dcache_resp_queue_.erase(dcache_resp_queue_.begin()); + + ++num_resps_to_dcache_; + + ILOG("L2Cache Resp is sent to DCache!"); + } + + // Returning resp to ICache + void L2Cache::handle_L2Cache_ICache_Resp_() { + out_l2cache_icache_resp_.send(icache_resp_queue_.front()); + icache_resp_queue_.erase(icache_resp_queue_.begin()); + + ++num_resps_to_icache_; + + ILOG("L2Cache Resp is sent to ICache!"); + } + + // Handle arbitration and forward the req to pipeline_req_queue_ + void L2Cache::create_Req_() { + + Channel arbitration_winner = arbitrateL2CacheAccessReqs_(); + + if (arbitration_winner == Channel::BIU) { + + const olympia::InstPtr &instPtr = biu_resp_queue_.front(); + + // Function to check if the request to the given cacheline is present in the miss_pending_buffer_ + auto getCacheLine = [this] (auto inst_ptr) { return inst_ptr->getRAdr() >> shiftBy_; }; + auto const inst_cl = getCacheLine(instPtr); + + auto is_cl_present = [&instPtr, inst_cl, getCacheLine] (auto reqPtr) + { return getCacheLine(reqPtr->getInstPtr()) == inst_cl; }; + + auto req = std::find_if(miss_pending_buffer_.begin(), miss_pending_buffer_.end(), is_cl_present); + + // Set the original SrcUnit as the DestUnit because the resp will + // now be forwarded from BIU to the original SrcUnit + if (req != miss_pending_buffer_.end()) { + + ILOG("Request found in miss_pending_buffer_ with SrcUnit : " << (*req)->getSrcUnit()); + + (*req)->setDestUnit((*req)->getSrcUnit()); + (*req)->setSrcUnit(L2ArchUnit::BIU); + (*req)->setCacheState(L2CacheState::RELOAD); + + if (pipeline_req_queue_.numFree() > 0) { + pipeline_req_queue_.push(*req); + } + else { + sparta_assert("pipeline_req_queue_ is full. Check the sizing.") + } + + // Check if this was the last occuring + auto iter = req; ++iter; + auto next_req = std::find_if(iter, miss_pending_buffer_.end(), is_cl_present); + + if (next_req == miss_pending_buffer_.end()) { + + // When the find_if() returns .end(), free the entry in the biu_resp_queue_ + biu_resp_queue_.erase(biu_resp_queue_.begin()); + } + + // Free the entry in the miss_pending_buffer_ + miss_pending_buffer_.erase(req); + } + } + else if (arbitration_winner == Channel::ICACHE) { + + const auto &reqPtr = sparta::allocate_sparta_shared_pointer(memory_access_allocator_, + icache_req_queue_.front()); + + reqPtr->setSrcUnit(L2ArchUnit::ICACHE); + reqPtr->setDestUnit(L2ArchUnit::ICACHE); + + pipeline_req_queue_.push(reqPtr); + ILOG("ICache request is sent to Pipeline_req_Q!"); + + icache_req_queue_.erase(icache_req_queue_.begin()); + + // Send out the ack to ICache for credit management + ev_handle_l2cache_icache_ack_.schedule(sparta::Clock::Cycle(1)); + } + else if (arbitration_winner == Channel::DCACHE) { + + const auto &reqPtr = sparta::allocate_sparta_shared_pointer(memory_access_allocator_, + dcache_req_queue_.front()); + + reqPtr->setSrcUnit(L2ArchUnit::DCACHE); + reqPtr->setDestUnit(L2ArchUnit::DCACHE); + + pipeline_req_queue_.push(reqPtr); + ILOG("DCache request is sent to Pipeline_req_Q!"); + + dcache_req_queue_.erase(dcache_req_queue_.begin()); + + // Send out the ack to DCache for credit management + ev_handle_l2cache_dcache_ack_.schedule(sparta::Clock::Cycle(1)); + } + else if (arbitration_winner == Channel::NO_ACCESS) { + // Schedule a ev_create_req_ event again to see if the the new request + // from any of the requestors can be put into pipeline_req_queue_ + ev_create_req_.schedule(sparta::Clock::Cycle(1)); + } + else { + sparta_assert(false, "Invalid arbitration winner, What Channel is picked up?"); + } + + // Try to issue a request to l2cache_pipeline_ + ev_issue_req_.schedule(1); + + // Schedule a ev_create_req_ event again to see if the the new request + // from any of the requestors can be put into pipeline_req_queue_ + if ( !biu_resp_queue_.empty() + || !icache_req_queue_.empty() + || !dcache_req_queue_.empty() ) { + ev_create_req_.schedule(sparta::Clock::Cycle(1)); + } + } + + void L2Cache::issue_Req_() { + + // Append the request to a pipeline if the pipeline_req_queue_ is not empty + // and l2cache_pipeline_ has credits available + if (hasCreditsForPipelineIssue_() && !pipeline_req_queue_.empty()) { + l2cache_pipeline_.append(pipeline_req_queue_.front()); + ++inFlight_reqs_; + ILOG("Request is sent to Pipeline! SrcUnit : " << pipeline_req_queue_.front()->getSrcUnit()); + + pipeline_req_queue_.pop(); + } + + // Checking for the queue empty again before scheduling the event for the next clock cycle + if (!pipeline_req_queue_.empty()) { + ev_issue_req_.schedule(1); + } + } + + // Pipeline Stage CACHE_LOOKUP + void L2Cache::handleCacheAccessRequest_() { + const auto req = l2cache_pipeline_[stages_.CACHE_LOOKUP]; + ILOG("Pipeline stage CACHE_LOOKUP : " << req->getInstPtr()); + + const L2CacheState cacheLookUpResult = cacheLookup_(req); + + // Access cache, and check cache hit or miss + if (req->getCacheState() == L2CacheState::RELOAD) { + + if (cacheLookUpResult == L2CacheState::MISS) { + + // Reload cache line + reloadCache_(req->getInstPtr()->getRAdr()); + + ILOG("Reload Complete: phyAddr=0x" << std::hex << req->getInstPtr()->getRAdr()); + } + + req->setCacheState(L2CacheState::HIT); + } + else { + + // Update memory access info + req->setCacheState(cacheLookUpResult); + } + } + + // Pipeline Stage HIT_MISS_HANDLING + void L2Cache::handleCacheAccessResult_() { + const auto req = l2cache_pipeline_[stages_.HIT_MISS_HANDLING]; + ILOG("Pipeline stage HIT_MISS_HANDLING : " << req->getInstPtr()); + + --inFlight_reqs_; + + // This request to access cache came from DCache or ICache to do a cache lookup. + // It was either a miss or hit based on cacheLookup_() in the previous stage of the pipeline + if (req->getCacheState() == L2CacheState::HIT) { + // If it was originally a miss in L2Cache, on return from BIU, it's SrcUnit is set to BIU + // and DestUnit to whatever the original SrcUnit was. + // + // If it was a hit in L2Cache, return the request back to where it originally came from. + // + // Send out the resp to the original SrcUnit -- which is now the DestUnit. + sendOutResp_(req->getDestUnit(), req->getInstPtr()); + } + else { // if (req->getCacheState() == L2CacheState::MISS) + + // Set Destination for this request to BIU + req->setDestUnit(L2ArchUnit::BIU); + + // Handle the miss instruction by storing it aside while waiting + // for lower level memory to return + if (miss_pending_buffer_.size() < miss_pending_buffer_size_) { + + ILOG("Storing the CACHE MISS in miss_pending_buffer_"); + miss_pending_buffer_.push_back(req); + } + else { + sparta_assert(false, "No space in miss_pending_buffer_! Why did the frontend issue push the request onto l2cache_pipeline_?"); + } + + // Function to check if the request to the given cacheline is present in the miss_pending_buffer_ + auto getCacheLine = [this] (auto reqPtr) { return reqPtr->getInstPtr()->getRAdr() >> shiftBy_; }; + const auto req_cl = getCacheLine(req); + + auto is_cl_present = [&req, req_cl, getCacheLine] (auto reqPtr) + { return (req != reqPtr && getCacheLine(reqPtr) == req_cl); }; + + // Send out the request to BIU for a cache MISS if it is not sent out already + auto reqIter = std::find_if(miss_pending_buffer_.rbegin(), miss_pending_buffer_.rend(), is_cl_present); + + if (reqIter == miss_pending_buffer_.rend()) { + sendOutReq_(req->getDestUnit(), req->getInstPtr()); + } + else { + // Found a request to same cacheLine. + // Link the current request to the last pending request + (*reqIter)->setNextReq(req); + } + } + } + + //////////////////////////////////////////////////////////////////////////////// + // Regular Function/Subroutine Call + //////////////////////////////////////////////////////////////////////////////// + + // Append L2Cache request queue for reqs from DCache + void L2Cache::appendDCacheReqQueue_(const olympia::InstPtr& inst_ptr) { + sparta_assert(dcache_req_queue_.size() <= dcache_req_queue_size_ ,"DCache request queue overflows!"); + + // Push new requests from back + dcache_req_queue_.emplace_back(inst_ptr); + ILOG("Append DCache->L2Cache request queue!"); + } + + // Append L2Cache request queue for reqs from ICache + void L2Cache::appendICacheReqQueue_(const olympia::InstPtr& inst_ptr) { + sparta_assert(icache_req_queue_.size() <= icache_req_queue_size_ ,"ICache request queue overflows!"); + + // Push new requests from back + icache_req_queue_.emplace_back(inst_ptr); + ILOG("Append ICache->L2Cache request queue!"); + } + + // Append BIU resp queue + void L2Cache::appendBIURespQueue_(const olympia::InstPtr& inst_ptr) { + sparta_assert(biu_resp_queue_.size() <= biu_resp_queue_size_ ,"BIU resp queue overflows!"); + + // Push new requests from back + biu_resp_queue_.emplace_back(inst_ptr); + + ILOG("Append BIU->L2Cache resp queue!"); + } + + // Append DCache resp queue + void L2Cache::appendDCacheRespQueue_(const olympia::InstPtr& inst_ptr) { + sparta_assert(dcache_resp_queue_.size() <= dcache_resp_queue_size_ ,"DCache resp queue overflows!"); + + // Push new resp to the dcache_resp_queue_ + dcache_resp_queue_.emplace_back(inst_ptr); + ev_handle_l2cache_dcache_resp_.schedule(sparta::Clock::Cycle(0)); + + ILOG("Append L2Cache->DCache resp queue!"); + } + + // Append ICache resp queue + void L2Cache::appendICacheRespQueue_(const olympia::InstPtr& inst_ptr) { + sparta_assert(icache_resp_queue_.size() <= icache_resp_queue_size_ ,"ICache resp queue overflows!"); + + // Push new resp to the icache_resp_queue_ + icache_resp_queue_.emplace_back(inst_ptr); + ev_handle_l2cache_icache_resp_.schedule(sparta::Clock::Cycle(0)); + + ILOG("Append L2Cache->ICache resp queue!"); + } + + // Append BIU req queue + void L2Cache::appendBIUReqQueue_(const olympia::InstPtr& inst_ptr) { + sparta_assert(biu_req_queue_.size() <= biu_req_queue_size_ ,"BIU req queue overflows!"); + + // Push new request to the biu_req_queue_ if biu credits are available with the L2Cache + biu_req_queue_.emplace_back(inst_ptr); + ev_handle_l2cache_biu_req_.schedule(sparta::Clock::Cycle(0)); + + ILOG("Append L2Cache->BIU req queue"); + } + + // Return the resp to the master units + void L2Cache::sendOutResp_(const L2ArchUnit &unit, const olympia::InstPtr& instPtr) { + // if (instPtr is originally from DCache) + if (unit == L2ArchUnit::DCACHE) { + appendDCacheRespQueue_(instPtr); + } + // if (instPtr is originally from ICache) + else if (unit == L2ArchUnit::ICACHE) { + appendICacheRespQueue_(instPtr); + } + else { + sparta_assert(false, "Resp is being sent to a Unit that is not valid"); + } + } + + // Send the request to the slave units + void L2Cache::sendOutReq_(const L2ArchUnit &unit, const olympia::InstPtr& instPtr) { + // if (instPtr is destined for BIU on L2Cache miss) + if (unit == L2ArchUnit::BIU) { + appendBIUReqQueue_(instPtr); + } + else { + sparta_assert(false, "Request is being sent to a Unit that is not valid"); + } + } + + // Select the Channel to pick the request from + // Current options : + // BIU - P0 + // DCache - P1 - RoundRobin Candidate + // DL1 - P1 - RoundRobin Candidate + L2Cache::Channel L2Cache::arbitrateL2CacheAccessReqs_() { + sparta_assert(icache_req_queue_.size() > 0 || dcache_req_queue_.size() > 0 || biu_resp_queue_.size() > 0, + "Arbitration failed: Reqest queues are empty!"); + + Channel winner; + + if (pipeline_req_queue_.numFree() == 0) { + + // pipeline_req_queue_ is full, try again next cycle + winner = Channel::NO_ACCESS; + } + // P0 priority to sevice the pending request in the buffer + else if (!biu_resp_queue_.empty()) { + + winner = Channel::BIU; + ILOG("Arbitration winner - BIU"); + } + // RoundRobin for P1 Priority + else if (channel_select_ == Channel::ICACHE) { + // Set it up for the following arbitration request + channel_select_ = Channel::DCACHE; + winner = Channel::NO_ACCESS; + + if (!icache_req_queue_.empty()) { + + winner = Channel::ICACHE; + ILOG("Arbitration winner - ICache"); + } + } + else if (channel_select_ == Channel::DCACHE) { + + // Set it up for the following arbitration request + channel_select_ = Channel::ICACHE; + winner = Channel::NO_ACCESS; + + if (!dcache_req_queue_.empty()) { + + winner = Channel::DCACHE; + ILOG("Arbitration winner - DCache"); + } + } + else { + sparta_assert(false, "Illegal else : Why is channel_select_ incorrectly set?"); + } + + return winner; + } + + // Cache lookup for a HIT or MISS on a given request + L2Cache::L2CacheState L2Cache::cacheLookup_(olympia::MemoryAccessInfoPtr mem_access_info_ptr) { + const olympia::InstPtr & inst_ptr = mem_access_info_ptr->getInstPtr(); + uint64_t phyAddr = inst_ptr->getRAdr(); + + bool cache_hit = false; + + if (l2_always_hit_) { + cache_hit = true; + } + else { + auto cache_line = l2_cache_->peekLine(phyAddr); + cache_hit = (cache_line != nullptr) && cache_line->isValid(); + + // Update MRU replacement state if Cache HIT + if (cache_hit) { + l2_cache_->touchMRU(*cache_line); + } + } + + if (l2_always_hit_) { + ILOG("HIT all the time: phyAddr=0x" << std::hex << phyAddr); + ++l2_cache_hits_; + } + else if (cache_hit) { + ILOG("Cache HIT: phyAddr=0x" << std::hex << phyAddr); + ++l2_cache_hits_; + } + else { + ILOG("Cache MISS: phyAddr=0x" << std::hex << phyAddr); + ++l2_cache_misses_; + } + + return (cache_hit ? + (L2CacheState::HIT) + : (L2CacheState::MISS)); + } + + // Allocating the cacheline in the L2 based on return from BIU/L3 + void L2Cache::reloadCache_(uint64_t phyAddr) { + auto l2_cache_line = &l2_cache_->getLineForReplacementWithInvalidCheck(phyAddr); + l2_cache_->allocateWithMRUUpdate(*l2_cache_line, phyAddr); + } + + // Check if there are enough credits for the request to be issued to the l2cache_pipeline_ + bool L2Cache::hasCreditsForPipelineIssue_() { + + uint32_t num_free_biu_req_queue = biu_req_queue_size_ - biu_req_queue_.size(); + uint32_t num_free_miss_pending_buffer_ = miss_pending_buffer_.numFree(); + + uint32_t empty_slots = std::min(num_free_biu_req_queue, num_free_miss_pending_buffer_); + + DLOG("Inflight req : " << inFlight_reqs_ << " - Empty slots : " << empty_slots); + return (inFlight_reqs_ < empty_slots); + } +} diff --git a/mss/L2Cache.hpp b/mss/L2Cache.hpp new file mode 100644 index 00000000..9a999ebb --- /dev/null +++ b/mss/L2Cache.hpp @@ -0,0 +1,375 @@ +// -*- C++ -*- + + + +#pragma once + +#include +#include + +#include "sparta/ports/PortSet.hpp" +#include "sparta/ports/SignalPort.hpp" +#include "sparta/ports/DataPort.hpp" +#include "sparta/events/EventSet.hpp" +#include "sparta/events/UniqueEvent.hpp" +#include "sparta/events/SingleCycleUniqueEvent.hpp" +#include "sparta/simulation/Unit.hpp" +#include "sparta/simulation/ParameterSet.hpp" +#include "sparta/simulation/TreeNode.hpp" +#include "sparta/collection/Collectable.hpp" +#include "sparta/events/StartupEvent.hpp" +#include "sparta/ports/SyncPort.hpp" +#include "sparta/resources/Pipe.hpp" +#include "sparta/resources/Pipeline.hpp" + +#include "Inst.hpp" +#include "CoreTypes.hpp" +#include "MemoryAccessInfo.hpp" + +#include "CacheFuncModel.hpp" +#include "LSU.hpp" + +namespace olympia_mss +{ + class L2Cache : public sparta::Unit + { + public: + //! Parameters for L2Cache model + class L2CacheParameterSet : public sparta::ParameterSet { + public: + // Constructor for L2CacheParameterSet + L2CacheParameterSet(sparta::TreeNode* n): + sparta::ParameterSet(n) + { } + + PARAMETER(uint32_t, dcache_req_queue_size, 8, "DCache request queue size") + PARAMETER(uint32_t, dcache_resp_queue_size, 4, "DCache resp queue size") + PARAMETER(uint32_t, icache_req_queue_size, 8, "ICache request queue size") + PARAMETER(uint32_t, icache_resp_queue_size, 4, "ICache resp queue size") + PARAMETER(uint32_t, biu_req_queue_size, 4, "BIU request queue size") + PARAMETER(uint32_t, biu_resp_queue_size, 4, "BIU resp queue size") + PARAMETER(uint32_t, pipeline_req_queue_size, 64, "Pipeline request buffer size") + PARAMETER(uint32_t, miss_pending_buffer_size, 64, "Pipeline request buffer size") + + // Parameters for the L2 cache + PARAMETER(uint32_t, l2_line_size, 64, "L2 line size (power of 2)") + PARAMETER(uint32_t, l2_size_kb, 512, "Size of L2 in KB (power of 2)") + PARAMETER(uint32_t, l2_associativity, 16, "L2 associativity (power of 2)") + PARAMETER(bool, l2_always_hit, false, "L2 will always hit") + + PARAMETER(uint32_t, l2cache_latency, 10, "Cache Lookup HIT latency") + PARAMETER(bool, is_icache_connected, false, "Does this unit have ICache connected to it") + PARAMETER(bool, is_dcache_connected, true, "Does this unit have DCache connected to it") + }; + + // Constructor for L2Cache + // node parameter is the node that represent the L2Cache and p is the L2Cache parameter set + L2Cache(sparta::TreeNode* node, const L2CacheParameterSet* p); + + // name of this resource. + static const char name[]; + + //////////////////////////////////////////////////////////////////////////////// + // Type Name/Alias Declaration + //////////////////////////////////////////////////////////////////////////////// + + private: + + // Friend class used in rename testing + friend class L2CacheTester; + + //////////////////////////////////////////////////////////////////////////////// + // Statistics and Counters + //////////////////////////////////////////////////////////////////////////////// + // sparta::StatisticDef cycles_per_instr_; // A simple expression to calculate IPC + sparta::Counter num_reqs_from_dcache_; // Counter of number instructions received from DCache + sparta::Counter num_reqs_from_icache_; // Counter of number instructions received from ICache + sparta::Counter num_reqs_to_biu_; // Counter of number instructions forwarded to BIU -- Totals misses + sparta::Counter num_acks_from_biu_; // Counter of number acks received from BIU + sparta::Counter num_acks_to_icache_; // Counter of number acks provided to ICache + sparta::Counter num_acks_to_dcache_; // Counter of number acks provided to DCache + + sparta::Counter num_resps_from_biu_; // Counter of number responses received from BIU + sparta::Counter num_resps_to_icache_; // Counter of number responses provided to ICache + sparta::Counter num_resps_to_dcache_; // Counter of number responses provided to DCache + + sparta::Counter l2_cache_hits_; // Counter of number L2 Cache Hits + sparta::Counter l2_cache_misses_; // Counter of number L2 Cache Misses + + //////////////////////////////////////////////////////////////////////////////// + // Input Ports + //////////////////////////////////////////////////////////////////////////////// + + sparta::DataInPort in_dcache_l2cache_req_ + {&unit_port_set_, "in_dcache_l2cache_req", 1}; + + sparta::DataInPort in_icache_l2cache_req_ + {&unit_port_set_, "in_icache_l2cache_req", 1}; + + sparta::DataInPort in_biu_resp_ + {&unit_port_set_, "in_biu_l2cache_resp", 1}; + + sparta::DataInPort in_biu_ack_ + {&unit_port_set_, "in_biu_l2cache_ack", 1}; + + + //////////////////////////////////////////////////////////////////////////////// + // Output Ports + //////////////////////////////////////////////////////////////////////////////// + + sparta::DataOutPort out_biu_req_ + {&unit_port_set_, "out_l2cache_biu_req"}; + + sparta::DataOutPort out_l2cache_icache_resp_ + {&unit_port_set_, "out_l2cache_icache_resp"}; + + sparta::DataOutPort out_l2cache_dcache_resp_ + {&unit_port_set_, "out_l2cache_dcache_resp"}; + + sparta::DataOutPort out_l2cache_icache_ack_ + {&unit_port_set_, "out_l2cache_icache_ack"}; + + sparta::DataOutPort out_l2cache_dcache_ack_ + {&unit_port_set_, "out_l2cache_dcache_ack"}; + + + //////////////////////////////////////////////////////////////////////////////// + // Internal States + //////////////////////////////////////////////////////////////////////////////// + + + using CacheRequestQueue = std::vector; + + // Buffers for the incoming requests from DCache and ICache + CacheRequestQueue dcache_req_queue_; + CacheRequestQueue icache_req_queue_; + + const uint32_t dcache_req_queue_size_; + const uint32_t icache_req_queue_size_; + + // Buffers for the outgoing requests from L2Cache + CacheRequestQueue biu_req_queue_; + + const uint32_t biu_req_queue_size_; + + // Buffers for the incoming resps from BIU + CacheRequestQueue biu_resp_queue_; + + const uint32_t biu_resp_queue_size_; + + // Buffers for the outgoing resps to DCache and ICache + CacheRequestQueue dcache_resp_queue_; + CacheRequestQueue icache_resp_queue_; + + const uint32_t dcache_resp_queue_size_; + const uint32_t icache_resp_queue_size_; + + // Channels enum + enum class Channel : uint32_t + { + NO_ACCESS = 0, + __FIRST = NO_ACCESS, + BIU, + ICACHE, + DCACHE, + NUM_CHANNELS, + __LAST = NUM_CHANNELS + }; + + + // Cache Pipeline + class PipelineStages { + public: + PipelineStages(uint32_t latency) : NUM_STAGES(latency), + HIT_MISS_HANDLING(NUM_STAGES - 1), + CACHE_LOOKUP(HIT_MISS_HANDLING - 1) {} + + const uint32_t NUM_STAGES; + const uint32_t HIT_MISS_HANDLING; + const uint32_t CACHE_LOOKUP; + const uint32_t NO_ACCESS = 0; + }; + + using L2ArchUnit = olympia::MemoryAccessInfo::ArchUnit; + using L2CacheState = olympia::MemoryAccessInfo::CacheState; + using L2CachePipeline = sparta::Pipeline; + + PipelineStages stages_; + L2CachePipeline l2cache_pipeline_; + + sparta::Queue pipeline_req_queue_; + uint32_t inFlight_reqs_ = 0; + + sparta::Buffer miss_pending_buffer_; + const uint32_t miss_pending_buffer_size_; + + + // L2 Cache + using CacheHandle = olympia::CacheFuncModel::Handle; + CacheHandle l2_cache_; + + const uint32_t l2_lineSize_; + const uint32_t shiftBy_; + const bool l2_always_hit_; + + // Local state variables + uint32_t l2cache_biu_credits_ = 0; + Channel channel_select_ = Channel::ICACHE; + const uint32_t l2cache_latency_; + + const bool is_icache_connected_ = false; + const bool is_dcache_connected_ = false; + + // allocator for this object type + olympia::MemoryAccessInfoAllocator & memory_access_allocator_; + //////////////////////////////////////////////////////////////////////////////// + // Event Handlers + //////////////////////////////////////////////////////////////////////////////// + + // Event to handle L2Cache request from DCache + sparta::UniqueEvent<> ev_handle_dcache_l2cache_req_ + {&unit_event_set_, "ev_handle_dcache_l2cache_req", CREATE_SPARTA_HANDLER(L2Cache, handle_DCache_L2Cache_Req_)}; + + // Event to handle L2Cache request from ICache + sparta::UniqueEvent<> ev_handle_icache_l2cache_req_ + {&unit_event_set_, "ev_handle_icache_l2cache_req", CREATE_SPARTA_HANDLER(L2Cache, handle_ICache_L2Cache_Req_)}; + + // Event to handle L2Cache resp for ICache + sparta::UniqueEvent<> ev_handle_l2cache_icache_resp_ + {&unit_event_set_, "ev_handle_l2cache_icache_resp", CREATE_SPARTA_HANDLER(L2Cache, handle_L2Cache_ICache_Resp_)}; + + // Event to handle L2Cache resp for DCache + sparta::UniqueEvent<> ev_handle_l2cache_dcache_resp_ + {&unit_event_set_, "ev_handle_l2cache_dcache_resp", CREATE_SPARTA_HANDLER(L2Cache, handle_L2Cache_DCache_Resp_)}; + + // Event to handle L2Cache request to BIU + sparta::UniqueEvent<> ev_handle_l2cache_biu_req_ + {&unit_event_set_, "ev_handle_l2cache_biu_req", CREATE_SPARTA_HANDLER(L2Cache, handle_L2Cache_BIU_Req_)}; + + // Event to handle L2Cache request to BIU + sparta::UniqueEvent<> ev_handle_biu_l2cache_resp_ + {&unit_event_set_, "ev_handle_biu_l2cache_resp", CREATE_SPARTA_HANDLER(L2Cache, handle_BIU_L2Cache_Resp_)}; + + // Event to handle L2Cache ack for ICache + sparta::UniqueEvent<> ev_handle_l2cache_icache_ack_ + {&unit_event_set_, "ev_handle_l2cache_icache_ack", CREATE_SPARTA_HANDLER(L2Cache, handle_L2Cache_ICache_Ack_)}; + + // Event to handle L2Cache ack for DCache + sparta::UniqueEvent<> ev_handle_l2cache_dcache_ack_ + {&unit_event_set_, "ev_handle_l2cache_dcache_ack", CREATE_SPARTA_HANDLER(L2Cache, handle_L2Cache_DCache_Ack_)}; + + // Event to create request for pipeline and feed it to the pipeline_req_queue_ + sparta::UniqueEvent ev_create_req_ + {&unit_event_set_, "create_req", CREATE_SPARTA_HANDLER(L2Cache, create_Req_)}; + + // Event to issue request to pipeline + sparta::UniqueEvent ev_issue_req_ + {&unit_event_set_, "issue_req", CREATE_SPARTA_HANDLER(L2Cache, issue_Req_)}; + + //////////////////////////////////////////////////////////////////////////////// + // Callbacks + //////////////////////////////////////////////////////////////////////////////// + + // Receive new L2Cache request from DCache + void getReqFromDCache_(const olympia::InstPtr &); + + // Receive new L2Cache request from ICache + void getReqFromICache_(const olympia::InstPtr &); + + // Receive BIU access Response + void getRespFromBIU_(const olympia::InstPtr &); + + // Receive BIU ack Response + void getAckFromBIU_(const uint32_t &); + + // Handle L2Cache request from DCache + void handle_DCache_L2Cache_Req_(); + + // Handle L2Cache request from ICache + void handle_ICache_L2Cache_Req_(); + + // Handle L2Cache request to BIU + void handle_L2Cache_BIU_Req_(); + + // Handle L2Cahe resp for ICache + void handle_L2Cache_ICache_Resp_(); + + // Handle L2Cahe resp for DCache + void handle_L2Cache_DCache_Resp_(); + + // Handle L2Cahe ack for ICache + void handle_L2Cache_ICache_Ack_(); + + // Handle L2Cahe ack for DCache + void handle_L2Cache_DCache_Ack_(); + + // Handle BIU resp to L2Cache + void handle_BIU_L2Cache_Resp_(); + + + // Pipeline request create callback + void create_Req_(); + + // Pipeline request issue callback + void issue_Req_(); + + + // Pipeline callbacks + // Stage 1 + void handleCacheAccessRequest_(); + + // Stage 2 + void handleCacheAccessResult_(); + + // Sending Initial credits to I/D-Cache + void sendInitialCredits_(); + + //////////////////////////////////////////////////////////////////////////////// + // Regular Function/Subroutine Call + //////////////////////////////////////////////////////////////////////////////// + + // Append L2Cache request queue for reqs from DCache + void appendDCacheReqQueue_(const olympia::InstPtr &); + + // Append L2Cache request queue for reqs from ICache + void appendICacheReqQueue_(const olympia::InstPtr &); + + // Append L2Cache request queue for reqs to BIU + void appendBIUReqQueue_(const olympia::InstPtr &); + + // Append L2Cache resp queue for resps from BIU + void appendBIURespQueue_(const olympia::InstPtr &); + + // Append L2Cache resp queue for resps to DCache BIU + void appendDCacheRespQueue_(const olympia::InstPtr &); + + // Append L2Cache resp queue for resps to ICache + void appendICacheRespQueue_(const olympia::InstPtr &); + + + + // Select the channel to pick the request from + // Current options : + // BIU - P0 + // ICache - P1 - RoundRobin Candidate + // DCache - P1 - RoundRobin Candidate + Channel arbitrateL2CacheAccessReqs_(); + + // Cache lookup for a HIT or MISS on a given request + L2CacheState cacheLookup_(sparta::SpartaSharedPointer); + + // Allocating the cacheline in the L2 bbased on return from BIU/L3 + void reloadCache_(uint64_t); + + // Return the resp to the master units + void sendOutResp_(const L2ArchUnit&, const olympia::InstPtr&); + + // Send the request to the slave units + void sendOutReq_(const L2ArchUnit&, const olympia::InstPtr&); + + // Check if there are enough credits for the request to be issued to the l2cache_pipeline_ + bool hasCreditsForPipelineIssue_(); + }; + + class L2CacheTester; +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e812a002..291c8532 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -30,4 +30,5 @@ include_directories(${PROJECT_SOURCE_DIR}/..) add_subdirectory(sim) add_subdirectory(core/common) add_subdirectory(core/dispatch) +add_subdirectory(core/l2cache) add_subdirectory(core/rename) diff --git a/test/core/l2cache/BIUSinkUnit.hpp b/test/core/l2cache/BIUSinkUnit.hpp new file mode 100644 index 00000000..ed72857f --- /dev/null +++ b/test/core/l2cache/BIUSinkUnit.hpp @@ -0,0 +1,67 @@ + +#pragma once + +#include "sparta/simulation/TreeNode.hpp" +#include "sparta/events/SingleCycleUniqueEvent.hpp" +#include "sparta/utils/SpartaSharedPointer.hpp" +#include "sparta/utils/LogUtils.hpp" + +#include + +namespace l2cache_test +{ + // //////////////////////////////////////////////////////////////////////////////// + // "Sink" unit, just sinks instructions sent to it. Sends credits + // back as directed by params/execution mode + class BIUSinkUnit : public sparta::Unit + { + public: + static constexpr char name[] = "BIUSinkUnit"; + + class BIUSinkUnitParameters : public sparta::ParameterSet + { + public: + explicit BIUSinkUnitParameters(sparta::TreeNode *n) : + sparta::ParameterSet(n) + { } + PARAMETER(std::string, purpose, "grp", "Purpose of this SinkUnit: grp, single") + PARAMETER(sparta::Clock::Cycle, sink_latency, 10, "Latency of this SinkUnit") + }; + + BIUSinkUnit(sparta::TreeNode * n, const BIUSinkUnitParameters * params) : sparta::Unit(n) { + + purpose_ = params->purpose; + sink_latency_ = params->sink_latency; + + in_biu_req_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(BIUSinkUnit, sinkInst_, olympia::InstPtr)); + + sparta::StartupEvent(n, CREATE_SPARTA_HANDLER(BIUSinkUnit, sendInitialCredits_)); + } + private: + + // Sending Initial credits to L2Cache + void sendInitialCredits_() { + uint32_t biu_req_queue_size_ = 32; + out_biu_ack_.send(biu_req_queue_size_); + ILOG("Sending initial credits to L2Cache : " << biu_req_queue_size_); + } + + void sinkInst_(const olympia::InstPtr & instPtr) { + ILOG("Instruction: '" << instPtr << "' sinked"); + + uint32_t biu_req_queue_size_ = 32; + + out_biu_ack_.send(biu_req_queue_size_, sink_latency_); + out_biu_resp_.send(instPtr, 2*sink_latency_); + } + + sparta::DataInPort in_biu_req_ {&unit_port_set_, "in_biu_req", + sparta::SchedulingPhase::Tick, 1}; + sparta::DataOutPort out_biu_resp_ {&unit_port_set_, "out_biu_resp"}; + sparta::DataOutPort out_biu_ack_ {&unit_port_set_, "out_biu_ack"}; + + std::string purpose_; + sparta::Clock::Cycle sink_latency_; + }; +} diff --git a/test/core/l2cache/CMakeLists.txt b/test/core/l2cache/CMakeLists.txt new file mode 100644 index 00000000..22aa7020 --- /dev/null +++ b/test/core/l2cache/CMakeLists.txt @@ -0,0 +1,15 @@ +project(L2Cache_test) + +add_executable(L2Cache_test L2Cache_test.cpp) + +target_link_libraries(L2Cache_test core common_test mss ${STF_LINK_LIBS} SPARTA::sparta) + +file(CREATE_LINK ${SIM_BASE}/mavis/json ${CMAKE_CURRENT_BINARY_DIR}/mavis_isa_files SYMBOLIC) +file(CREATE_LINK ${SIM_BASE}/arches ${CMAKE_CURRENT_BINARY_DIR}/arches SYMBOLIC) +file(CREATE_LINK ${CMAKE_CURRENT_SOURCE_DIR}/test_arches ${CMAKE_CURRENT_BINARY_DIR}/test_arches SYMBOLIC) +file(CREATE_LINK ${CMAKE_CURRENT_SOURCE_DIR}/expected_output ${CMAKE_CURRENT_BINARY_DIR}/expected_output SYMBOLIC) +file(CREATE_LINK ${CMAKE_CURRENT_SOURCE_DIR}/single_access.json ${CMAKE_CURRENT_BINARY_DIR}/single_access.json SYMBOLIC) +file(CREATE_LINK ${CMAKE_CURRENT_SOURCE_DIR}/hit_case.json ${CMAKE_CURRENT_BINARY_DIR}/hit_case.json SYMBOLIC) + +sparta_named_test(L2Cache_test_single_access L2Cache_test single_access.out -c test_arches/2_src_L2Cache.yaml --input-file single_access.json) +sparta_named_test(L2Cache_test_hit_case L2Cache_test hit_case.out -c test_arches/2_src_L2Cache.yaml --input-file hit_case.json) diff --git a/test/core/l2cache/L2Cache_test.cpp b/test/core/l2cache/L2Cache_test.cpp new file mode 100644 index 00000000..5028a7c8 --- /dev/null +++ b/test/core/l2cache/L2Cache_test.cpp @@ -0,0 +1,223 @@ + +#include "L2Cache.hpp" +#include "MavisUnit.hpp" +#include "CoreUtils.hpp" + +#include "test/core/common/SourceUnit.hpp" +#include "test/core/common/SinkUnit.hpp" + +#include "test/core/l2cache/BIUSinkUnit.hpp" +#include "test/core/l2cache/L2SourceUnit.hpp" + +#include "sparta/app/CommandLineSimulator.hpp" +#include "sparta/utils/SpartaTester.hpp" +#include "sparta/resources/Buffer.hpp" +#include "sparta/sparta.hpp" +#include "sparta/simulation/ClockManager.hpp" +#include "sparta/kernel/Scheduler.hpp" +#include "sparta/utils/SpartaTester.hpp" +#include "sparta/statistics/StatisticSet.hpp" +#include "sparta/report/Report.hpp" +#include "sparta/events/UniqueEvent.hpp" +#include "sparta/app/Simulation.hpp" +#include "sparta/utils/SpartaSharedPointer.hpp" +#include "OlympiaAllocators.hpp" + +#include +#include +#include +#include +#include + +TEST_INIT + +// +// Simple L2Cache Simulator. +// +// SourceUnit 0 <-> L2Cache <-> BIUSinkUnit +// ^ +// | +// | +// SourceUnit 1 <--- +// +class L2CacheSim : public sparta::app::Simulation +{ +public: + + L2CacheSim(sparta::Scheduler * sched, + const std::string & mavis_isa_files, + const std::string & mavis_uarch_files, + const std::string & output_file, + const std::string & input_file) : + sparta::app::Simulation("L2CacheSim", sched), + input_file_(input_file), + test_tap_(getRoot(), "info", output_file) + { + } + + ~L2CacheSim() { + getRoot()->enterTeardown(); + } + + void runRaw(uint64_t run_time) override final + { + (void) run_time; // ignored + + sparta::app::Simulation::runRaw(run_time); + } + +private: + + void buildTree_() override + { + auto rtn = getRoot(); + + // Cerate the common Allocators + allocators_tn_.reset(new olympia::OlympiaAllocators(rtn)); + + // Create a Mavis Unit + sparta::ResourceTreeNode * mavis = new sparta::ResourceTreeNode(rtn, + olympia::MavisUnit::name, + sparta::TreeNode::GROUP_NAME_NONE, + sparta::TreeNode::GROUP_IDX_NONE, + "Mavis Unit", + &mavis_fact); + tns_to_delete_.emplace_back(mavis); + + // Create a Source Units that represents DCache and ICache + sparta::ResourceTreeNode * Test_DCache = new sparta::ResourceTreeNode(rtn, + "dcache", + sparta::TreeNode::GROUP_NAME_NONE, + sparta::TreeNode::GROUP_IDX_NONE, + "dcache", + &dcache_fact); + Test_DCache->getParameterSet()->getParameter("input_file")->setValueFromString(input_file_); + tns_to_delete_.emplace_back(Test_DCache); + + sparta::ResourceTreeNode * Test_ICache = new sparta::ResourceTreeNode(rtn, + "icache", + sparta::TreeNode::GROUP_NAME_NONE, + sparta::TreeNode::GROUP_IDX_NONE, + "icache", + &icache_fact); + Test_ICache->getParameterSet()->getParameter("input_file")->setValueFromString(input_file_); + tns_to_delete_.emplace_back(Test_ICache); + + // Create L2Cache + sparta::ResourceTreeNode * L2CacheUnit = new sparta::ResourceTreeNode(rtn, + "l2cache", + sparta::TreeNode::GROUP_NAME_NONE, + sparta::TreeNode::GROUP_IDX_NONE, + "l2cache", + &l2cache_fact); + tns_to_delete_.emplace_back(L2CacheUnit); + + // Create SinkUnit that represents the BIU + sparta::ResourceTreeNode * Test_BIU = new sparta::ResourceTreeNode(rtn, + "biu", + sparta::TreeNode::GROUP_NAME_NONE, + sparta::TreeNode::GROUP_IDX_NONE, + "biu", + &biu_fact); + tns_to_delete_.emplace_back(Test_BIU); + } + + void configureTree_() override { } + + void bindTree_() override + { + auto * root_node = getRoot(); + + // Bind the "ROB" (simple SinkUnit that accepts instruction + // groups) to Dispatch + sparta::bind(root_node->getChildAs("dcache.ports.out_source_req"), + root_node->getChildAs("l2cache.ports.in_dcache_l2cache_req")); + sparta::bind(root_node->getChildAs("dcache.ports.in_source_resp"), + root_node->getChildAs("l2cache.ports.out_l2cache_dcache_resp")); + sparta::bind(root_node->getChildAs("dcache.ports.in_source_ack"), + root_node->getChildAs("l2cache.ports.out_l2cache_dcache_ack")); + + sparta::bind(root_node->getChildAs("icache.ports.out_source_req"), + root_node->getChildAs("l2cache.ports.in_icache_l2cache_req")); + sparta::bind(root_node->getChildAs("icache.ports.in_source_resp"), + root_node->getChildAs("l2cache.ports.out_l2cache_icache_resp")); + sparta::bind(root_node->getChildAs("icache.ports.in_source_ack"), + root_node->getChildAs("l2cache.ports.out_l2cache_icache_ack")); + + sparta::bind(root_node->getChildAs("biu.ports.in_biu_req"), + root_node->getChildAs("l2cache.ports.out_l2cache_biu_req")); + sparta::bind(root_node->getChildAs("biu.ports.out_biu_resp"), + root_node->getChildAs("l2cache.ports.in_biu_l2cache_resp")); + sparta::bind(root_node->getChildAs("biu.ports.out_biu_ack"), + root_node->getChildAs("l2cache.ports.in_biu_l2cache_ack")); + } + // Allocators. Last thing to delete + std::unique_ptr allocators_tn_; + + sparta::ResourceFactory dcache_fact; + sparta::ResourceFactory icache_fact; + + sparta::ResourceFactory l2cache_fact; + + sparta::ResourceFactory biu_fact; + + olympia::MavisFactoy mavis_fact; + std::vector> tns_to_delete_; + + const std::string input_file_; + sparta::log::Tap test_tap_; +}; + +const char USAGE[] = + "Usage:\n" + " \n" + "\n"; + +sparta::app::DefaultValues DEFAULTS; + +// The main tester of L2Cache. The test is encapsulated in the +// parameter test_type of the Source unit. +void runTest(int argc, char **argv) +{ + DEFAULTS.auto_summary_default = "off"; + std::vector datafiles; + std::string input_file; + + sparta::app::CommandLineSimulator cls(USAGE, DEFAULTS); + auto & app_opts = cls.getApplicationOptions(); + app_opts.add_options() + ("output_file", + sparta::app::named_value>("output_file", &datafiles), + "Specifies the output file") + ("input-file", + sparta::app::named_value("INPUT_FILE", &input_file)->default_value(""), + "Provide a JSON instruction stream", + "Provide a JSON file with instructions to run through Execute"); + + po::positional_options_description& pos_opts = cls.getPositionalOptions(); + pos_opts.add("output_file", -1); // example, look for the at the end + + int err_code = 0; + if(!cls.parse(argc, argv, err_code)){ + sparta_assert(false, "Command line parsing failed"); // Any errors already printed to cerr + } + + sparta_assert(false == datafiles.empty(), "Need an output file as the last argument of the test"); + + sparta::Scheduler sched; + L2CacheSim l2cache_sim(&sched, "mavis_isa_files", "arches/isa_json", datafiles[0], input_file); + + cls.populateSimulation(&l2cache_sim); + + cls.runSimulator(&l2cache_sim); + + EXPECT_FILES_EQUAL(datafiles[0], "expected_output/" + datafiles[0] + ".EXPECTED"); +} + +int main(int argc, char **argv) +{ + runTest(argc, argv); + + REPORT_ERROR; + return (int)ERROR_CODE; +} diff --git a/test/core/l2cache/L2SourceUnit.hpp b/test/core/l2cache/L2SourceUnit.hpp new file mode 100644 index 00000000..f60f7ec1 --- /dev/null +++ b/test/core/l2cache/L2SourceUnit.hpp @@ -0,0 +1,125 @@ +#pragma once + + +#include "core/InstGenerator.hpp" +#include "core/MavisUnit.hpp" +#include "mavis/ExtractorDirectInfo.h" +#include "sparta/simulation/TreeNode.hpp" +#include "sparta/events/SingleCycleUniqueEvent.hpp" +#include "sparta/utils/SpartaSharedPointer.hpp" +#include "sparta/utils/LogUtils.hpp" + +#include + +namespace l2cache_test +{ + // //////////////////////////////////////////////////////////////////////////////// + // "Source" unit, just Sources instructions sent to it. Sends credits + // back as directed by params/execution mode + class L2SourceUnit : public sparta::Unit + { + public: + static constexpr char name[] = "L2SourceUnit"; + + class L2SourceUnitParameters : public sparta::ParameterSet + { + public: + explicit L2SourceUnitParameters(sparta::TreeNode *n) : + sparta::ParameterSet(n) + { } + PARAMETER(bool, unit_enable, true, "Is this unit enabled?") + PARAMETER(std::string, input_file, "", "Input file: STF or JSON") + PARAMETER(sparta::Clock::Cycle, delay_btwn_insts, 50, "Clock delay between instruction/requests to L2Cache") + }; + + L2SourceUnit(sparta::TreeNode * n, const L2SourceUnitParameters * params) + : sparta::Unit(n), + mavis_facade_(olympia::getMavis(n)), + delay_btwn_insts_(params->delay_btwn_insts), + unit_enable_(params->unit_enable) { + + sparta_assert(mavis_facade_ != nullptr, "Could not find the Mavis Unit"); + + in_source_resp_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(L2SourceUnit, ReceiveInst_, olympia::InstPtr)); + in_source_ack_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(L2SourceUnit, ReceiveAck_, uint32_t)); + + if(params->input_file != "") { + inst_generator_ = olympia::InstGenerator::createGenerator(mavis_facade_, params->input_file, false); + } + + if (unit_enable_ == true) + sparta::StartupEvent(n, CREATE_SPARTA_HANDLER(L2SourceUnit, injectInsts_)); + } + + ~L2SourceUnit() { + sparta_assert(unit_enable_ == true && pending_reqs_ == 0, "pending_reqs remaining in the L2SourceUnit") + sparta_assert(unit_enable_ == true && pending_acks_ == 0, "pending_acks remaining in the L2SourceUnit") + } + + private: + + void injectInsts_() { + olympia::InstPtr dinst; + + if(inst_generator_) { + + while (!inst_generator_->isDone()) { + + dinst = inst_generator_->getNextInst(getClock()); + dinst->setUniqueID(unique_id_++); + + req_inst_queue_.emplace_back(dinst); + ev_req_inst_.schedule(schedule_time_); + + schedule_time_ += delay_btwn_insts_; + } + } + } + + void req_inst_() { + + ILOG("Instruction: '" << req_inst_queue_.front() << "' Requested"); + + pending_reqs_++; + pending_acks_++; + + out_source_req_.send(req_inst_queue_.front()); + req_inst_queue_.erase(req_inst_queue_.begin()); + } + + void ReceiveInst_(const olympia::InstPtr & instPtr) { + pending_reqs_--; + ILOG("Instruction: '" << instPtr << "' Received"); + } + + void ReceiveAck_(const uint32_t & ack) { + pending_acks_--; + ILOG("Ack: '" << ack << "' Received"); + } + + sparta::DataInPort in_source_resp_ {&unit_port_set_, "in_source_resp", + sparta::SchedulingPhase::Tick, 1}; + sparta::DataInPort in_source_ack_ {&unit_port_set_, "in_source_ack"}; + + sparta::DataOutPort out_source_req_ {&unit_port_set_, "out_source_req"}; + + uint32_t pending_acks_ = 1; + uint32_t pending_reqs_ = 0; + + uint32_t unique_id_ = 0; + + olympia::MavisType * mavis_facade_ = nullptr; + std::unique_ptr inst_generator_; + + // Event to issue request to L2Cache + sparta::UniqueEvent<> ev_req_inst_ + {&unit_event_set_, "req_inst", CREATE_SPARTA_HANDLER(L2SourceUnit, req_inst_)}; + + std::vector req_inst_queue_; + sparta::Clock::Cycle schedule_time_ = 0; + sparta::Clock::Cycle delay_btwn_insts_ = 0; + bool unit_enable_; + }; +} \ No newline at end of file diff --git a/test/core/l2cache/expected_output/hit_case.out.EXPECTED b/test/core/l2cache/expected_output/hit_case.out.EXPECTED new file mode 100644 index 00000000..abf6f485 --- /dev/null +++ b/test/core/l2cache/expected_output/hit_case.out.EXPECTED @@ -0,0 +1,91 @@ +#Name: +#Cmdline: +#Exe: +#SimulatorVersion: +#Repro: +#Start: Friday Fri Nov 17 11:05:39 2023 +#Elapsed: 0.074727s +{0000000000 00000000 top.l2cache info} L2Cache: L2Cache construct: #4294967295 +{0000000000 00000000 top.l2cache info} sendInitialCredits_: Sending initial credits to ICache : 8 +{0000000000 00000000 top.l2cache info} sendInitialCredits_: Sending initial credits to DCache : 8 +{0000000000 00000000 top.biu info} sendInitialCredits_: Sending initial credits to L2Cache : 32 +{0000000000 00000000 top.icache info} ReceiveAck_: Ack: '8' Received +{0000000000 00000000 top.dcache info} ReceiveAck_: Ack: '8' Received +{0000000000 00000000 top.dcache info} req_inst_: Instruction: 'uid: 0 FETCHED 0 pid: 1 'sw 3' ' Requested +{0000000000 00000000 top.icache info} req_inst_: Instruction: 'uid: 0 FETCHED 0 pid: 1 'sw 3' ' Requested +{0000000001 00000001 top.l2cache info} getAckFromBIU_: Ack received from BIU on the port : Current BIU credit available = 32 +{0000000001 00000001 top.l2cache info} getReqFromDCache_: Request received from DCache on the port +{0000000001 00000001 top.l2cache info} appendDCacheReqQueue_: Append DCache->L2Cache request queue! +{0000000001 00000001 top.l2cache info} getReqFromICache_: Request received from ICache on the port +{0000000001 00000001 top.l2cache info} appendICacheReqQueue_: Append ICache->L2Cache request queue! +{0000000001 00000001 top.l2cache info} arbitrateL2CacheAccessReqs_: Arbitration winner - ICache +{0000000001 00000001 top.l2cache info} create_Req_: ICache request is sent to Pipeline_req_Q! +{0000000002 00000002 top.icache info} ReceiveAck_: Ack: '8' Received +{0000000002 00000002 top.l2cache info} handle_L2Cache_ICache_Ack_: L2Cache->ICache : Ack is sent. +{0000000002 00000002 top.l2cache info} issue_Req_: Request is sent to Pipeline! SrcUnit : ICACHE +{0000000002 00000002 top.l2cache info} arbitrateL2CacheAccessReqs_: Arbitration winner - DCache +{0000000002 00000002 top.l2cache info} create_Req_: DCache request is sent to Pipeline_req_Q! +{0000000003 00000003 top.dcache info} ReceiveAck_: Ack: '8' Received +{0000000003 00000003 top.l2cache info} handle_L2Cache_DCache_Ack_: L2Cache->DCache : Ack is sent. +{0000000003 00000003 top.l2cache info} issue_Req_: Request is sent to Pipeline! SrcUnit : DCACHE +{0000000011 00000011 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000011 00000011 top.l2cache info} cacheLookup_: Cache MISS: phyAddr=0xdeadbeef +{0000000012 00000012 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000012 00000012 top.l2cache info} cacheLookup_: Cache MISS: phyAddr=0xdeadbeef +{0000000012 00000012 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000012 00000012 top.l2cache info} handleCacheAccessResult_: Storing the CACHE MISS in miss_pending_buffer_ +{0000000012 00000012 top.l2cache info} appendBIUReqQueue_: Append L2Cache->BIU req queue +{0000000012 00000012 top.l2cache info} handle_L2Cache_BIU_Req_: L2Cache Request sent to BIU : Current BIU credit available = 31 +{0000000013 00000013 top.biu info} sinkInst_: Instruction: 'uid: 0 FETCHED 0 pid: 1 'sw 3' ' sinked +{0000000013 00000013 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000013 00000013 top.l2cache info} handleCacheAccessResult_: Storing the CACHE MISS in miss_pending_buffer_ +{0000000024 00000024 top.l2cache info} getAckFromBIU_: Ack received from BIU on the port : Current BIU credit available = 32 +{0000000034 00000034 top.l2cache info} getRespFromBIU_: Response received from BIU on the port +{0000000034 00000034 top.l2cache info} appendBIURespQueue_: Append BIU->L2Cache resp queue! +{0000000034 00000034 top.l2cache info} arbitrateL2CacheAccessReqs_: Arbitration winner - BIU +{0000000034 00000034 top.l2cache info} create_Req_: Request found in miss_pending_buffer_ with SrcUnit : ICACHE +{0000000035 00000035 top.l2cache info} issue_Req_: Request is sent to Pipeline! SrcUnit : BIU +{0000000035 00000035 top.l2cache info} arbitrateL2CacheAccessReqs_: Arbitration winner - BIU +{0000000035 00000035 top.l2cache info} create_Req_: Request found in miss_pending_buffer_ with SrcUnit : DCACHE +{0000000036 00000036 top.l2cache info} issue_Req_: Request is sent to Pipeline! SrcUnit : BIU +{0000000044 00000044 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000044 00000044 top.l2cache info} cacheLookup_: Cache MISS: phyAddr=0xdeadbeef +{0000000044 00000044 top.l2cache info} handleCacheAccessRequest_: Reload Complete: phyAddr=0xdeadbeef +{0000000045 00000045 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000045 00000045 top.l2cache info} cacheLookup_: Cache HIT: phyAddr=0xdeadbeef +{0000000045 00000045 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000045 00000045 top.l2cache info} appendICacheRespQueue_: Append L2Cache->ICache resp queue! +{0000000045 00000045 top.l2cache info} handle_L2Cache_ICache_Resp_: L2Cache Resp is sent to ICache! +{0000000046 00000046 top.icache info} ReceiveInst_: Instruction: 'uid: 0 FETCHED 0 pid: 1 'sw 3' ' Received +{0000000046 00000046 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000046 00000046 top.l2cache info} appendDCacheRespQueue_: Append L2Cache->DCache resp queue! +{0000000046 00000046 top.l2cache info} handle_L2Cache_DCache_Resp_: L2Cache Resp is sent to DCache! +{0000000047 00000047 top.dcache info} ReceiveInst_: Instruction: 'uid: 0 FETCHED 0 pid: 1 'sw 3' ' Received +{0000000050 00000050 top.dcache info} req_inst_: Instruction: 'uid: 1 FETCHED 0 pid: 2 'lw 5,3,4' ' Requested +{0000000050 00000050 top.icache info} req_inst_: Instruction: 'uid: 1 FETCHED 0 pid: 2 'lw 5,3,4' ' Requested +{0000000051 00000051 top.l2cache info} getReqFromDCache_: Request received from DCache on the port +{0000000051 00000051 top.l2cache info} appendDCacheReqQueue_: Append DCache->L2Cache request queue! +{0000000051 00000051 top.l2cache info} getReqFromICache_: Request received from ICache on the port +{0000000051 00000051 top.l2cache info} appendICacheReqQueue_: Append ICache->L2Cache request queue! +{0000000051 00000051 top.l2cache info} arbitrateL2CacheAccessReqs_: Arbitration winner - ICache +{0000000051 00000051 top.l2cache info} create_Req_: ICache request is sent to Pipeline_req_Q! +{0000000052 00000052 top.icache info} ReceiveAck_: Ack: '8' Received +{0000000052 00000052 top.l2cache info} handle_L2Cache_ICache_Ack_: L2Cache->ICache : Ack is sent. +{0000000052 00000052 top.l2cache info} issue_Req_: Request is sent to Pipeline! SrcUnit : ICACHE +{0000000052 00000052 top.l2cache info} arbitrateL2CacheAccessReqs_: Arbitration winner - DCache +{0000000052 00000052 top.l2cache info} create_Req_: DCache request is sent to Pipeline_req_Q! +{0000000053 00000053 top.dcache info} ReceiveAck_: Ack: '8' Received +{0000000053 00000053 top.l2cache info} handle_L2Cache_DCache_Ack_: L2Cache->DCache : Ack is sent. +{0000000053 00000053 top.l2cache info} issue_Req_: Request is sent to Pipeline! SrcUnit : DCACHE +{0000000061 00000061 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : uid: 1 FETCHED 0 pid: 2 'lw 5,3,4' +{0000000061 00000061 top.l2cache info} cacheLookup_: Cache HIT: phyAddr=0xdeadbeef +{0000000062 00000062 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : uid: 1 FETCHED 0 pid: 2 'lw 5,3,4' +{0000000062 00000062 top.l2cache info} cacheLookup_: Cache HIT: phyAddr=0xdeadbeef +{0000000062 00000062 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : uid: 1 FETCHED 0 pid: 2 'lw 5,3,4' +{0000000062 00000062 top.l2cache info} appendICacheRespQueue_: Append L2Cache->ICache resp queue! +{0000000062 00000062 top.l2cache info} handle_L2Cache_ICache_Resp_: L2Cache Resp is sent to ICache! +{0000000063 00000063 top.icache info} ReceiveInst_: Instruction: 'uid: 1 FETCHED 0 pid: 2 'lw 5,3,4' ' Received +{0000000063 00000063 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : uid: 1 FETCHED 0 pid: 2 'lw 5,3,4' +{0000000063 00000063 top.l2cache info} appendDCacheRespQueue_: Append L2Cache->DCache resp queue! +{0000000063 00000063 top.l2cache info} handle_L2Cache_DCache_Resp_: L2Cache Resp is sent to DCache! +{0000000064 00000064 top.dcache info} ReceiveInst_: Instruction: 'uid: 1 FETCHED 0 pid: 2 'lw 5,3,4' ' Received diff --git a/test/core/l2cache/expected_output/single_access.out.EXPECTED b/test/core/l2cache/expected_output/single_access.out.EXPECTED new file mode 100644 index 00000000..0a296dc2 --- /dev/null +++ b/test/core/l2cache/expected_output/single_access.out.EXPECTED @@ -0,0 +1,63 @@ +#Name: +#Cmdline: +#Exe: +#SimulatorVersion: +#Repro: +#Start: Friday Fri Nov 17 11:01:07 2023 +#Elapsed: 0.082742s +{0000000000 00000000 top.l2cache info} L2Cache: L2Cache construct: #4294967295 +{0000000000 00000000 top.l2cache info} sendInitialCredits_: Sending initial credits to ICache : 8 +{0000000000 00000000 top.l2cache info} sendInitialCredits_: Sending initial credits to DCache : 8 +{0000000000 00000000 top.biu info} sendInitialCredits_: Sending initial credits to L2Cache : 32 +{0000000000 00000000 top.icache info} ReceiveAck_: Ack: '8' Received +{0000000000 00000000 top.dcache info} ReceiveAck_: Ack: '8' Received +{0000000000 00000000 top.dcache info} req_inst_: Instruction: 'uid: 0 FETCHED 0 pid: 1 'sw 3' ' Requested +{0000000000 00000000 top.icache info} req_inst_: Instruction: 'uid: 0 FETCHED 0 pid: 1 'sw 3' ' Requested +{0000000001 00000001 top.l2cache info} getAckFromBIU_: Ack received from BIU on the port : Current BIU credit available = 32 +{0000000001 00000001 top.l2cache info} getReqFromDCache_: Request received from DCache on the port +{0000000001 00000001 top.l2cache info} appendDCacheReqQueue_: Append DCache->L2Cache request queue! +{0000000001 00000001 top.l2cache info} getReqFromICache_: Request received from ICache on the port +{0000000001 00000001 top.l2cache info} appendICacheReqQueue_: Append ICache->L2Cache request queue! +{0000000001 00000001 top.l2cache info} arbitrateL2CacheAccessReqs_: Arbitration winner - ICache +{0000000001 00000001 top.l2cache info} create_Req_: ICache request is sent to Pipeline_req_Q! +{0000000002 00000002 top.icache info} ReceiveAck_: Ack: '8' Received +{0000000002 00000002 top.l2cache info} handle_L2Cache_ICache_Ack_: L2Cache->ICache : Ack is sent. +{0000000002 00000002 top.l2cache info} issue_Req_: Request is sent to Pipeline! SrcUnit : ICACHE +{0000000002 00000002 top.l2cache info} arbitrateL2CacheAccessReqs_: Arbitration winner - DCache +{0000000002 00000002 top.l2cache info} create_Req_: DCache request is sent to Pipeline_req_Q! +{0000000003 00000003 top.dcache info} ReceiveAck_: Ack: '8' Received +{0000000003 00000003 top.l2cache info} handle_L2Cache_DCache_Ack_: L2Cache->DCache : Ack is sent. +{0000000003 00000003 top.l2cache info} issue_Req_: Request is sent to Pipeline! SrcUnit : DCACHE +{0000000011 00000011 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000011 00000011 top.l2cache info} cacheLookup_: Cache MISS: phyAddr=0xdeadbeef +{0000000012 00000012 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000012 00000012 top.l2cache info} cacheLookup_: Cache MISS: phyAddr=0xdeadbeef +{0000000012 00000012 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000012 00000012 top.l2cache info} handleCacheAccessResult_: Storing the CACHE MISS in miss_pending_buffer_ +{0000000012 00000012 top.l2cache info} appendBIUReqQueue_: Append L2Cache->BIU req queue +{0000000012 00000012 top.l2cache info} handle_L2Cache_BIU_Req_: L2Cache Request sent to BIU : Current BIU credit available = 31 +{0000000013 00000013 top.biu info} sinkInst_: Instruction: 'uid: 0 FETCHED 0 pid: 1 'sw 3' ' sinked +{0000000013 00000013 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000013 00000013 top.l2cache info} handleCacheAccessResult_: Storing the CACHE MISS in miss_pending_buffer_ +{0000000024 00000024 top.l2cache info} getAckFromBIU_: Ack received from BIU on the port : Current BIU credit available = 32 +{0000000034 00000034 top.l2cache info} getRespFromBIU_: Response received from BIU on the port +{0000000034 00000034 top.l2cache info} appendBIURespQueue_: Append BIU->L2Cache resp queue! +{0000000034 00000034 top.l2cache info} arbitrateL2CacheAccessReqs_: Arbitration winner - BIU +{0000000034 00000034 top.l2cache info} create_Req_: Request found in miss_pending_buffer_ with SrcUnit : ICACHE +{0000000035 00000035 top.l2cache info} issue_Req_: Request is sent to Pipeline! SrcUnit : BIU +{0000000035 00000035 top.l2cache info} arbitrateL2CacheAccessReqs_: Arbitration winner - BIU +{0000000035 00000035 top.l2cache info} create_Req_: Request found in miss_pending_buffer_ with SrcUnit : DCACHE +{0000000036 00000036 top.l2cache info} issue_Req_: Request is sent to Pipeline! SrcUnit : BIU +{0000000044 00000044 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000044 00000044 top.l2cache info} cacheLookup_: Cache MISS: phyAddr=0xdeadbeef +{0000000044 00000044 top.l2cache info} handleCacheAccessRequest_: Reload Complete: phyAddr=0xdeadbeef +{0000000045 00000045 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000045 00000045 top.l2cache info} cacheLookup_: Cache HIT: phyAddr=0xdeadbeef +{0000000045 00000045 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000045 00000045 top.l2cache info} appendICacheRespQueue_: Append L2Cache->ICache resp queue! +{0000000045 00000045 top.l2cache info} handle_L2Cache_ICache_Resp_: L2Cache Resp is sent to ICache! +{0000000046 00000046 top.icache info} ReceiveInst_: Instruction: 'uid: 0 FETCHED 0 pid: 1 'sw 3' ' Received +{0000000046 00000046 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000046 00000046 top.l2cache info} appendDCacheRespQueue_: Append L2Cache->DCache resp queue! +{0000000046 00000046 top.l2cache info} handle_L2Cache_DCache_Resp_: L2Cache Resp is sent to DCache! +{0000000047 00000047 top.dcache info} ReceiveInst_: Instruction: 'uid: 0 FETCHED 0 pid: 1 'sw 3' ' Received diff --git a/test/core/l2cache/hit_case.json b/test/core/l2cache/hit_case.json new file mode 100644 index 00000000..68cfe280 --- /dev/null +++ b/test/core/l2cache/hit_case.json @@ -0,0 +1,14 @@ +[ + { + "mnemonic": "sw", + "rs2": 3, + "vaddr" : "0xdeadbeef" + }, + { + "mnemonic": "lw", + "rs1": 3, + "rs2": 4, + "rd": 5, + "vaddr" : "0xdeadbeef" + } +] \ No newline at end of file diff --git a/test/core/l2cache/single_access.json b/test/core/l2cache/single_access.json new file mode 100644 index 00000000..46a55a69 --- /dev/null +++ b/test/core/l2cache/single_access.json @@ -0,0 +1,7 @@ +[ + { + "mnemonic": "sw", + "rs2": 3, + "vaddr" : "0xdeadbeef" + } +] \ No newline at end of file diff --git a/test/core/l2cache/test_arches/2_src_L2Cache.yaml b/test/core/l2cache/test_arches/2_src_L2Cache.yaml new file mode 100644 index 00000000..0282ccd4 --- /dev/null +++ b/test/core/l2cache/test_arches/2_src_L2Cache.yaml @@ -0,0 +1,28 @@ +top: + icache: + params: + unit_enable: true + delay_btwn_insts: 50 + dcache: + params: + unit_enable: true + delay_btwn_insts: 50 + l2cache: + params: + dcache_req_queue_size: 8 + dcache_resp_queue_size: 4 + icache_req_queue_size: 8 + icache_resp_queue_size: 4 + biu_req_queue_size: 8 + biu_resp_queue_size: 4 + miss_pending_buffer_size : 2 + l2_line_size : 64 + l2_size_kb : 512 + l2_associativity : 16 + l2_always_hit : false + l2cache_latency : 10 + is_icache_connected : true + is_dcache_connected : true + biu: + params: + sink_latency: 10