From 13edff8133fb669aeb3278afdb274672eec8d3d5 Mon Sep 17 00:00:00 2001 From: Daniel Bone Date: Thu, 8 Feb 2024 13:55:40 +0000 Subject: [PATCH 1/7] Add L1 Instruction Cache --- core/CMakeLists.txt | 1 + core/ICache.cpp | 241 ++++++++++++++++++ core/ICache.hpp | 144 +++++++++++ core/MemoryAccessInfo.hpp | 34 ++- test/CMakeLists.txt | 1 + test/core/icache/CMakeLists.txt | 8 + test/core/icache/ICacheChecker.hpp | 192 ++++++++++++++ test/core/icache/ICacheSink.hpp | 155 +++++++++++ test/core/icache/ICacheSource.hpp | 121 +++++++++ test/core/icache/ICache_test.cpp | 185 ++++++++++++++ .../expected_output/hit_case.out.EXPECTED | 28 +- .../single_access.out.EXPECTED | 20 +- 12 files changed, 1101 insertions(+), 29 deletions(-) create mode 100644 core/ICache.cpp create mode 100644 core/ICache.hpp create mode 100644 test/core/icache/CMakeLists.txt create mode 100644 test/core/icache/ICacheChecker.hpp create mode 100644 test/core/icache/ICacheSink.hpp create mode 100644 test/core/icache/ICacheSource.hpp create mode 100644 test/core/icache/ICache_test.cpp diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 40eebfa1..fdf462b1 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -1,6 +1,7 @@ project (core) add_library(core Core.cpp + ICache.cpp Fetch.cpp Decode.cpp Rename.cpp diff --git a/core/ICache.cpp b/core/ICache.cpp new file mode 100644 index 00000000..05ef2349 --- /dev/null +++ b/core/ICache.cpp @@ -0,0 +1,241 @@ +// -*- C++ -*- + +//! +//! \file ICache.cpp +//! \brief Implementation of the CoreModel ICache unit +//! + + +#include "ICache.hpp" + +#include "OlympiaAllocators.hpp" + + +namespace olympia { + const char ICache::name[] = "instruction_cache"; + + ICache::ICache(sparta::TreeNode *node, const ICacheParameterSet *p) : + sparta::Unit(node), + l1_always_hit_(p->l1_always_hit), + cache_latency_(p->cache_latency), + pending_miss_buffer_("pending_miss_buffer", fetch_queue_size_, getClock()), + memory_access_allocator_( + sparta::notNull(olympia::OlympiaAllocators::getOlympiaAllocators(node))-> + memory_access_allocator) + { + + in_fetch_req_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(ICache, getRequestFromFetch_, MemoryAccessInfoPtr)); + + in_l2cache_ack_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(ICache, getAckFromL2Cache_, uint32_t)); + + in_l2cache_resp_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(ICache, getRespFromL2Cache_, MemoryAccessInfoPtr)); + + // IL1 cache config + const uint32_t l1_line_size = p->l1_line_size; + const uint32_t l1_size_kb = p->l1_size_kb; + const uint32_t l1_associativity = p->l1_associativity; + std::unique_ptr repl(new sparta::cache::TreePLRUReplacement + (l1_associativity)); + l1_cache_.reset(new CacheFuncModel(getContainer(), l1_size_kb, l1_line_size, *repl)); + sparta::StartupEvent(node, CREATE_SPARTA_HANDLER(ICache, sendInitialCredits_)); + + + } + + void ICache::sendInitialCredits_() + { + out_fetch_credit_.send(fetch_queue_size_); + } + + // Access ICache + bool ICache::lookupCache_(const MemoryAccessInfoPtr & mem_access_info_ptr) + { + uint64_t phyAddr = mem_access_info_ptr->getPhyAddr(); + + bool cache_hit = false; + + if (l1_always_hit_) { + cache_hit = true; + } + else { + auto cache_line = l1_cache_->peekLine(phyAddr); + cache_hit = (cache_line != nullptr) && cache_line->isValid(); + + // Update MRU replacement state if ICache HIT + if (cache_hit) { + l1_cache_->touchMRU(*cache_line); + } + } + + if (l1_always_hit_) { + ILOG("IL1 Cache HIT all the time: phyAddr=0x" << std::hex << phyAddr); + il1_cache_hits_++; + } + else if (cache_hit) { + ILOG("IL1 Cache HIT: phyAddr=0x" << std::hex << phyAddr); + il1_cache_hits_++; + } + else { + ILOG("IL1 Cache MISS: phyAddr=0x" << std::hex << phyAddr); + il1_cache_misses_++; + } + + return cache_hit; + } + + void ICache::reloadCache_(const MemoryAccessInfoPtr & mem_access_info_ptr) + { + + auto const decoder = l1_cache_->getAddrDecoder(); + auto const reload_addr = mem_access_info_ptr->getPhyAddr(); + auto const reload_block = decoder->calcBlockAddr(reload_addr); + + auto l1_cache_line = &l1_cache_->getLineForReplacementWithInvalidCheck(reload_addr); + l1_cache_->allocateWithMRUUpdate(*l1_cache_line, reload_addr); + + // Move pending misses into the replay queue + DLOG("finding misses to replay"); + auto iter = pending_miss_buffer_.begin(); + while (iter != pending_miss_buffer_.end()) { + auto delete_iter = iter++; + + if (decoder->calcBlockAddr((*delete_iter)->getPhyAddr()) == reload_block) { + DLOG("scheduling for replay " << *delete_iter); + replay_buffer_.emplace_back(*delete_iter); + pending_miss_buffer_.erase(delete_iter); + } + } + + // Schedule next cycle + DLOG("reload completed"); + ev_arbitrate_.schedule(1); + } + + void ICache::doArbitration_() + { + if (!l2cache_resp_queue_.empty()) { + // Do a linefill + auto const mem_access_info_ptr = l2cache_resp_queue_.front(); + ILOG("doing reload " << mem_access_info_ptr); + reloadCache_(mem_access_info_ptr); + l2cache_resp_queue_.pop_front(); + } + + // Priotize replays over fetches, replays can run in parallel with a fill. + // NOTE: Ideally we'd want to prioritize demand fetches over lingering misses + // from a speculative search + if (!replay_buffer_.empty()) { + // Replay miss + auto const mem_access_info_ptr = replay_buffer_.front(); + ILOG("doing replay for fetch request " << mem_access_info_ptr); + ev_replay_ready_.preparePayload(mem_access_info_ptr) + ->schedule(sparta::Clock::Cycle(cache_latency_)); + replay_buffer_.pop_front(); + + } + else if (!fetch_req_queue_.empty()) { + // Do a read access + auto const mem_access_info_ptr = fetch_req_queue_.front(); + ILOG("doing lookup for fetch request " << mem_access_info_ptr); + if (lookupCache_(mem_access_info_ptr)) { + mem_access_info_ptr->setCacheState(MemoryAccessInfo::CacheState::HIT); + } + else { + mem_access_info_ptr->setCacheState(MemoryAccessInfo::CacheState::MISS); + addToMissQueue_(mem_access_info_ptr); + } + ev_respond_.preparePayload(mem_access_info_ptr) + ->schedule(sparta::Clock::Cycle(cache_latency_)); + fetch_req_queue_.pop_front(); + } + + if (!l2cache_resp_queue_.empty() || !replay_buffer_.empty() || !fetch_req_queue_.empty()) { + ev_arbitrate_.schedule(1); + } + } + + void ICache::addToMissQueue_(const MemoryAccessInfoPtr & mem_access_info_ptr) + { + // Don't make requests to cachelines that are already pending + auto const decoder = l1_cache_->getAddrDecoder(); + auto missed_block = decoder->calcBlockAddr(mem_access_info_ptr->getPhyAddr()); + auto same_line = [decoder, missed_block] (auto other) { + return decoder->calcBlockAddr(other->getPhyAddr()) == missed_block; + }; + auto it = std::find_if(pending_miss_buffer_.begin(), pending_miss_buffer_.end(), same_line); + if (it == pending_miss_buffer_.end()) { + DLOG("appending miss to l2 miss queue: " << mem_access_info_ptr); + miss_queue_.emplace_back(mem_access_info_ptr); + makeL2CacheRequest_(); + } + ILOG("miss request queued for replay: " << mem_access_info_ptr); + pending_miss_buffer_.push_back(mem_access_info_ptr); + } + + void ICache::getRequestFromFetch_(const MemoryAccessInfoPtr &mem_access_info_ptr) + { + ILOG("received fetch request " << mem_access_info_ptr); + fetch_req_queue_.emplace_back(mem_access_info_ptr); + ev_arbitrate_.schedule(sparta::Clock::Cycle(0)); + } + + void ICache::getRespFromL2Cache_(const MemoryAccessInfoPtr &mem_access_info_ptr) + { + ILOG("received fill response " << mem_access_info_ptr); + if (mem_access_info_ptr->getCacheState() == MemoryAccessInfo::CacheState::HIT) { + l2cache_resp_queue_.emplace_back(mem_access_info_ptr); + ev_arbitrate_.schedule(sparta::Clock::Cycle(0)); + } + } + + void ICache::getAckFromL2Cache_(const uint32_t &ack) + { + l2cache_credits_ += ack; + if (!miss_queue_.empty()) { + ev_l2cache_request_.schedule(sparta::Clock::Cycle(0)); + } + } + + // Respond misses + void ICache::sendReplay_(const MemoryAccessInfoPtr & mem_access_info_ptr) + { + // Delayed change to hit state until we're ready to send it back + mem_access_info_ptr->setCacheState(MemoryAccessInfo::CacheState::HIT); + out_fetch_resp_.send(mem_access_info_ptr); + out_fetch_credit_.send(1); + } + + void ICache::sendResponse_(const MemoryAccessInfoPtr & mem_access_info_ptr) + { + out_fetch_resp_.send(mem_access_info_ptr); + if (mem_access_info_ptr->getCacheState() == MemoryAccessInfo::CacheState::HIT) { + out_fetch_credit_.send(1); + } + } + + void ICache::makeL2CacheRequest_() + { + if (l2cache_credits_ == 0 || miss_queue_.empty()) { + return; + } + + // Create new MemoryAccessInfo to avoid propagating changes made by L2 back to the core + const auto &l2cache_req = sparta::allocate_sparta_shared_pointer( + memory_access_allocator_, *(miss_queue_.front())); + + // Forward miss to next cache level + ILOG("requesting linefill for " << l2cache_req); + out_l2cache_req_.send(l2cache_req); + --l2cache_credits_; + + miss_queue_.pop_front(); + + // Schedule another + if (l2cache_credits_ > 0 && !miss_queue_.empty()) { + ev_l2cache_request_.schedule(1); + } + } +} diff --git a/core/ICache.hpp b/core/ICache.hpp new file mode 100644 index 00000000..869edd86 --- /dev/null +++ b/core/ICache.hpp @@ -0,0 +1,144 @@ +// -*- C++ -*- + +//! +//! \file Fetch.hpp +//! \brief Definition of the CoreModel ICache unit +//! + +#pragma once + +#include "sparta/simulation/Unit.hpp" +#include "sparta/ports/DataPort.hpp" +#include "sparta/ports/SignalPort.hpp" +#include "sparta/simulation/ParameterSet.hpp" +#include "sparta/resources/Buffer.hpp" +#include "sparta/utils/LogUtils.hpp" + +#include "CacheFuncModel.hpp" +#include "Inst.hpp" +#include "cache/TreePLRUReplacement.hpp" +#include "MemoryAccessInfo.hpp" + +namespace olympia +{ + + /** + * @file ICache.hpp + * @brief The L1 Instruction Cache block -- L1 Cache unit for instruction code + * + * This is an L1 Instruction Cache that features: + * - Hit and miss under miss + * - Pipelining of requests + * - Automatic miss replay following a linefill + * Both interfaces use a credit protocol. + */ + class ICache : public sparta::Unit + { + public: + class ICacheParameterSet : public sparta::ParameterSet + { + public: + ICacheParameterSet(sparta::TreeNode* n) : sparta::ParameterSet(n) {} + + // Parameters for the IL1 cache + PARAMETER(uint32_t, l1_line_size, 64, "IL1 line size (power of 2)") + PARAMETER(uint32_t, l1_size_kb, 32, "Size of IL1 in KB (power of 2)") + PARAMETER(uint32_t, l1_associativity, 8, "IL1 associativity (power of 2)") + PARAMETER(uint32_t, cache_latency, 1, "Assumed latency of the memory system") + PARAMETER(bool, l1_always_hit, false, "IL1 will always hit") + }; + + static const char name[]; + ICache(sparta::TreeNode* n, const ICacheParameterSet* p); + + private: + + void doArbitration_(); + void sendReplay_(const MemoryAccessInfoPtr &); + void sendResponse_(const MemoryAccessInfoPtr &); + void addToMissQueue_(const MemoryAccessInfoPtr &); + void makeL2CacheRequest_(); + void reloadCache_(const MemoryAccessInfoPtr&); + bool lookupCache_(const MemoryAccessInfoPtr &); + + void sendInitialCredits_(); + + // Callbacks + void getRequestFromFetch_(const MemoryAccessInfoPtr &); + void getAckFromL2Cache_(const uint32_t &); + void getRespFromL2Cache_(const MemoryAccessInfoPtr &); + + using L1Handle = CacheFuncModel::Handle; + L1Handle l1_cache_; + const bool l1_always_hit_; + const uint32_t cache_latency_ = 0; + const uint32_t fetch_queue_size_ = 8; + + std::deque l2cache_resp_queue_; + std::deque fetch_req_queue_; + std::deque replay_buffer_; + std::deque miss_queue_; + + sparta::Buffer pending_miss_buffer_; + + // Credits for sending miss request to L2Cache + uint32_t l2cache_credits_ = 0; + + olympia::MemoryAccessInfoAllocator & memory_access_allocator_; + + //////////////////////////////////////////////////////////////////////////////// + // Input Ports + //////////////////////////////////////////////////////////////////////////////// + sparta::DataInPort in_fetch_req_{&unit_port_set_, + "in_fetch_req", 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 + //////////////////////////////////////////////////////////////////////////////// + + sparta::DataOutPort out_fetch_credit_{&unit_port_set_, + "out_fetch_credit", 0}; + + sparta::DataOutPort out_fetch_resp_{&unit_port_set_, + "out_fetch_resp", 0}; + + sparta::DataOutPort out_l2cache_req_{&unit_port_set_, + "out_l2cache_req", 0}; + + //////////////////////////////////////////////////////////////////////////////// + // Events + //////////////////////////////////////////////////////////////////////////////// + sparta::UniqueEvent<> ev_arbitrate_{&unit_event_set_, "ev_arbitrate", + CREATE_SPARTA_HANDLER(ICache, doArbitration_)}; + + sparta::UniqueEvent<> ev_l2cache_request_{&unit_event_set_, "ev_l2cache_request", + CREATE_SPARTA_HANDLER(ICache, makeL2CacheRequest_)}; + + + sparta::PayloadEvent ev_respond_{ + &unit_event_set_, "ev_respond", + CREATE_SPARTA_HANDLER_WITH_DATA(ICache, sendResponse_, MemoryAccessInfoPtr)}; + + sparta::PayloadEvent ev_replay_ready_{ + &unit_event_set_, "ev_replay_ready", + CREATE_SPARTA_HANDLER_WITH_DATA(ICache, sendReplay_, MemoryAccessInfoPtr)}; + + //////////////////////////////////////////////////////////////////////////////// + // Counters + //////////////////////////////////////////////////////////////////////////////// + sparta::Counter il1_cache_hits_{getStatisticSet(), "IL1_cache_hits", + "Number of IL1 cache hits", sparta::Counter::COUNT_NORMAL}; + + sparta::Counter il1_cache_misses_{getStatisticSet(), "IL1_cache_misses", + "Number of IL1 cache misses", + sparta::Counter::COUNT_NORMAL}; + + friend class ICacheTester; + }; + class ICacheTester; +} // namespace olympia diff --git a/core/MemoryAccessInfo.hpp b/core/MemoryAccessInfo.hpp index 50a04b31..01f7e262 100644 --- a/core/MemoryAccessInfo.hpp +++ b/core/MemoryAccessInfo.hpp @@ -67,6 +67,19 @@ namespace olympia MemoryAccessInfo(const MemoryAccessInfo &rhs) = default; + MemoryAccessInfo(const uint64_t addr) : + ldst_inst_ptr_(nullptr), + phy_addr_ready_(true), + mmu_access_state_(MMUState::NO_ACCESS), + cache_access_state_(CacheState::NO_ACCESS), + cache_data_ready_(false), + src_(ArchUnit::NO_ACCESS), + dest_(ArchUnit::NO_ACCESS), + vaddr_(addr), + paddr_(addr) + {} + + MemoryAccessInfo(const InstPtr & inst_ptr) : ldst_inst_ptr_(inst_ptr), phy_addr_ready_(false), @@ -76,7 +89,9 @@ namespace olympia cache_access_state_(CacheState::NO_ACCESS), cache_data_ready_(false), src_(ArchUnit::NO_ACCESS), - dest_(ArchUnit::NO_ACCESS) + dest_(ArchUnit::NO_ACCESS), + vaddr_(inst_ptr->getTargetVAddr()), + paddr_(inst_ptr->getRAdr()) { } @@ -104,9 +119,9 @@ namespace olympia bool getPhyAddrStatus() const { return phy_addr_ready_; } - uint64_t getPhyAddr() const { return ldst_inst_ptr_->getRAdr(); } + uint64_t getPhyAddr() const { return paddr_; } - sparta::memory::addr_t getVAddr() const { return ldst_inst_ptr_->getTargetVAddr(); } + sparta::memory::addr_t getVAddr() const { return vaddr_; } void setSrcUnit(const ArchUnit & src_unit) { src_ = src_unit; } @@ -153,7 +168,7 @@ namespace olympia private: // load/store instruction pointer - InstPtr ldst_inst_ptr_; + const InstPtr ldst_inst_ptr_; // Indicate MMU address translation status bool phy_addr_ready_; @@ -169,6 +184,12 @@ namespace olympia ArchUnit src_ = ArchUnit::NO_ACCESS; ArchUnit dest_ = ArchUnit::NO_ACCESS; + // Virtual Address + const uint64_t vaddr_; + + // Physical Address + const uint64_t paddr_; + // Pointer to next request for DEBUG/TRACK // (Note : Currently used only to track request with same cacheline in L2Cache // Not for functional/performance purpose) @@ -256,7 +277,10 @@ namespace olympia inline std::ostream & operator<<(std::ostream & os, const olympia::MemoryAccessInfo & mem) { - os << "memptr: " << mem.getInstPtr(); + os << "memptr: " << std::hex << mem.getPhyAddr() << std::dec; + if (mem.getInstPtr() != nullptr) { + os << " " << mem.getInstPtr(); + } return os; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 35bb2a8d..4eb15341 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -32,3 +32,4 @@ add_subdirectory(core/l2cache) add_subdirectory(core/rename) add_subdirectory(core/lsu) add_subdirectory(core/issue_queue) +add_subdirectory(core/icache) diff --git a/test/core/icache/CMakeLists.txt b/test/core/icache/CMakeLists.txt new file mode 100644 index 00000000..0e3acda2 --- /dev/null +++ b/test/core/icache/CMakeLists.txt @@ -0,0 +1,8 @@ +project(ICache_test) + +add_executable(ICache_test ICache_test.cpp) +target_link_libraries(ICache_test core common_test mss mavis SPARTA::sparta) + +sparta_named_test(ICache_test_single_access ICache_test --testname single_access --seed 1) +sparta_named_test(ICache_test_simple ICache_test --testname simple --seed 1) +sparta_named_test(ICache_test_random ICache_test --testname random --seed 1 -p top.sink.params.miss_rate 100) diff --git a/test/core/icache/ICacheChecker.hpp b/test/core/icache/ICacheChecker.hpp new file mode 100644 index 00000000..4019bbb8 --- /dev/null +++ b/test/core/icache/ICacheChecker.hpp @@ -0,0 +1,192 @@ +#pragma once + + +#include "core/MemoryAccessInfo.hpp" +#include "sparta/simulation/TreeNode.hpp" +#include "sparta/events/SingleCycleUniqueEvent.hpp" +#include "sparta/utils/SpartaSharedPointer.hpp" +#include "sparta/utils/LogUtils.hpp" +#include "ICache.hpp" + +#include +#include +#include + +// Basically just a way of getting to the address decoder so that the checker can +// calculate set index or block addresses +class olympia::ICacheTester +{ +public: + void setDUT(olympia::ICache *dut) { dut_ = dut; } + + uint64_t getSetIdx(uint64_t addr) + { + auto decoder = dut_->l1_cache_->getAddrDecoder(); + return decoder->calcIdx(addr); + } + + uint64_t getTag(uint64_t addr) + { + auto decoder = dut_->l1_cache_->getAddrDecoder(); + return decoder->calcTag(addr); + } + + uint64_t getBlockAddress(uint64_t addr) + { + auto decoder = dut_->l1_cache_->getAddrDecoder(); + return decoder->calcBlockAddr(addr); + } + + uint32_t getNumWays() + { + return dut_->l1_cache_->getNumWays(); + } + +private: + olympia::ICache * dut_ = nullptr; +}; + +namespace icache_test +{ + class ICacheChecker : public sparta::Unit, public olympia::ICacheTester + { + public: + static constexpr char name[] = "instruction cache checker"; + + using ICacheCheckerParameters = sparta::ParameterSet; + ICacheChecker(sparta::TreeNode * node, const ICacheCheckerParameters *p) : sparta::Unit(node) + { + + in_fetch_req_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(ICacheChecker, getRequestFromFetch_, olympia::MemoryAccessInfoPtr)); + in_fetch_resp_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(ICacheChecker, getResponseToFetch_, olympia::MemoryAccessInfoPtr)); + in_l2cache_req_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(ICacheChecker, getRequestToL2Cache_, olympia::MemoryAccessInfoPtr)); + in_l2cache_resp_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(ICacheChecker, getResponseFromL2Cache_, olympia::MemoryAccessInfoPtr)); + } + + uint32_t getICacheHitCount() const { return icache_hits_;} + uint32_t getICacheMissCount() const { return icache_misses_;} + uint32_t getL2CacheHitCount() const { return l2cache_hits_;} + uint32_t getL2CacheMissCount() const { return l2cache_misses_;} + + private: + + void getRequestFromFetch_(const olympia::MemoryAccessInfoPtr & mem_access_info_ptr) + { + fetch_pending_queue_.push_back(mem_access_info_ptr); + } + + void getResponseToFetch_(const olympia::MemoryAccessInfoPtr & mem_access_info_ptr) + { + // Should only be a HIT or MISS response + const auto cache_state = mem_access_info_ptr->getCacheState(); + if (cache_state != olympia::MemoryAccessInfo::CacheState::HIT) { + sparta_assert(cache_state == olympia::MemoryAccessInfo::CacheState::MISS); + } + + // Search for the original request + const auto fetch_req = std::find(fetch_pending_queue_.begin(), fetch_pending_queue_.end(), mem_access_info_ptr); + sparta_assert(fetch_req != fetch_pending_queue_.end(), "response received without a corresponding request"); + + auto tag = getTag(mem_access_info_ptr->getPhyAddr()); + auto set = getSetIdx(mem_access_info_ptr->getPhyAddr()); + + if (cache_state == olympia::MemoryAccessInfo::CacheState::HIT) { + auto block = getBlockAddress(mem_access_info_ptr->getPhyAddr()); + + // Check that we don't have an outstanding L2 request on this block + sparta_assert(pending_l2cache_reqs_.count(block) == 0); + + // Check that we've filled this block at least once.. + sparta_assert(filled_blocks_.count(block)); + + // Track the last tag to hit on this set + last_access_tracker_[set] = tag; + + ILOG("removing fetch request") + fetch_pending_queue_.erase(fetch_req); + + ++icache_hits_; + } + else { + // We cannot miss if we hit on this set last time with this tag + if (auto itr = last_access_tracker_.find(set); itr != last_access_tracker_.end()) { + sparta_assert(itr->second != tag); + } + + ++icache_misses_; + } + } + + void getRequestToL2Cache_(const olympia::MemoryAccessInfoPtr & mem_access_info_ptr) + { + + auto block = getBlockAddress(mem_access_info_ptr->getPhyAddr()); + auto matches_block = [this, block](auto req) { return block == getBlockAddress(req->getPhyAddr()); }; + + // Check that fetch has tried to request this address + const auto fetch_req = std::find_if(fetch_pending_queue_.begin(), fetch_pending_queue_.end(), matches_block); + sparta_assert(fetch_req != fetch_pending_queue_.end(), "response received without a corresponding request"); + + // Check that we don't have another l2cache request inflight for the same block + sparta_assert(pending_l2cache_reqs_.count(block) == 0); + pending_l2cache_reqs_.insert(block); + } + + void getResponseFromL2Cache_(const olympia::MemoryAccessInfoPtr & mem_access_info_ptr) + { + if (mem_access_info_ptr->getCacheState() == olympia::MemoryAccessInfo::CacheState::HIT) { + auto block = getBlockAddress(mem_access_info_ptr->getPhyAddr()); + + // Flag that we've filled this block atleast once + filled_blocks_.insert(block); + + // Shouldn't have received a response if we didn't request it? + sparta_assert(pending_l2cache_reqs_.erase(block)); + + ++l2cache_hits_; + } + else { + ++l2cache_misses_; + } + } + + void onStartingTeardown_() override final + { + sparta_assert(fetch_pending_queue_.empty()); + sparta_assert(pending_l2cache_reqs_.size() == 0); + } + + //////////////////////////////////////////////////////////////////////////////// + // Variables + //////////////////////////////////////////////////////////////////////////////// + + uint32_t icache_hits_ = 0; + uint32_t icache_misses_ = 0; + uint32_t l2cache_hits_ = 0; + uint32_t l2cache_misses_ = 0; + + // Track pending fetch requests + std::vector fetch_pending_queue_; + + // Track pending l2cache reqs - icache shouldn't request them again, and fetch cannot hit on them + std::set pending_l2cache_reqs_; + + // Track blocks which have been filled atleast once - fetch cannot hit on them until they're filled + std::set filled_blocks_; + + // Track the tags of the last hits on each set - fetch cannot miss on them + std::map last_access_tracker_; + + //////////////////////////////////////////////////////////////////////////////// + // Input Ports + //////////////////////////////////////////////////////////////////////////////// + sparta::DataInPort in_fetch_req_ {&unit_port_set_, "in_fetch_req", 1}; + sparta::DataInPort in_fetch_resp_ {&unit_port_set_, "in_fetch_resp", 1}; + sparta::DataInPort in_l2cache_req_ {&unit_port_set_, "in_l2cache_req", 1}; + sparta::DataInPort in_l2cache_resp_ {&unit_port_set_, "in_l2cache_resp", 1}; + }; +} \ No newline at end of file diff --git a/test/core/icache/ICacheSink.hpp b/test/core/icache/ICacheSink.hpp new file mode 100644 index 00000000..a7264d4c --- /dev/null +++ b/test/core/icache/ICacheSink.hpp @@ -0,0 +1,155 @@ +// ICacheSink.hpp +#pragma once + +#include "core/MemoryAccessInfo.hpp" +#include "sparta/simulation/TreeNode.hpp" +#include "sparta/ports/DataPort.hpp" +#include "sparta/events/SingleCycleUniqueEvent.hpp" +#include "sparta/utils/SpartaSharedPointer.hpp" +#include "sparta/utils/LogUtils.hpp" + + +namespace icache_test +{ + class ICacheSink : public sparta::Unit + { + public: + static constexpr char name[] = "icache_sink_unit"; + + class ICacheSinkParameters : public sparta::ParameterSet + { + public: + ICacheSinkParameters(sparta::TreeNode *n) : sparta::ParameterSet(n) + {} + PARAMETER(double, miss_rate, 0, "miss rate per 1k requests") + PARAMETER(uint32_t, latency, 8, "hit latency") + PARAMETER(uint32_t, miss_penalty, 32, "miss latency") + + }; + + struct ICacheResponse { + uint64_t scheduled_time; + olympia::MemoryAccessInfo::CacheState hit_state; + olympia::MemoryAccessInfoPtr access; + }; + + + ICacheSink(sparta::TreeNode *n, const ICacheSinkParameters *params) : + sparta::Unit(n), + latency_(params->latency), + miss_penalty_(params->miss_penalty), + miss_distribution_({1000, params->miss_rate}), + gen_(1) + { + in_icache_req_.registerConsumerHandler( + CREATE_SPARTA_HANDLER_WITH_DATA(ICacheSink, getRequestFromICache_, olympia::MemoryAccessInfoPtr)); + + sparta::StartupEvent(n, CREATE_SPARTA_HANDLER(ICacheSink, sendInitialCredits_)); + ev_respond_.setContinuing(true); + + } + + void setRandomSeed(uint32_t seed) + { + gen_.seed(seed); + } + + + private: + + //////////////////////////////////////////////////////////////////////////////// + // Methods + //////////////////////////////////////////////////////////////////////////////// + + void sendInitialCredits_() + { + out_icache_credit_.send(8); + } + + void getRequestFromICache_(const olympia::MemoryAccessInfoPtr & mem_access_info_ptr) + { + // Hit's are removed from the pending queue + // Misses are retained. + // Randomly choose to miss, and also randomly generate a latency + ILOG("received request " << mem_access_info_ptr); + ICacheResponse resp; + resp.access = mem_access_info_ptr; + resp.scheduled_time = getClock()->currentCycle() + latency_; + if (miss_distribution_(gen_)) { + resp.hit_state = olympia::MemoryAccessInfo::CacheState::MISS; + } + else { + resp.hit_state = olympia::MemoryAccessInfo::CacheState::HIT; + } + response_queue_.emplace_back(resp); + // mem_access_info_ptr->setCacheState(olympia::MemoryAccessInfo::CacheState::HIT); + // ev_respond_.preparePayload(mem_access_info_ptr)->schedule(10); + ev_respond_.schedule(latency_); + out_icache_credit_.send(1); + } + + void sendResponse_() + { + // Find first response that's ready + auto response_ready = [this](ICacheResponse resp){ + return resp.scheduled_time <= getClock()->currentCycle(); + }; + auto resp_iter = std::find_if(response_queue_.begin(), response_queue_.end(), response_ready); + if (resp_iter == response_queue_.end()) { + ev_respond_.schedule(1); + return; + } + + ILOG("sending response " << resp_iter->access); + out_icache_resp_.send(resp_iter->access); + + // Replay the miss later + resp_iter->access->setCacheState(resp_iter->hit_state); + if (resp_iter->hit_state == olympia::MemoryAccessInfo::CacheState::MISS) { + resp_iter->hit_state = olympia::MemoryAccessInfo::CacheState::HIT; + resp_iter->scheduled_time = getClock()->currentCycle() + miss_penalty_; + ev_respond_.schedule(1); + } + else { + response_queue_.erase(resp_iter); + } + } + + void onStartingTeardown_() override final + { + sparta_assert(response_queue_.empty()); + } + + + //////////////////////////////////////////////////////////////////////////////// + // Variables + //////////////////////////////////////////////////////////////////////////////// + + const uint32_t latency_; + const uint32_t miss_penalty_; + + std::vector response_queue_; + + std::discrete_distribution<> miss_distribution_; + std::mt19937 gen_; + + //////////////////////////////////////////////////////////////////////////////// + // Ports + //////////////////////////////////////////////////////////////////////////////// + sparta::DataInPort in_icache_req_{&unit_port_set_, + "in_icache_req", 0}; + + sparta::DataOutPort out_icache_resp_{&unit_port_set_, + "out_icache_resp", 0}; + + sparta::DataOutPort out_icache_credit_{&unit_port_set_, + "out_icache_credit", 0}; + + //////////////////////////////////////////////////////////////////////////////// + // Events + //////////////////////////////////////////////////////////////////////////////// + sparta::UniqueEvent<> ev_respond_{ + &unit_event_set_, "ev_respond", + CREATE_SPARTA_HANDLER(ICacheSink, sendResponse_)}; + }; +} \ No newline at end of file diff --git a/test/core/icache/ICacheSource.hpp b/test/core/icache/ICacheSource.hpp new file mode 100644 index 00000000..9afc7dd0 --- /dev/null +++ b/test/core/icache/ICacheSource.hpp @@ -0,0 +1,121 @@ +// Source for ICache +#pragma once + +#include "core/MemoryAccessInfo.hpp" +#include "sparta/simulation/TreeNode.hpp" +#include "sparta/ports/DataPort.hpp" +#include "sparta/events/SingleCycleUniqueEvent.hpp" +#include "sparta/utils/SpartaSharedPointer.hpp" +#include "sparta/utils/LogUtils.hpp" + +#include "OlympiaAllocators.hpp" + +namespace icache_test +{ + class ICacheSource : public sparta::Unit + { + public: + static constexpr char name[] = "icache_source_unit"; + + class ICacheSourceParameters : public sparta::ParameterSet + { + public: + ICacheSourceParameters(sparta::TreeNode *n) : sparta::ParameterSet(n) + {} + }; + + ICacheSource(sparta::TreeNode *n, const ICacheSourceParameters *params) : + sparta::Unit(n), + memory_access_allocator_( + sparta::notNull(olympia::OlympiaAllocators::getOlympiaAllocators(n))-> + memory_access_allocator) + { + in_icache_resp_.registerConsumerHandler( + CREATE_SPARTA_HANDLER_WITH_DATA(ICacheSource, getResponseFromICache_, olympia::MemoryAccessInfoPtr)); + in_icache_credit_.registerConsumerHandler( + CREATE_SPARTA_HANDLER_WITH_DATA(ICacheSource, getCreditFromICache_, uint32_t)); + } + + // Queue up an ICache request for the address given + void queueRequest(const uint64_t & addr) + { + request_queue_.emplace_back(addr); + ev_send_requests_.schedule(sparta::Clock::Cycle(1)); + } + + + private: + + //////////////////////////////////////////////////////////////////////////////// + // Methods + //////////////////////////////////////////////////////////////////////////////// + + void sendRequests_() + { + if (!request_queue_.empty()) { + if (icache_credits_ > 0) { + auto addr = request_queue_.front(); + auto memory_access_info_ptr = + sparta::allocate_sparta_shared_pointer( + memory_access_allocator_, addr); + ILOG("sending " << memory_access_info_ptr); + out_icache_req_.send(memory_access_info_ptr); + icache_credits_--; + request_queue_.pop_front(); + ++outstanding_reqs_; + } + ev_send_requests_.schedule(1); + } + } + + void getResponseFromICache_(const olympia::MemoryAccessInfoPtr & mem_access_info_ptr) + { + // Hit's are removed from the pending queue + // Misses are retained. + ILOG("received response " << mem_access_info_ptr); + if (mem_access_info_ptr->getCacheState() == olympia::MemoryAccessInfo::CacheState::HIT) { + --outstanding_reqs_; + } + } + + void getCreditFromICache_(const uint32_t & credits) + { + icache_credits_ += credits; + } + + void onStartingTeardown_() override final + { + sparta_assert(outstanding_reqs_ == 0); + } + + + //////////////////////////////////////////////////////////////////////////////// + // Variables + //////////////////////////////////////////////////////////////////////////////// + + olympia::MemoryAccessInfoAllocator & memory_access_allocator_; + uint32_t icache_credits_ = 0; + std::deque request_queue_; + + uint32_t outstanding_reqs_ = 0; + + //////////////////////////////////////////////////////////////////////////////// + // Ports + //////////////////////////////////////////////////////////////////////////////// + sparta::DataOutPort out_icache_req_{&unit_port_set_, + "out_icache_req", 0}; + + sparta::DataInPort in_icache_resp_{&unit_port_set_, + "in_icache_resp", 0}; + + sparta::DataInPort in_icache_credit_{&unit_port_set_, + "in_icache_credit", 0}; + + //////////////////////////////////////////////////////////////////////////////// + // Events + //////////////////////////////////////////////////////////////////////////////// + sparta::UniqueEvent<> ev_send_requests_{&unit_event_set_, "ev_send_requests", + CREATE_SPARTA_HANDLER(ICacheSource, sendRequests_)}; + + }; +} \ No newline at end of file diff --git a/test/core/icache/ICache_test.cpp b/test/core/icache/ICache_test.cpp new file mode 100644 index 00000000..bac017fa --- /dev/null +++ b/test/core/icache/ICache_test.cpp @@ -0,0 +1,185 @@ +// ICache test + +#include "sparta/sparta.hpp" +#include "sparta/app/Simulation.hpp" +#include "sparta/app/CommandLineSimulator.hpp" +#include "sparta/simulation/ClockManager.hpp" +#include "sparta/kernel/Scheduler.hpp" +#include "sparta/utils/SpartaTester.hpp" +#include "sparta/utils/SpartaSharedPointer.hpp" +#include "sparta/utils/SpartaTester.hpp" + +#include "test/core/icache/ICacheSink.hpp" +#include "test/core/icache/ICacheSource.hpp" +#include "test/core/icache/ICacheChecker.hpp" + +#include "ICache.hpp" +#include "OlympiaAllocators.hpp" + +#include + +class ICacheSim : public sparta::app::Simulation{ +public: + ICacheSim(sparta::Scheduler *sched) : + sparta::app::Simulation("Test_special_params", sched){} + + virtual ~ICacheSim(){ + getRoot()->enterTeardown(); + } +private: + void buildTree_() override + { + auto rtn = getRoot(); + + allocators_tn_.reset(new olympia::OlympiaAllocators(rtn)); + + // ICache + tns_to_delete_.emplace_back(new sparta::ResourceTreeNode(rtn, + "icache", + sparta::TreeNode::GROUP_NAME_NONE, + sparta::TreeNode::GROUP_IDX_NONE, + "Instruction Cache", + &icache_fact)); + + // Source + tns_to_delete_.emplace_back(new sparta::ResourceTreeNode(rtn, + "source", + sparta::TreeNode::GROUP_NAME_NONE, + sparta::TreeNode::GROUP_IDX_NONE, + "Source", + &source_fact)); + // Sink + tns_to_delete_.emplace_back(new sparta::ResourceTreeNode(rtn, + "sink", + sparta::TreeNode::GROUP_NAME_NONE, + sparta::TreeNode::GROUP_IDX_NONE, + "Sink", + &sink_fact)); + + tns_to_delete_.emplace_back(new sparta::ResourceTreeNode(rtn, + "checker", + sparta::TreeNode::GROUP_NAME_NONE, + sparta::TreeNode::GROUP_IDX_NONE, + "Checker", + &checker_fact)); + + }; + void configureTree_() override{}; + void bindTree_() override + { + auto * root_node = getRoot(); + // Bind up source + sparta::bind(root_node->getChildAs("icache.ports.in_fetch_req"), + root_node->getChildAs("source.ports.out_icache_req")); + sparta::bind(root_node->getChildAs("icache.ports.out_fetch_credit"), + root_node->getChildAs("source.ports.in_icache_credit")); + sparta::bind(root_node->getChildAs("icache.ports.out_fetch_resp"), + root_node->getChildAs("source.ports.in_icache_resp")); + + // Bind up sink + sparta::bind(root_node->getChildAs("icache.ports.out_l2cache_req"), + root_node->getChildAs("sink.ports.in_icache_req")); + sparta::bind(root_node->getChildAs("icache.ports.in_l2cache_resp"), + root_node->getChildAs("sink.ports.out_icache_resp")); + sparta::bind(root_node->getChildAs("icache.ports.in_l2cache_ack"), + root_node->getChildAs("sink.ports.out_icache_credit")); + + // Bind up checker + sparta::bind(root_node->getChildAs("source.ports.out_icache_req"), + root_node->getChildAs("checker.ports.in_fetch_req")); + sparta::bind(root_node->getChildAs("icache.ports.out_fetch_resp"), + root_node->getChildAs("checker.ports.in_fetch_resp")); + sparta::bind(root_node->getChildAs("icache.ports.out_l2cache_req"), + root_node->getChildAs("checker.ports.in_l2cache_req")); + sparta::bind(root_node->getChildAs("sink.ports.out_icache_resp"), + root_node->getChildAs("checker.ports.in_l2cache_resp")); + }; + + std::unique_ptr allocators_tn_; + + sparta::ResourceFactory icache_fact; + sparta::ResourceFactory source_fact; + sparta::ResourceFactory sink_fact; + sparta::ResourceFactory checker_fact; + + std::vector> tns_to_delete_; + +}; + +const char USAGE[] = + "Usage:\n" + " \n" + "\n"; + +sparta::app::DefaultValues DEFAULTS; + +int main(int argc, char **argv) +{ + + std::string testname; + uint32_t seed = 1; + + sparta::app::CommandLineSimulator cls(USAGE, DEFAULTS); + auto & app_opts = cls.getApplicationOptions(); + app_opts.add_options() + ("testname", + sparta::app::named_value("TESTNAME", &testname)->default_value(""), + "Provide a testname to run", + "Test to run") + ("seed", + sparta::app::named_value("SEED", &seed)->default_value(1), + "Provide a value to seed the random generators", + "random seed"); + + 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::Scheduler sched; + ICacheSim sim(&sched); + cls.populateSimulation(&sim); + sparta::RootTreeNode* root = sim.getRoot(); + + icache_test::ICacheChecker *checker = root->getChild("checker")->getResourceAs(); + checker->setDUT(root->getChild("icache")->getResourceAs()); + + icache_test::ICacheSource *source = root->getChild("source")->getResourceAs(); + + icache_test::ICacheSink *sink = root->getChild("sink")->getResourceAs(); + sink->setRandomSeed(seed); + + if (testname == "single_access") { + source->queueRequest(1); + cls.runSimulator(&sim, 100); + } + else if (testname == "simple") { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 8; j++) { + source->queueRequest(8 << j); + } + } + cls.runSimulator(&sim, 1000); + } + else if (testname == "random") { + std::mt19937 gen(seed); + std::lognormal_distribution<> d(2.0, 1.0); + std::vector addrs = {1}; + for (int i = 0; i < 256; ++i) { + auto addr = addrs.back() + uint64_t(d(gen)); + addrs.push_back(addr); + } + for (int i = 0; i < 2048; ++i) { + auto idx = uint32_t(d(gen)) % addrs.size(); + source->queueRequest(addrs[idx]); + if (i % 128 == 0) { + std::shuffle(addrs.begin(), addrs.end(), gen); + } + } + cls.runSimulator(&sim, 100000); + } + else { + sparta_assert(false, "Must provide a valid testname"); + } + return 0; +} \ 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 index abf3ec67..c47a5eac 100644 --- a/test/core/l2cache/expected_output/hit_case.out.EXPECTED +++ b/test/core/l2cache/expected_output/hit_case.out.EXPECTED @@ -3,8 +3,8 @@ #Exe: #SimulatorVersion: #Repro: -#Start: Saturday Sat Jan 27 08:59:40 2024 -#Elapsed: 0.004092s +#Start: Thursday Thu Feb 8 10:29:35 2024 +#Elapsed: 0.024913s {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 @@ -28,16 +28,16 @@ {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 : memptr: uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000011 00000011 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef 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 : memptr: uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000012 00000012 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef 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 : memptr: uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000012 00000012 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef 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 : memptr: uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000013 00000013 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef 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 @@ -48,16 +48,16 @@ {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 : memptr: uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000044 00000044 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef 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 : memptr: uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000045 00000045 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef 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 : memptr: uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000045 00000045 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef 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 : memptr: uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000046 00000046 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef 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 @@ -77,15 +77,15 @@ {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 : memptr: uid: 1 FETCHED 0 pid: 2 'lw 5,3,4' +{0000000061 00000061 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef 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 : memptr: uid: 1 FETCHED 0 pid: 2 'lw 5,3,4' +{0000000062 00000062 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef 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 : memptr: uid: 1 FETCHED 0 pid: 2 'lw 5,3,4' +{0000000062 00000062 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef 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 : memptr: uid: 1 FETCHED 0 pid: 2 'lw 5,3,4' +{0000000063 00000063 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef 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 index bf6d8619..d7e055da 100644 --- a/test/core/l2cache/expected_output/single_access.out.EXPECTED +++ b/test/core/l2cache/expected_output/single_access.out.EXPECTED @@ -3,8 +3,8 @@ #Exe: #SimulatorVersion: #Repro: -#Start: Saturday Sat Jan 27 08:59:40 2024 -#Elapsed: 0.003294s +#Start: Thursday Thu Feb 8 10:29:35 2024 +#Elapsed: 0.031446s {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 @@ -28,16 +28,16 @@ {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 : memptr: uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000011 00000011 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef 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 : memptr: uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000012 00000012 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef 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 : memptr: uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000012 00000012 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef 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 : memptr: uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000013 00000013 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef 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 @@ -48,16 +48,16 @@ {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 : memptr: uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000044 00000044 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef 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 : memptr: uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000045 00000045 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef 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 : memptr: uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000045 00000045 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef 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 : memptr: uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000046 00000046 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef 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 From 431e74e72a35074e85c9bb6f0a37aee75093e9f1 Mon Sep 17 00:00:00 2001 From: Daniel Bone Date: Sun, 11 Feb 2024 23:46:28 +0000 Subject: [PATCH 2/7] Connect up ICache to Fetch --- core/CPUFactories.hpp | 10 +- core/CPUTopology.cpp | 32 +++++++ core/Fetch.cpp | 194 ++++++++++++++++++++++++++++++++------ core/Fetch.hpp | 45 ++++++++- core/Inst.cpp | 2 +- core/Inst.hpp | 17 +++- core/InstGenerator.cpp | 1 + core/MemoryAccessInfo.hpp | 8 ++ mss/BIU.cpp | 2 + mss/L2Cache.cpp | 5 +- mss/L2Cache.hpp | 2 +- 11 files changed, 280 insertions(+), 38 deletions(-) diff --git a/core/CPUFactories.hpp b/core/CPUFactories.hpp index bf0799d5..28f90dae 100644 --- a/core/CPUFactories.hpp +++ b/core/CPUFactories.hpp @@ -5,6 +5,7 @@ #include "sparta/simulation/ResourceFactory.hpp" #include "Core.hpp" +#include "ICache.hpp" #include "Fetch.hpp" #include "Decode.hpp" #include "Rename.hpp" @@ -39,6 +40,10 @@ namespace olympia{ sparta::ResourceFactory core_rf; + //! \brief Resource Factory to build an Instruction Cache Unit + sparta::ResourceFactory icache_rf; + //! \brief Resource Factory to build a Fetch Unit sparta::ResourceFactory fetch_rf; @@ -56,8 +61,7 @@ namespace olympia{ //! \brief Resource Factory to build a Execute Unit ExecuteFactory execute_rf; - - //! \brief Resource Factory to build a MMU Unit + //! \brief Resource Factory to build a Data Cache Unit sparta::ResourceFactory dcache_rf; @@ -100,7 +104,7 @@ namespace olympia{ // //! \brief Resource Factory to build a IssueQueue Unit // sparta::ResourceFactory issue_queue_rf; - + //! \brief Set up the Mavis Decode functional unit MavisFactory mavis_rf; }; // struct CPUFactories diff --git a/core/CPUTopology.cpp b/core/CPUTopology.cpp index b4260c67..9829a431 100644 --- a/core/CPUTopology.cpp +++ b/core/CPUTopology.cpp @@ -27,6 +27,14 @@ olympia::CoreTopologySimple::CoreTopologySimple(){ sparta::TreeNode::GROUP_IDX_NONE, &factories->flushmanager_rf }, + { + "icache", + "cpu.core*", + "Instruction Cache Unit", + sparta::TreeNode::GROUP_NAME_NONE, + sparta::TreeNode::GROUP_IDX_NONE, + &factories->icache_rf + }, { "fetch", "cpu.core*", @@ -152,6 +160,18 @@ olympia::CoreTopologySimple::CoreTopologySimple(){ //! Instantiating ports of this topology port_connections = { + { + "cpu.core*.fetch.ports.out_fetch_icache_req", + "cpu.core*.icache.ports.in_fetch_req" + }, + { + "cpu.core*.fetch.ports.in_icache_fetch_resp", + "cpu.core*.icache.ports.out_fetch_resp" + }, + { + "cpu.core*.fetch.ports.in_icache_fetch_credits", + "cpu.core*.icache.ports.out_fetch_credit" + }, { "cpu.core*.fetch.ports.out_fetch_queue_write", "cpu.core*.decode.ports.in_fetch_queue_write" @@ -220,6 +240,18 @@ olympia::CoreTopologySimple::CoreTopologySimple(){ "cpu.core*.dcache.ports.in_l2cache_resp", "cpu.core*.l2cache.ports.out_l2cache_dcache_resp" }, + { + "cpu.core*.icache.ports.out_l2cache_req", + "cpu.core*.l2cache.ports.in_icache_l2cache_req" + }, + { + "cpu.core*.icache.ports.in_l2cache_ack", + "cpu.core*.l2cache.ports.out_l2cache_icache_ack" + }, + { + "cpu.core*.icache.ports.in_l2cache_resp", + "cpu.core*.l2cache.ports.out_l2cache_icache_resp" + }, { "cpu.core*.l2cache.ports.out_l2cache_biu_req", "cpu.core*.biu.ports.in_biu_req" diff --git a/core/Fetch.cpp b/core/Fetch.cpp index 0629111f..b4124deb 100644 --- a/core/Fetch.cpp +++ b/core/Fetch.cpp @@ -9,6 +9,7 @@ #include "Fetch.hpp" #include "InstGenerator.hpp" #include "MavisUnit.hpp" +#include "OlympiaAllocators.hpp" #include "sparta/utils/LogUtils.hpp" #include "sparta/events/StartupEvent.hpp" @@ -22,7 +23,10 @@ namespace olympia sparta::Unit(node), num_insts_to_fetch_(p->num_to_fetch), skip_nonuser_mode_(p->skip_nonuser_mode), - my_clk_(getClock()) + my_clk_(getClock()), + ibuf_capacity_(32), // buffer up instructions read from trace + memory_access_allocator_(sparta::notNull(OlympiaAllocators::getOlympiaAllocators(node)) + ->memory_access_allocator) { in_fetch_queue_credits_. registerConsumerHandler(CREATE_SPARTA_HANDLER_WITH_DATA(Fetch, receiveFetchQueueCredits_, uint32_t)); @@ -30,8 +34,18 @@ namespace olympia in_fetch_flush_redirect_. registerConsumerHandler(CREATE_SPARTA_HANDLER_WITH_DATA(Fetch, flushFetch_, FlushManager::FlushingCriteria)); - fetch_inst_event_.reset(new sparta::SingleCycleUniqueEvent<>(&unit_event_set_, "fetch_random", - CREATE_SPARTA_HANDLER(Fetch, fetchInstruction_))); + in_icache_fetch_resp_. + registerConsumerHandler(CREATE_SPARTA_HANDLER_WITH_DATA(Fetch, receiveCacheResponse_, MemoryAccessInfoPtr)); + + in_icache_fetch_credits_. + registerConsumerHandler(CREATE_SPARTA_HANDLER_WITH_DATA(Fetch, receiveCacheCredit_, uint32_t)); + + ev_fetch_insts.reset(new sparta::SingleCycleUniqueEvent<>(&unit_event_set_, "fetch_instruction_data", + CREATE_SPARTA_HANDLER(Fetch, fetchInstruction_))); + + ev_drive_insts.reset(new sparta::SingleCycleUniqueEvent<>(&unit_event_set_, "drive_instructions_out", + CREATE_SPARTA_HANDLER(Fetch, driveInstructions_))); + // Schedule a single event to start reading from a trace file sparta::StartupEvent(node, CREATE_SPARTA_HANDLER(Fetch, initialize_)); @@ -49,52 +63,151 @@ namespace olympia workload->getValueAsString(), skip_nonuser_mode_); - fetch_inst_event_->schedule(1); + ev_fetch_insts->schedule(1); } + void Fetch::fetchInstruction_() { - const uint32_t upper = std::min(credits_inst_queue_, num_insts_to_fetch_); + // Prefill the ibuf with some instructions read from the tracefile + // keeping enough capacity to group them into cache block accesses. + for (uint32_t i = ibuf_.size(); i < ibuf_capacity_; ++i) + { + const auto & inst_ptr = inst_generator_->getNextInst(my_clk_); + if (SPARTA_EXPECT_TRUE(nullptr != inst_ptr)) { + ibuf_.emplace_back(inst_ptr); + } + else { + break; + } + } + + if (credits_icache_ == 0 || ibuf_.empty() || fetch_buffer_.size() > fetch_buffer_capacity_) { return; } + + // Gather instructions going to the same cacheblock + // TODO this should be a constant related to the ICache + const uint32_t icache_block_shift_ = 4; // 16B blocks.. + // TODO instructions which straddle should be placed into the next group + auto different_blocks = [icache_block_shift_](const auto &lhs, const auto &rhs) { + return (lhs->getPC() >> icache_block_shift_) != (rhs->getPC() >> icache_block_shift_) || + lhs->isTakenBranch() || + rhs->isCoF(); + }; + + auto block_end = std::adjacent_find(ibuf_.begin(), ibuf_.end(), different_blocks); + if (block_end != ibuf_.end()) { + ++block_end; + } + + // Send to ICache + auto memory_access_ptr = sparta::allocate_sparta_shared_pointer(memory_access_allocator_, + ibuf_.front()->getPC()); + + InstGroupPtr fetch_group_ptr = sparta::allocate_sparta_shared_pointer(instgroup_allocator); + + // Place in fetch group for the memory access, and place in fetch buffer for later processing. + for (auto iter = ibuf_.begin(); iter != block_end; iter++) { + fetch_group_ptr->emplace_back(*iter); + fetch_buffer_.emplace_back(*iter); + } + + // Set the last in block + fetch_buffer_.back()->setLastInFetchBlock(true); + + // Associate the icache transaction with the instructions + memory_access_ptr->setFetchGroup(fetch_group_ptr); + + ILOG("requesting: " << fetch_group_ptr); + + out_fetch_icache_req_.send(memory_access_ptr); + --credits_icache_; + + // We want to track blocks, not instructions. + ++fetch_buffer_occupancy_; + + ibuf_.erase(ibuf_.begin(), block_end); + if (!ibuf_.empty() && credits_icache_ > 0 && fetch_buffer_occupancy_ < fetch_buffer_capacity_) { + ev_fetch_insts->schedule(1); + } + } + + // Read instructions from the fetch buffer and send them to decode + void Fetch::driveInstructions_() + { + const uint32_t upper = std::min({credits_inst_queue_, num_insts_to_fetch_, + static_cast(fetch_buffer_.size())}); // Nothing to send. Don't need to schedule this again. - if(upper == 0) { return; } + if (upper == 0) { return ; } InstGroupPtr insts_to_send = sparta::allocate_sparta_shared_pointer(instgroup_allocator); - for(uint32_t i = 0; i < upper; ++i) + for (uint32_t i = 0; i < upper; ++i) { - InstPtr ex_inst = inst_generator_->getNextInst(my_clk_); - if(SPARTA_EXPECT_TRUE(nullptr != ex_inst)) - { - ex_inst->setSpeculative(speculative_path_); - insts_to_send->emplace_back(ex_inst); + const auto entry = fetch_buffer_.front(); - ILOG("Sending: " << ex_inst << " down the pipe"); - } - else { + // Can't send instructions that still waiting for ICache data + if (entry->getStatus() != Inst::Status::FETCHED) { break; } - } - if(false == insts_to_send->empty()) - { - out_fetch_queue_write_.send(insts_to_send); + // Don't group instructions where there has been a change of flow + if (entry->isCoF() && insts_to_send->size() > 0) { + break; + } - credits_inst_queue_ -= static_cast (insts_to_send->size()); + // Send instruction to decode + entry->setSpeculative(speculative_path_); + insts_to_send->emplace_back(entry); + ILOG("Sending: " << entry << " down the pipe") + fetch_buffer_.pop_front(); - if((credits_inst_queue_ > 0) && (false == inst_generator_->isDone())) { - fetch_inst_event_->schedule(1); + if (entry->isLastInFetchBlock()) { + --fetch_buffer_occupancy_; } - if(SPARTA_EXPECT_FALSE(info_logger_)) { - info_logger_ << "Fetch: send num_inst=" << insts_to_send->size() - << " instructions, remaining credit=" << credits_inst_queue_; + // Only one taken branch per group + if (entry->isTakenBranch()) { + break; } } - else if(SPARTA_EXPECT_FALSE(info_logger_)) { - info_logger_ << "Fetch: no instructions from trace"; + + credits_inst_queue_ -= static_cast(insts_to_send->size()); + out_fetch_queue_write_.send(insts_to_send); + + if (!fetch_buffer_.empty() && credits_inst_queue_ > 0) { + ev_drive_insts->schedule(1); + } + + ev_fetch_insts->schedule(1); + + } + + void Fetch::receiveCacheResponse_(const MemoryAccessInfoPtr &response) + { + if (response->getCacheState() == MemoryAccessInfo::CacheState::HIT) { + // Mark instructions as fetched + auto & fetched_insts = response->getFetchGroup(); + sparta_assert(fetched_insts != nullptr); + for(auto & inst : *fetched_insts) { + inst->setStatus(Inst::Status::FETCHED); + } + + ev_drive_insts->schedule(sparta::Clock::Cycle(0)); } } + // Called when ICache has room + void Fetch::receiveCacheCredit_(const uint32_t &dat) + { + credits_icache_ += dat; + + ILOG("Fetch: receive num_credits_icache=" << dat + << ", total credits_icache=" << credits_icache_); + + // Schedule a fetch event this cycle + ev_fetch_insts->schedule(sparta::Clock::Cycle(0)); + } + // Called when decode has room void Fetch::receiveFetchQueueCredits_(const uint32_t & dat) { credits_inst_queue_ += dat; @@ -103,7 +216,7 @@ namespace olympia << ", total decode_credits=" << credits_inst_queue_); // Schedule a fetch event this cycle - fetch_inst_event_->schedule(sparta::Clock::Cycle(0)); + ev_drive_insts->schedule(sparta::Clock::Cycle(0)); } // Called from FlushManager via in_fetch_flush_redirect_port @@ -126,8 +239,33 @@ namespace olympia // Cancel all previously sent instructions on the outport out_fetch_queue_write_.cancel(); + // Cancel any ICache request + out_fetch_icache_req_.cancel(); + + // Clear internal buffers + ibuf_.clear(); + fetch_buffer_.clear(); + // No longer speculative // speculative_path_ = false; } + void Fetch::dumpDebugContent_(std::ostream & output) const + { + output << "Fetch Contents" << std::endl; + for (const auto & entry : fetch_buffer_) + { + output << '\t' << entry << std::endl; + } + } + + void Fetch::onStartingTeardown_() + { + // if ((false == rob_stopped_simulation_) && (false == fetch_buffer_.empty())) + // { + // dumpDebugContent_(std::cerr); + // sparta_assert(false, "fetch buffer has pending instructions"); + // } + } + } diff --git a/core/Fetch.hpp b/core/Fetch.hpp index 1538bb17..36b4c236 100644 --- a/core/Fetch.hpp +++ b/core/Fetch.hpp @@ -19,6 +19,7 @@ #include "CoreTypes.hpp" #include "InstGroup.hpp" #include "FlushManager.hpp" +#include "MemoryAccessInfo.hpp" namespace olympia { @@ -88,6 +89,18 @@ namespace olympia sparta::DataInPort in_fetch_flush_redirect_ {&unit_port_set_, "in_fetch_flush_redirect", sparta::SchedulingPhase::Flush, 1}; + // Instruction Cache Request + sparta::DataOutPort out_fetch_icache_req_ + {&unit_port_set_, "out_fetch_icache_req"}; + + // Instruction Cache Response + sparta::DataInPort in_icache_fetch_resp_ + {&unit_port_set_, "in_icache_fetch_resp", sparta::SchedulingPhase::Tick, 1}; + + // Instruction Cache Credit + sparta::DataInPort in_icache_fetch_credits_ + {&unit_port_set_, "in_icache_fetch_credits", sparta::SchedulingPhase::Tick, 0}; + //////////////////////////////////////////////////////////////////////////////// // Instruction fetch // Number of instructions to fetch @@ -99,16 +112,34 @@ namespace olympia // Number of credits from decode that fetch has uint32_t credits_inst_queue_ = 0; + uint32_t credits_icache_ = 0; + // Unit's clock const sparta::Clock * my_clk_ = nullptr; + // Size of trace buffer (must be sized >= L1ICache bandwidth / 2B) + const uint32_t ibuf_capacity_; + + // allocator for ICache transactions + MemoryAccessInfoAllocator & memory_access_allocator_; + + uint32_t fetch_buffer_occupancy_ = 0; + const uint32_t fetch_buffer_capacity_ = 16; + // Instruction generation std::unique_ptr inst_generator_; // Fetch instruction event, triggered when there are credits // from decode. The callback set is either to fetch random // instructions or a perfect IPC set - std::unique_ptr> fetch_inst_event_; + std::unique_ptr> ev_fetch_insts; + std::unique_ptr> ev_drive_insts; + + // Buffers up instructions read from the tracefile + std::deque ibuf_; + + // Holds fetched instructions from the ICache + std::deque fetch_buffer_; //////////////////////////////////////////////////////////////////////////////// // Callbacks @@ -122,9 +153,21 @@ namespace olympia // Read data from a trace void fetchInstruction_(); + // Read instructions from the fetch buffer and send them to decode + void driveInstructions_(); + // Receive flush from FlushManager void flushFetch_(const FlushManager::FlushingCriteria &); + // Receieve the number of free credits from the instruction cache + void receiveCacheCredit_(const uint32_t &); + + // Receive read data from the instruction cache + void receiveCacheResponse_(const MemoryAccessInfoPtr &); + + void onStartingTeardown_() override; + void dumpDebugContent_(std::ostream&) const override final; + // Are we fetching a speculative path? bool speculative_path_ = false; }; diff --git a/core/Inst.cpp b/core/Inst.cpp index 9a067076..59182a65 100644 --- a/core/Inst.cpp +++ b/core/Inst.cpp @@ -54,7 +54,7 @@ namespace olympia is_condbranch_(opcode_info_->isInstType(mavis::OpcodeInfo::InstructionTypes::CONDITIONAL)), is_call_(isCallInstruction(opcode_info)), is_return_(isReturnInstruction(opcode_info)), - status_state_(Status::FETCHED) + status_state_(Status::BEFORE_FETCH) { sparta_assert(inst_arch_info_ != nullptr, "Mavis decoded the instruction, but Olympia has no uarch data for it: " diff --git a/core/Inst.hpp b/core/Inst.hpp index 66e66ef1..4465ab2b 100644 --- a/core/Inst.hpp +++ b/core/Inst.hpp @@ -83,8 +83,9 @@ namespace olympia enum class Status : std::uint16_t { - FETCHED = 0, - __FIRST = FETCHED, + BEFORE_FETCH = 0, + __FIRST = BEFORE_FETCH, + FETCHED, DECODED, RENAMED, DISPATCHED, @@ -181,6 +182,10 @@ namespace olympia // TBD -- add branch prediction void setSpeculative(bool spec) { is_speculative_ = spec; } + // Last instruction within the cache block fetched from the ICache + void setLastInFetchBlock(bool last) { last_in_fetch_block_ = last; } + bool isLastInFetchBlock() const { return last_in_fetch_block_; } + // Opcode information std::string getMnemonic() const { return opcode_info_->getMnemonic(); } @@ -221,6 +226,9 @@ namespace olympia bool isReturn() const { return is_return_; } + void setCoF(const bool &cof) { is_cof_ = cof; } + bool isCoF() const { return is_cof_; } + // Rename information core_types::RegisterBitMask & getSrcRegisterBitMask(const core_types::RegFile rf) { @@ -276,7 +284,9 @@ namespace olympia const bool is_condbranch_; const bool is_call_; const bool is_return_; + bool is_cof_ = false; // Is change of flow bool is_taken_branch_ = false; + bool last_in_fetch_block_ = false; // This is the last instruction in the fetch block sparta::Scheduleable* ev_retire_ = nullptr; Status status_state_; @@ -300,6 +310,9 @@ namespace olympia { switch (status) { + case Inst::Status::BEFORE_FETCH: + os << "BEFORE_FETCH"; + break; case Inst::Status::FETCHED: os << "FETCHED"; break; diff --git a/core/InstGenerator.cpp b/core/InstGenerator.cpp index c68c9e15..ed8dd068 100644 --- a/core/InstGenerator.cpp +++ b/core/InstGenerator.cpp @@ -203,6 +203,7 @@ namespace olympia //For misaligns, more than 1 address is provided //inst->setVAddrVector(std::move(addrs)); } + inst->setCoF(next_it_->isCoF()); if (next_it_->isBranch()) { inst->setTakenBranch(next_it_->isTakenBranch()); diff --git a/core/MemoryAccessInfo.hpp b/core/MemoryAccessInfo.hpp index 01f7e262..133f3afa 100644 --- a/core/MemoryAccessInfo.hpp +++ b/core/MemoryAccessInfo.hpp @@ -8,6 +8,7 @@ #include "sparta/utils/SpartaSharedPointer.hpp" #include "sparta/utils/SpartaSharedPointerAllocator.hpp" #include "Inst.hpp" +#include "InstGroup.hpp" namespace olympia { @@ -149,6 +150,9 @@ namespace olympia void setDataReady(bool is_ready) { cache_data_ready_ = is_ready; } + void setFetchGroup(const InstGroupPtr &group) { fetch_group_ = group; } + const InstGroupPtr & getFetchGroup() const { return fetch_group_; } + const LoadStoreInstIterator getIssueQueueIterator() const { return issue_queue_iterator_; } void setIssueQueueIterator(const LoadStoreInstIterator & iter) @@ -195,6 +199,10 @@ namespace olympia // Not for functional/performance purpose) MemoryAccessInfoPtr next_req_ = nullptr; + // Instructions that this memory access is fetching + // *USED* only for instruction fetch + InstGroupPtr fetch_group_; + LoadStoreInstIterator issue_queue_iterator_; LoadStoreInstIterator replay_queue_iterator_; }; diff --git a/mss/BIU.cpp b/mss/BIU.cpp index 82452e1f..d1e2fb7f 100644 --- a/mss/BIU.cpp +++ b/mss/BIU.cpp @@ -27,6 +27,8 @@ namespace olympia_mss sparta::StartupEvent(node, CREATE_SPARTA_HANDLER(BIU, sendInitialCredits_)); ILOG("BIU construct: #" << node->getGroupIdx()); + + ev_handle_mss_ack_ >> ev_handle_biu_req_ >> ev_handle_biu_l2cache_ack_; } diff --git a/mss/L2Cache.cpp b/mss/L2Cache.cpp index 55c877d6..e2fc24ae 100644 --- a/mss/L2Cache.cpp +++ b/mss/L2Cache.cpp @@ -247,8 +247,9 @@ namespace olympia_mss // 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); + // uint32_t available_slots = icache_req_queue_size_ - icache_req_queue_.size(); + // out_l2cache_icache_ack_.send(available_slots); + out_l2cache_icache_ack_.send(1); ++num_acks_to_icache_; ILOG("L2Cache->ICache : Ack is sent."); diff --git a/mss/L2Cache.hpp b/mss/L2Cache.hpp index a5fb1610..22da8f99 100644 --- a/mss/L2Cache.hpp +++ b/mss/L2Cache.hpp @@ -57,7 +57,7 @@ namespace olympia_mss 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_icache_connected, true, "Does this unit have ICache connected to it") PARAMETER(bool, is_dcache_connected, true, "Does this unit have DCache connected to it") }; From b09fd6c82b0c6d119ab660e637833a3734b75e67 Mon Sep 17 00:00:00 2001 From: Daniel Bone Date: Mon, 12 Feb 2024 10:50:36 +0000 Subject: [PATCH 3/7] Fix rename and issue tests accounting for icache latency --- .../issue_queue/test_cores/test_big_core_full.yaml | 7 +++++-- test/core/lsu/test_cores/test_small_core_full.yaml | 6 ++++-- test/core/rename/Rename_test.cpp | 10 ++++++---- test/core/rename/test_cores/test_medium_core_full.yaml | 6 ++++-- test/core/rename/test_cores/test_small_core_full.yaml | 6 ++++-- 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/test/core/issue_queue/test_cores/test_big_core_full.yaml b/test/core/issue_queue/test_cores/test_big_core_full.yaml index 66c1ec15..72e61334 100644 --- a/test/core/issue_queue/test_cores/test_big_core_full.yaml +++ b/test/core/issue_queue/test_cores/test_big_core_full.yaml @@ -1,3 +1,6 @@ +top.cpu.core0.icache.params.l1_always_hit: true +top.cpu.core0.icache.params.cache_latency: 0 + # # Set up the pipeline for a 8-wide machine # @@ -10,13 +13,13 @@ top.cpu.core0.extension.core_extensions: ["int", "mul", "i2f", "cmov"], ["int"], ["int"], - ["float", "faddsub", "fmac"], + ["float", "faddsub", "fmac"], ["float", "f2i"], ["br"], ["br"] ] issue_queue_to_pipe_map: - [ + [ ["0", "1"], ["2", "3"], ["4", "5"], diff --git a/test/core/lsu/test_cores/test_small_core_full.yaml b/test/core/lsu/test_cores/test_small_core_full.yaml index da6e5864..35899e52 100644 --- a/test/core/lsu/test_cores/test_small_core_full.yaml +++ b/test/core/lsu/test_cores/test_small_core_full.yaml @@ -4,6 +4,8 @@ top.cpu.core0: fetch.params.num_to_fetch: 2 + icache.params.l1_always_hit: true + icache.params.cache_latency: 0 decode.params.num_to_decode: 2 rename.params.num_to_rename: 2 dispatch.params.num_to_dispatch: 2 @@ -22,11 +24,11 @@ top.cpu.core0.extension.core_extensions: pipelines: [ ["int", "mul", "i2f", "cmov", "div"], - ["float", "faddsub", "fmac", "f2i"], + ["float", "faddsub", "fmac", "f2i"], ["br"] ] issue_queue_to_pipe_map: - [ + [ ["0"], ["1"], ["2"] diff --git a/test/core/rename/Rename_test.cpp b/test/core/rename/Rename_test.cpp index 12b289d1..ca3c7225 100644 --- a/test/core/rename/Rename_test.cpp +++ b/test/core/rename/Rename_test.cpp @@ -426,7 +426,7 @@ void runTest(int argc, char **argv) { root_node->getChild("cpu.core0.execute.iq1") ->getResourceAs(); olympia::IssueQueueTester issuequeue_tester; - cls.runSimulator(&sim, 7); + cls.runSimulator(&sim, 8); issuequeue_tester.test_dependent_integer_first_instruction(*my_issuequeue); issuequeue_tester.test_dependent_integer_second_instruction( *my_issuequeue1); @@ -437,7 +437,9 @@ void runTest(int argc, char **argv) { ->getResourceAs(); olympia::RenameTester rename_tester; - cls.runSimulator(&sim, 4); + // Must stop the simulation before it retires the i2f instructions, + // otherwise the register would have been moved back into the freelist + cls.runSimulator(&sim, 8); rename_tester.test_float(*my_rename); } else if (input_file == "raw_int_lsu.json") { // testing RAW dependency for address operand @@ -451,7 +453,7 @@ void runTest(int argc, char **argv) { root_node->getChild("cpu.core0.execute.iq0") ->getResourceAs(); olympia::IssueQueueTester issuequeue_tester; - cls.runSimulator(&sim, 7); + cls.runSimulator(&sim, 8); issuequeue_tester.test_dependent_integer_first_instruction(*my_issuequeue); lsu_tester.test_dependent_lsu_instruction(*my_lsu); lsu_tester.clear_entries(*my_lsu); @@ -468,7 +470,7 @@ void runTest(int argc, char **argv) { root_node->getChild("cpu.core0.execute.iq1") ->getResourceAs(); olympia::IssueQueueTester issuequeue_tester; - cls.runSimulator(&sim, 6); + cls.runSimulator(&sim, 8); issuequeue_tester.test_dependent_integer_first_instruction(*my_issuequeue); lsu_tester.test_dependent_lsu_instruction(*my_lsu); lsu_tester.clear_entries(*my_lsu); diff --git a/test/core/rename/test_cores/test_medium_core_full.yaml b/test/core/rename/test_cores/test_medium_core_full.yaml index 85f39c74..ba3c5c0f 100644 --- a/test/core/rename/test_cores/test_medium_core_full.yaml +++ b/test/core/rename/test_cores/test_medium_core_full.yaml @@ -6,6 +6,8 @@ top.cpu.core0: fetch.params.num_to_fetch: 3 + icache.params.l1_always_hit: true + icache.params.cache_latency: 0 decode.params.num_to_decode: 3 rename.params.num_to_rename: 3 dispatch.params.num_to_dispatch: 3 @@ -19,12 +21,12 @@ top.cpu.core0.extension.core_extensions: ["int", "mul", "i2f", "cmov"], ["int", "div"], ["int"], - ["float", "faddsub", "fmac"], + ["float", "faddsub", "fmac"], ["float", "f2i"], ["br"] ] issue_queue_to_pipe_map: - [ + [ ["0"], ["1", "2"], ["3", "4"], diff --git a/test/core/rename/test_cores/test_small_core_full.yaml b/test/core/rename/test_cores/test_small_core_full.yaml index 20feb903..4a947668 100644 --- a/test/core/rename/test_cores/test_small_core_full.yaml +++ b/test/core/rename/test_cores/test_small_core_full.yaml @@ -4,6 +4,8 @@ top.cpu.core0: fetch.params.num_to_fetch: 2 + icache.params.l1_always_hit: true + icache.params.cache_latency: 0 decode.params.num_to_decode: 2 rename.params.num_to_rename: 2 dispatch.params.num_to_dispatch: 2 @@ -19,11 +21,11 @@ top.cpu.core0.extension.core_extensions: pipelines: [ ["int", "mul", "i2f", "cmov", "div"], - ["float", "faddsub", "fmac", "f2i"], + ["float", "faddsub", "fmac", "f2i"], ["br"] ] issue_queue_to_pipe_map: - [ + [ ["0"], ["1"], ["2"] From 4dee5066b074233f9151041f851393a1184f80d0 Mon Sep 17 00:00:00 2001 From: Daniel Bone Date: Mon, 12 Feb 2024 12:21:20 +0000 Subject: [PATCH 4/7] Fix LSU test accounting for extra ICache latency --- test/core/lsu/Lsu_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/core/lsu/Lsu_test.cpp b/test/core/lsu/Lsu_test.cpp index def392ec..02f2f261 100644 --- a/test/core/lsu/Lsu_test.cpp +++ b/test/core/lsu/Lsu_test.cpp @@ -107,7 +107,7 @@ void runTest(int argc, char **argv) olympia::LSU *my_lsu = root_node->getChild("cpu.core0.lsu")->getResourceAs(); olympia::LSUTester lsupipe_tester; lsupipe_tester.test_pipeline_stages(*my_lsu); - cls.runSimulator(&sim, 7); + cls.runSimulator(&sim, 9); lsupipe_tester.test_inst_issue(*my_lsu, 2); // Loads operand dependency meet cls.runSimulator(&sim, 52); lsupipe_tester.test_replay_issue_abort(*my_lsu, 2); // Loads operand dependency meet From 9c77264606dc5b7cc2657c5562a880946b71b46a Mon Sep 17 00:00:00 2001 From: Daniel Bone Date: Mon, 12 Feb 2024 14:33:22 +0000 Subject: [PATCH 5/7] Fix L2Cache tests. Change L2+MSS to use credit instead of ack --- core/CPUTopology.cpp | 12 ++-- core/DCache.cpp | 8 +-- core/DCache.hpp | 4 +- core/ICache.cpp | 6 +- core/ICache.hpp | 4 +- mss/BIU.cpp | 29 +++----- mss/BIU.hpp | 20 ++---- mss/L2Cache.cpp | 45 +++++------- mss/L2Cache.hpp | 34 +++------ test/core/icache/ICache_test.cpp | 2 +- test/core/l2cache/BIUSinkUnit.hpp | 8 +-- test/core/l2cache/L2Cache_test.cpp | 12 ++-- test/core/l2cache/L2SourceUnit.hpp | 23 +++--- .../expected_output/hit_case.out.EXPECTED | 70 +++++++++---------- .../single_access.out.EXPECTED | 46 ++++++------ 15 files changed, 136 insertions(+), 187 deletions(-) diff --git a/core/CPUTopology.cpp b/core/CPUTopology.cpp index 9829a431..3cc5af74 100644 --- a/core/CPUTopology.cpp +++ b/core/CPUTopology.cpp @@ -233,8 +233,8 @@ olympia::CoreTopologySimple::CoreTopologySimple(){ "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_credits", + "cpu.core*.l2cache.ports.out_l2cache_dcache_credits" }, { "cpu.core*.dcache.ports.in_l2cache_resp", @@ -245,8 +245,8 @@ olympia::CoreTopologySimple::CoreTopologySimple(){ "cpu.core*.l2cache.ports.in_icache_l2cache_req" }, { - "cpu.core*.icache.ports.in_l2cache_ack", - "cpu.core*.l2cache.ports.out_l2cache_icache_ack" + "cpu.core*.icache.ports.in_l2cache_credits", + "cpu.core*.l2cache.ports.out_l2cache_icache_credits" }, { "cpu.core*.icache.ports.in_l2cache_resp", @@ -257,8 +257,8 @@ olympia::CoreTopologySimple::CoreTopologySimple(){ "cpu.core*.biu.ports.in_biu_req" }, { - "cpu.core*.biu.ports.out_biu_ack", - "cpu.core*.l2cache.ports.in_biu_l2cache_ack" + "cpu.core*.biu.ports.out_biu_credits", + "cpu.core*.l2cache.ports.in_biu_l2cache_credits" }, { "cpu.core*.biu.ports.out_biu_resp", diff --git a/core/DCache.cpp b/core/DCache.cpp index af8f0e37..e803108d 100644 --- a/core/DCache.cpp +++ b/core/DCache.cpp @@ -11,8 +11,8 @@ namespace olympia { in_lsu_lookup_req_.registerConsumerHandler (CREATE_SPARTA_HANDLER_WITH_DATA(DCache, getInstsFromLSU_, MemoryAccessInfoPtr)); - in_l2cache_ack_.registerConsumerHandler - (CREATE_SPARTA_HANDLER_WITH_DATA(DCache, getAckFromL2Cache_, uint32_t)); + in_l2cache_credits_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(DCache, getCreditsFromL2Cache_, uint32_t)); in_l2cache_resp_.registerConsumerHandler (CREATE_SPARTA_HANDLER_WITH_DATA(DCache, getRespFromL2Cache_, MemoryAccessInfoPtr)); @@ -99,13 +99,13 @@ namespace olympia { busy_ = false; } - void DCache::getAckFromL2Cache_(const uint32_t &ack) { + void DCache::getCreditsFromL2Cache_(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; + dcache_l2cache_credits_ += ack; } } diff --git a/core/DCache.hpp b/core/DCache.hpp index e5982cbd..269d9ce0 100644 --- a/core/DCache.hpp +++ b/core/DCache.hpp @@ -38,7 +38,7 @@ namespace olympia void getInstsFromLSU_(const MemoryAccessInfoPtr & memory_access_info_ptr); - void getAckFromL2Cache_(const uint32_t & ack); + void getCreditsFromL2Cache_(const uint32_t &); void getRespFromL2Cache_(const MemoryAccessInfoPtr & memory_access_info_ptr); @@ -59,7 +59,7 @@ namespace olympia sparta::DataInPort in_lsu_lookup_req_{&unit_port_set_, "in_lsu_lookup_req", 0}; - sparta::DataInPort in_l2cache_ack_{&unit_port_set_, "in_l2cache_ack", 1}; + sparta::DataInPort in_l2cache_credits_{&unit_port_set_, "in_l2cache_credits", 1}; sparta::DataInPort in_l2cache_resp_{&unit_port_set_, "in_l2cache_resp", 1}; diff --git a/core/ICache.cpp b/core/ICache.cpp index 05ef2349..da338783 100644 --- a/core/ICache.cpp +++ b/core/ICache.cpp @@ -27,8 +27,8 @@ namespace olympia { in_fetch_req_.registerConsumerHandler (CREATE_SPARTA_HANDLER_WITH_DATA(ICache, getRequestFromFetch_, MemoryAccessInfoPtr)); - in_l2cache_ack_.registerConsumerHandler - (CREATE_SPARTA_HANDLER_WITH_DATA(ICache, getAckFromL2Cache_, uint32_t)); + in_l2cache_credits_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(ICache, getCreditsFromL2Cache_, uint32_t)); in_l2cache_resp_.registerConsumerHandler (CREATE_SPARTA_HANDLER_WITH_DATA(ICache, getRespFromL2Cache_, MemoryAccessInfoPtr)); @@ -191,7 +191,7 @@ namespace olympia { } } - void ICache::getAckFromL2Cache_(const uint32_t &ack) + void ICache::getCreditsFromL2Cache_(const uint32_t &ack) { l2cache_credits_ += ack; if (!miss_queue_.empty()) { diff --git a/core/ICache.hpp b/core/ICache.hpp index 869edd86..ed18b962 100644 --- a/core/ICache.hpp +++ b/core/ICache.hpp @@ -65,7 +65,7 @@ namespace olympia // Callbacks void getRequestFromFetch_(const MemoryAccessInfoPtr &); - void getAckFromL2Cache_(const uint32_t &); + void getCreditsFromL2Cache_(const uint32_t &); void getRespFromL2Cache_(const MemoryAccessInfoPtr &); using L1Handle = CacheFuncModel::Handle; @@ -92,7 +92,7 @@ namespace olympia sparta::DataInPort in_fetch_req_{&unit_port_set_, "in_fetch_req", 1}; - sparta::DataInPort in_l2cache_ack_{&unit_port_set_, "in_l2cache_ack", 1}; + sparta::DataInPort in_l2cache_credits_{&unit_port_set_, "in_l2cache_credits", 1}; sparta::DataInPort in_l2cache_resp_{&unit_port_set_, "in_l2cache_resp", 1}; diff --git a/mss/BIU.cpp b/mss/BIU.cpp index d1e2fb7f..47aea104 100644 --- a/mss/BIU.cpp +++ b/mss/BIU.cpp @@ -28,7 +28,7 @@ namespace olympia_mss sparta::StartupEvent(node, CREATE_SPARTA_HANDLER(BIU, sendInitialCredits_)); ILOG("BIU construct: #" << node->getGroupIdx()); - ev_handle_mss_ack_ >> ev_handle_biu_req_ >> ev_handle_biu_l2cache_ack_; + ev_handle_mss_ack_ >> ev_handle_biu_req_; } @@ -38,7 +38,7 @@ namespace olympia_mss // Sending Initial credits to L2Cache void BIU::sendInitialCredits_() { - out_biu_ack_.send(biu_req_queue_size_); + out_biu_credits_.send(biu_req_queue_size_); ILOG("Sending initial credits to L2Cache : " << biu_req_queue_size_); } @@ -66,28 +66,24 @@ namespace olympia_mss } // Handle BIU request - void BIU::handle_BIU_Req_() + void BIU::handleBIUReq_() { 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_() + void BIU::handleMSSAck_() { 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)); + // Send out a credit to L2Cache, as we just created space in biu_req_queue_ + out_biu_credits_.send(1); + biu_busy_ = false; // Schedule BIU request handling event only when: @@ -96,7 +92,7 @@ namespace olympia_mss ev_handle_biu_req_.schedule(sparta::Clock::Cycle(0)); } - ILOG("MSS Ack is sent to LSU!"); + ILOG("BIU response sent back!"); } // Receive MSS access acknowledge @@ -114,15 +110,6 @@ 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 55a84acd..ca7aa5ad 100644 --- a/mss/BIU.hpp +++ b/mss/BIU.hpp @@ -67,8 +67,8 @@ namespace olympia_mss // Output Ports //////////////////////////////////////////////////////////////////////////////// - sparta::DataOutPort out_biu_ack_ - {&unit_port_set_, "out_biu_ack"}; + sparta::DataOutPort out_biu_credits_ + {&unit_port_set_, "out_biu_credits"}; sparta::DataOutPort out_biu_resp_ {&unit_port_set_, "out_biu_resp"}; @@ -96,16 +96,11 @@ namespace olympia_mss // 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_)}; + {&unit_event_set_, "handle_biu_req", CREATE_SPARTA_HANDLER(BIU, handleBIUReq_)}; // Event to handle MSS Ack 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_)}; - + {&unit_event_set_, "handle_mss_ack", CREATE_SPARTA_HANDLER(BIU, handleMSSAck_)}; //////////////////////////////////////////////////////////////////////////////// // Callbacks @@ -115,13 +110,10 @@ namespace olympia_mss void receiveReqFromL2Cache_(const olympia::MemoryAccessInfoPtr &); // Handle BIU request - void handle_BIU_Req_(); + void handleBIUReq_(); // Handle MSS Ack - void handle_MSS_Ack_(); - - // Handle ack backto L2Cache - void handle_BIU_L2Cache_Ack_(); + void handleMSSAck_(); // Receive MSS access acknowledge // Q: Does the argument list has to be "const DataType &" ? diff --git a/mss/L2Cache.cpp b/mss/L2Cache.cpp index e2fc24ae..b04aa2e7 100644 --- a/mss/L2Cache.cpp +++ b/mss/L2Cache.cpp @@ -96,8 +96,8 @@ namespace olympia_mss in_biu_resp_.registerConsumerHandler (CREATE_SPARTA_HANDLER_WITH_DATA(L2Cache, getRespFromBIU_, olympia::MemoryAccessInfoPtr)); - in_biu_ack_.registerConsumerHandler - (CREATE_SPARTA_HANDLER_WITH_DATA(L2Cache, getAckFromBIU_, uint32_t)); + in_biu_credits_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(L2Cache, getCreditsFromBIU_, uint32_t)); // Pipeline collection config l2cache_pipeline_.enableCollection(node); @@ -134,12 +134,12 @@ namespace olympia_mss // Sending Initial credits to I/D-Cache void L2Cache::sendInitialCredits_() { if (is_icache_connected_) { - out_l2cache_icache_ack_.send(icache_req_queue_size_); + out_l2cache_icache_credits_.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_); + out_l2cache_dcache_credits_.send(dcache_req_queue_size_); ILOG("Sending initial credits to DCache : " << dcache_req_queue_size_); } } @@ -184,16 +184,16 @@ namespace olympia_mss } } - // Handle BIU ack - void L2Cache::getAckFromBIU_(const uint32_t & ack) { + // Handle BIU Credits + void L2Cache::getCreditsFromBIU_(const uint32_t & credits) { // Update the biu credits - l2cache_biu_credits_ = ack; + l2cache_biu_credits_ += credits; // Kickstart the pipeline issueing ev_issue_req_.schedule(1); - ILOG("Ack received from BIU on the port : Current BIU credit available = " << l2cache_biu_credits_); + ILOG("Credits received from BIU on the port : Current BIU credit available = " << l2cache_biu_credits_); } // Handle L2Cache request from DCache @@ -236,25 +236,6 @@ namespace olympia_mss } } - // Returning ack to DCache - void L2Cache::handle_L2Cache_DCache_Ack_() { - uint32_t available_slots = dcache_req_queue_size_ - dcache_req_queue_.size(); - out_l2cache_dcache_ack_.send(available_slots); - ++num_acks_to_dcache_; - - ILOG("L2Cache->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); - out_l2cache_icache_ack_.send(1); - ++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()); @@ -338,7 +319,10 @@ namespace olympia_mss 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)); + out_l2cache_icache_credits_.send(1, 1); + ILOG("L2Cache->ICache : Credit is sent."); + ++num_acks_to_icache_; + } else if (arbitration_winner == Channel::DCACHE) { @@ -354,7 +338,10 @@ namespace olympia_mss 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)); + out_l2cache_dcache_credits_.send(1, 1); + ILOG("L2Cache->DCache : Credit is sent."); + ++num_acks_to_dcache_; + } else if (arbitration_winner == Channel::NO_ACCESS) { // Schedule a ev_create_req_ event again to see if the the new request diff --git a/mss/L2Cache.hpp b/mss/L2Cache.hpp index 22da8f99..f367d373 100644 --- a/mss/L2Cache.hpp +++ b/mss/L2Cache.hpp @@ -107,8 +107,8 @@ namespace olympia_mss 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}; + sparta::DataInPort in_biu_credits_ + {&unit_port_set_, "in_biu_l2cache_credits", 1}; //////////////////////////////////////////////////////////////////////////////// @@ -124,11 +124,11 @@ namespace olympia_mss 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_icache_credits_ + {&unit_port_set_, "out_l2cache_icache_credits"}; - sparta::DataOutPort out_l2cache_dcache_ack_ - {&unit_port_set_, "out_l2cache_dcache_ack"}; + sparta::DataOutPort out_l2cache_dcache_credits_ + {&unit_port_set_, "out_l2cache_dcache_credits"}; //////////////////////////////////////////////////////////////////////////////// @@ -248,14 +248,6 @@ namespace olympia_mss 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_)}; @@ -277,8 +269,8 @@ namespace olympia_mss // Receive BIU access Response void getRespFromBIU_(const olympia::MemoryAccessInfoPtr &); - // Receive BIU ack Response - void getAckFromBIU_(const uint32_t &); + // Receive BIU credits Response + void getCreditsFromBIU_(const uint32_t &); // Handle L2Cache request from DCache void handle_DCache_L2Cache_Req_(); @@ -289,18 +281,12 @@ namespace olympia_mss // Handle L2Cache request to BIU void handle_L2Cache_BIU_Req_(); - // Handle L2Cahe resp for ICache + // Handle L2Cache resp for ICache void handle_L2Cache_ICache_Resp_(); - // Handle L2Cahe resp for DCache + // Handle L2Cache 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_(); diff --git a/test/core/icache/ICache_test.cpp b/test/core/icache/ICache_test.cpp index bac017fa..790fe3df 100644 --- a/test/core/icache/ICache_test.cpp +++ b/test/core/icache/ICache_test.cpp @@ -81,7 +81,7 @@ class ICacheSim : public sparta::app::Simulation{ root_node->getChildAs("sink.ports.in_icache_req")); sparta::bind(root_node->getChildAs("icache.ports.in_l2cache_resp"), root_node->getChildAs("sink.ports.out_icache_resp")); - sparta::bind(root_node->getChildAs("icache.ports.in_l2cache_ack"), + sparta::bind(root_node->getChildAs("icache.ports.in_l2cache_credits"), root_node->getChildAs("sink.ports.out_icache_credit")); // Bind up checker diff --git a/test/core/l2cache/BIUSinkUnit.hpp b/test/core/l2cache/BIUSinkUnit.hpp index 47e2b044..fe52c3df 100644 --- a/test/core/l2cache/BIUSinkUnit.hpp +++ b/test/core/l2cache/BIUSinkUnit.hpp @@ -43,23 +43,21 @@ namespace l2cache_test // Sending Initial credits to L2Cache void sendInitialCredits_() { uint32_t biu_req_queue_size_ = 32; - out_biu_ack_.send(biu_req_queue_size_); + out_biu_credits_.send(biu_req_queue_size_); ILOG("Sending initial credits to L2Cache : " << biu_req_queue_size_); } void sinkInst_(const olympia::MemoryAccessInfoPtr & mem_access_info_ptr) { ILOG("Instruction: '" << mem_access_info_ptr->getInstPtr() << "' sinked"); - uint32_t biu_req_queue_size_ = 32; - - out_biu_ack_.send(biu_req_queue_size_, sink_latency_); + out_biu_credits_.send(1, sink_latency_); out_biu_resp_.send(mem_access_info_ptr, 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"}; + sparta::DataOutPort out_biu_credits_ {&unit_port_set_, "out_biu_credits"}; std::string purpose_; sparta::Clock::Cycle sink_latency_; diff --git a/test/core/l2cache/L2Cache_test.cpp b/test/core/l2cache/L2Cache_test.cpp index 76a6a169..dee33a85 100644 --- a/test/core/l2cache/L2Cache_test.cpp +++ b/test/core/l2cache/L2Cache_test.cpp @@ -134,22 +134,22 @@ class L2CacheSim : public sparta::app::Simulation 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("dcache.ports.in_source_credits"), + root_node->getChildAs("l2cache.ports.out_l2cache_dcache_credits")); 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("icache.ports.in_source_credits"), + root_node->getChildAs("l2cache.ports.out_l2cache_icache_credits")); 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")); + sparta::bind(root_node->getChildAs("biu.ports.out_biu_credits"), + root_node->getChildAs("l2cache.ports.in_biu_l2cache_credits")); } // Allocators. Last thing to delete std::unique_ptr allocators_tn_; diff --git a/test/core/l2cache/L2SourceUnit.hpp b/test/core/l2cache/L2SourceUnit.hpp index 05aab52f..64c87c81 100644 --- a/test/core/l2cache/L2SourceUnit.hpp +++ b/test/core/l2cache/L2SourceUnit.hpp @@ -43,8 +43,8 @@ namespace l2cache_test in_source_resp_.registerConsumerHandler (CREATE_SPARTA_HANDLER_WITH_DATA(L2SourceUnit, ReceiveInst_, olympia::MemoryAccessInfoPtr)); - in_source_ack_.registerConsumerHandler - (CREATE_SPARTA_HANDLER_WITH_DATA(L2SourceUnit, ReceiveAck_, uint32_t)); + in_source_credits_.registerConsumerHandler + (CREATE_SPARTA_HANDLER_WITH_DATA(L2SourceUnit, ReceiveCredits_, uint32_t)); if(params->input_file != "") { inst_generator_ = olympia::InstGenerator::createGenerator(mavis_facade_, params->input_file, false); @@ -58,7 +58,7 @@ namespace l2cache_test void onStartingTeardown_() override { 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"); + sparta_assert(unit_enable_ == true && pending_credits_ == 0, "pending_credits remaining in the L2SourceUnit"); } private: @@ -88,7 +88,7 @@ namespace l2cache_test ILOG("Instruction: '" << req_inst_queue_.front()->getInstPtr() << "' Requested"); pending_reqs_++; - pending_acks_++; + pending_credits_++; out_source_req_.send(req_inst_queue_.front()); req_inst_queue_.erase(req_inst_queue_.begin()); @@ -99,18 +99,17 @@ namespace l2cache_test ILOG("Instruction: '" << mem_info_ptr->getInstPtr() << "' Received"); } - void ReceiveAck_(const uint32_t & ack) { - pending_acks_--; - ILOG("Ack: '" << ack << "' Received"); + void ReceiveCredits_(const uint32_t & credits) { + pending_credits_--; + ILOG("Ack: '" << credits << "' Received"); } - sparta::DataInPort in_source_resp_ {&unit_port_set_, "in_source_resp", + 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::DataInPort in_source_credits_ {&unit_port_set_, "in_source_credits"}; + sparta::DataOutPort out_source_req_ {&unit_port_set_, "out_source_req"}; - sparta::DataOutPort out_source_req_ {&unit_port_set_, "out_source_req"}; - - uint32_t pending_acks_ = 1; + uint32_t pending_credits_ = 1; uint32_t pending_reqs_ = 0; uint32_t unique_id_ = 0; diff --git a/test/core/l2cache/expected_output/hit_case.out.EXPECTED b/test/core/l2cache/expected_output/hit_case.out.EXPECTED index c47a5eac..6f77671c 100644 --- a/test/core/l2cache/expected_output/hit_case.out.EXPECTED +++ b/test/core/l2cache/expected_output/hit_case.out.EXPECTED @@ -3,43 +3,43 @@ #Exe: #SimulatorVersion: #Repro: -#Start: Thursday Thu Feb 8 10:29:35 2024 -#Elapsed: 0.024913s +#Start: Monday Mon Feb 12 14:22:32 2024 +#Elapsed: 0.002725s {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 +{0000000000 00000000 top.icache info} ReceiveCredits_: Ack: '8' Received +{0000000000 00000000 top.dcache info} ReceiveCredits_: Ack: '8' Received +{0000000000 00000000 top.dcache info} req_inst_: Instruction: 'uid: 0 BEFORE_FETCH 0 pid: 1 'sw 3' ' Requested +{0000000000 00000000 top.icache info} req_inst_: Instruction: 'uid: 0 BEFORE_FETCH 0 pid: 1 'sw 3' ' Requested +{0000000001 00000001 top.l2cache info} getCreditsFromBIU_: Credits 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. +{0000000001 00000001 top.l2cache info} create_Req_: L2Cache->ICache : Credit is sent. +{0000000002 00000002 top.icache info} ReceiveCredits_: Ack: '1' Received {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. +{0000000002 00000002 top.l2cache info} create_Req_: L2Cache->DCache : Credit is sent. +{0000000003 00000003 top.dcache info} ReceiveCredits_: Ack: '1' Received {0000000003 00000003 top.l2cache info} issue_Req_: Request is sent to Pipeline! SrcUnit : DCACHE -{0000000011 00000011 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000011 00000011 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef uid: 0 BEFORE_FETCH 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 : memptr: deadbeef uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000012 00000012 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef uid: 0 BEFORE_FETCH 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 : memptr: deadbeef uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000012 00000012 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef uid: 0 BEFORE_FETCH 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 : memptr: deadbeef uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000013 00000013 top.biu info} sinkInst_: Instruction: 'uid: 0 BEFORE_FETCH 0 pid: 1 'sw 3' ' sinked +{0000000013 00000013 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef uid: 0 BEFORE_FETCH 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 +{0000000024 00000024 top.l2cache info} getCreditsFromBIU_: Credits 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 @@ -48,44 +48,44 @@ {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 : memptr: deadbeef uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000044 00000044 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef uid: 0 BEFORE_FETCH 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 : memptr: deadbeef uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000045 00000045 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef uid: 0 BEFORE_FETCH 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 : memptr: deadbeef uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000045 00000045 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef uid: 0 BEFORE_FETCH 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 : memptr: deadbeef uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000046 00000046 top.icache info} ReceiveInst_: Instruction: 'uid: 0 BEFORE_FETCH 0 pid: 1 'sw 3' ' Received +{0000000046 00000046 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef uid: 0 BEFORE_FETCH 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 +{0000000047 00000047 top.dcache info} ReceiveInst_: Instruction: 'uid: 0 BEFORE_FETCH 0 pid: 1 'sw 3' ' Received +{0000000050 00000050 top.dcache info} req_inst_: Instruction: 'uid: 1 BEFORE_FETCH 0 pid: 2 'lw 5,3,4' ' Requested +{0000000050 00000050 top.icache info} req_inst_: Instruction: 'uid: 1 BEFORE_FETCH 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. +{0000000051 00000051 top.l2cache info} create_Req_: L2Cache->ICache : Credit is sent. +{0000000052 00000052 top.icache info} ReceiveCredits_: Ack: '1' Received {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. +{0000000052 00000052 top.l2cache info} create_Req_: L2Cache->DCache : Credit is sent. +{0000000053 00000053 top.dcache info} ReceiveCredits_: Ack: '1' Received {0000000053 00000053 top.l2cache info} issue_Req_: Request is sent to Pipeline! SrcUnit : DCACHE -{0000000061 00000061 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef uid: 1 FETCHED 0 pid: 2 'lw 5,3,4' +{0000000061 00000061 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef uid: 1 BEFORE_FETCH 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 : memptr: deadbeef uid: 1 FETCHED 0 pid: 2 'lw 5,3,4' +{0000000062 00000062 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef uid: 1 BEFORE_FETCH 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 : memptr: deadbeef uid: 1 FETCHED 0 pid: 2 'lw 5,3,4' +{0000000062 00000062 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef uid: 1 BEFORE_FETCH 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 : memptr: deadbeef uid: 1 FETCHED 0 pid: 2 'lw 5,3,4' +{0000000063 00000063 top.icache info} ReceiveInst_: Instruction: 'uid: 1 BEFORE_FETCH 0 pid: 2 'lw 5,3,4' ' Received +{0000000063 00000063 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef uid: 1 BEFORE_FETCH 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 +{0000000064 00000064 top.dcache info} ReceiveInst_: Instruction: 'uid: 1 BEFORE_FETCH 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 index d7e055da..eb45aa14 100644 --- a/test/core/l2cache/expected_output/single_access.out.EXPECTED +++ b/test/core/l2cache/expected_output/single_access.out.EXPECTED @@ -3,43 +3,43 @@ #Exe: #SimulatorVersion: #Repro: -#Start: Thursday Thu Feb 8 10:29:35 2024 -#Elapsed: 0.031446s +#Start: Monday Mon Feb 12 14:21:20 2024 +#Elapsed: 0.003237s {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 +{0000000000 00000000 top.icache info} ReceiveCredits_: Ack: '8' Received +{0000000000 00000000 top.dcache info} ReceiveCredits_: Ack: '8' Received +{0000000000 00000000 top.dcache info} req_inst_: Instruction: 'uid: 0 BEFORE_FETCH 0 pid: 1 'sw 3' ' Requested +{0000000000 00000000 top.icache info} req_inst_: Instruction: 'uid: 0 BEFORE_FETCH 0 pid: 1 'sw 3' ' Requested +{0000000001 00000001 top.l2cache info} getCreditsFromBIU_: Credits 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. +{0000000001 00000001 top.l2cache info} create_Req_: L2Cache->ICache : Credit is sent. +{0000000002 00000002 top.icache info} ReceiveCredits_: Ack: '1' Received {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. +{0000000002 00000002 top.l2cache info} create_Req_: L2Cache->DCache : Credit is sent. +{0000000003 00000003 top.dcache info} ReceiveCredits_: Ack: '1' Received {0000000003 00000003 top.l2cache info} issue_Req_: Request is sent to Pipeline! SrcUnit : DCACHE -{0000000011 00000011 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000011 00000011 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef uid: 0 BEFORE_FETCH 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 : memptr: deadbeef uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000012 00000012 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef uid: 0 BEFORE_FETCH 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 : memptr: deadbeef uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000012 00000012 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef uid: 0 BEFORE_FETCH 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 : memptr: deadbeef uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000013 00000013 top.biu info} sinkInst_: Instruction: 'uid: 0 BEFORE_FETCH 0 pid: 1 'sw 3' ' sinked +{0000000013 00000013 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef uid: 0 BEFORE_FETCH 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 +{0000000024 00000024 top.l2cache info} getCreditsFromBIU_: Credits 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 @@ -48,16 +48,16 @@ {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 : memptr: deadbeef uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000044 00000044 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef uid: 0 BEFORE_FETCH 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 : memptr: deadbeef uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000045 00000045 top.l2cache info} handleCacheAccessRequest_: Pipeline stage CACHE_LOOKUP : memptr: deadbeef uid: 0 BEFORE_FETCH 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 : memptr: deadbeef uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000045 00000045 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef uid: 0 BEFORE_FETCH 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 : memptr: deadbeef uid: 0 FETCHED 0 pid: 1 'sw 3' +{0000000046 00000046 top.icache info} ReceiveInst_: Instruction: 'uid: 0 BEFORE_FETCH 0 pid: 1 'sw 3' ' Received +{0000000046 00000046 top.l2cache info} handleCacheAccessResult_: Pipeline stage HIT_MISS_HANDLING : memptr: deadbeef uid: 0 BEFORE_FETCH 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 +{0000000047 00000047 top.dcache info} ReceiveInst_: Instruction: 'uid: 0 BEFORE_FETCH 0 pid: 1 'sw 3' ' Received From 7fd5471044e3f6ee80c0071637c6c591c91e7022 Mon Sep 17 00:00:00 2001 From: Daniel Bone Date: Fri, 16 Feb 2024 12:51:44 +0000 Subject: [PATCH 6/7] Add fetch block size, and buffer size parameters to Fetch --- core/Fetch.cpp | 57 ++++++++++++++++++++++++++++++++----------------- core/Fetch.hpp | 46 +++++++++++++++++++++++++++------------ core/ICache.cpp | 2 +- 3 files changed, 70 insertions(+), 35 deletions(-) diff --git a/core/Fetch.cpp b/core/Fetch.cpp index b4124deb..26b81f77 100644 --- a/core/Fetch.cpp +++ b/core/Fetch.cpp @@ -11,6 +11,7 @@ #include "MavisUnit.hpp" #include "OlympiaAllocators.hpp" +#include "sparta/utils/MathUtils.hpp" #include "sparta/utils/LogUtils.hpp" #include "sparta/events/StartupEvent.hpp" @@ -21,10 +22,12 @@ namespace olympia Fetch::Fetch(sparta::TreeNode * node, const FetchParameterSet * p) : sparta::Unit(node), + my_clk_(getClock()), num_insts_to_fetch_(p->num_to_fetch), skip_nonuser_mode_(p->skip_nonuser_mode), - my_clk_(getClock()), - ibuf_capacity_(32), // buffer up instructions read from trace + icache_block_shift_(sparta::utils::floor_log2(p->block_width.getValue())), + ibuf_capacity_(std::ceil(p->block_width / 2)), // buffer up instructions read from trace + fetch_buffer_capacity_(p->fetch_buffer_size), memory_access_allocator_(sparta::notNull(OlympiaAllocators::getOlympiaAllocators(node)) ->memory_access_allocator) { @@ -43,12 +46,16 @@ namespace olympia ev_fetch_insts.reset(new sparta::SingleCycleUniqueEvent<>(&unit_event_set_, "fetch_instruction_data", CREATE_SPARTA_HANDLER(Fetch, fetchInstruction_))); - ev_drive_insts.reset(new sparta::SingleCycleUniqueEvent<>(&unit_event_set_, "drive_instructions_out", - CREATE_SPARTA_HANDLER(Fetch, driveInstructions_))); + ev_send_insts.reset(new sparta::SingleCycleUniqueEvent<>(&unit_event_set_, "send_instructions_out", + CREATE_SPARTA_HANDLER(Fetch, sendInstructions_))); // Schedule a single event to start reading from a trace file sparta::StartupEvent(node, CREATE_SPARTA_HANDLER(Fetch, initialize_)); + // Capture when the simulation is stopped prematurely by the ROB i.e. hitting retire limit + node->getParent()->registerForNotification( + this, "rob_stopped_notif_channel", false /* ROB maybe not be constructed yet */); + } Fetch::~Fetch() {} @@ -85,10 +92,9 @@ namespace olympia if (credits_icache_ == 0 || ibuf_.empty() || fetch_buffer_.size() > fetch_buffer_capacity_) { return; } // Gather instructions going to the same cacheblock - // TODO this should be a constant related to the ICache - const uint32_t icache_block_shift_ = 4; // 16B blocks.. - // TODO instructions which straddle should be placed into the next group - auto different_blocks = [icache_block_shift_](const auto &lhs, const auto &rhs) { + // NOTE: This doesn't deal with instructions straddling the blocks, + // they should be placed into the next group + auto different_blocks = [this](const auto &lhs, const auto &rhs) { return (lhs->getPC() >> icache_block_shift_) != (rhs->getPC() >> icache_block_shift_) || lhs->isTakenBranch() || rhs->isCoF(); @@ -132,7 +138,7 @@ namespace olympia } // Read instructions from the fetch buffer and send them to decode - void Fetch::driveInstructions_() + void Fetch::sendInstructions_() { const uint32_t upper = std::min({credits_inst_queue_, num_insts_to_fetch_, static_cast(fetch_buffer_.size())}); @@ -175,7 +181,7 @@ namespace olympia out_fetch_queue_write_.send(insts_to_send); if (!fetch_buffer_.empty() && credits_inst_queue_ > 0) { - ev_drive_insts->schedule(1); + ev_send_insts->schedule(1); } ev_fetch_insts->schedule(1); @@ -184,15 +190,21 @@ namespace olympia void Fetch::receiveCacheResponse_(const MemoryAccessInfoPtr &response) { + const auto & fetched_insts = response->getFetchGroup(); + sparta_assert(fetched_insts != nullptr, "no instructions set for cache request"); if (response->getCacheState() == MemoryAccessInfo::CacheState::HIT) { + ILOG("Cache hit response recieved for insts: " << fetched_insts); // Mark instructions as fetched - auto & fetched_insts = response->getFetchGroup(); - sparta_assert(fetched_insts != nullptr); for(auto & inst : *fetched_insts) { inst->setStatus(Inst::Status::FETCHED); } + ev_send_insts->schedule(sparta::Clock::Cycle(0)); + } - ev_drive_insts->schedule(sparta::Clock::Cycle(0)); + // Log misses + if (SPARTA_EXPECT_FALSE(info_logger_) && + response->getCacheState() == MemoryAccessInfo::CacheState::MISS) { + ILOG("Cache miss on insts: " << fetched_insts); } } @@ -216,7 +228,7 @@ namespace olympia << ", total decode_credits=" << credits_inst_queue_); // Schedule a fetch event this cycle - ev_drive_insts->schedule(sparta::Clock::Cycle(0)); + ev_send_insts->schedule(sparta::Clock::Cycle(0)); } // Called from FlushManager via in_fetch_flush_redirect_port @@ -250,9 +262,14 @@ namespace olympia // speculative_path_ = false; } + void Fetch::onROBTerminate_(const bool & stopped) + { + rob_stopped_simulation_ = stopped; + } + void Fetch::dumpDebugContent_(std::ostream & output) const { - output << "Fetch Contents" << std::endl; + output << "Fetch Buffer Contents" << std::endl; for (const auto & entry : fetch_buffer_) { output << '\t' << entry << std::endl; @@ -261,11 +278,11 @@ namespace olympia void Fetch::onStartingTeardown_() { - // if ((false == rob_stopped_simulation_) && (false == fetch_buffer_.empty())) - // { - // dumpDebugContent_(std::cerr); - // sparta_assert(false, "fetch buffer has pending instructions"); - // } + if ((false == rob_stopped_simulation_) && (false == fetch_buffer_.empty())) + { + dumpDebugContent_(std::cerr); + sparta_assert(false, "fetch buffer has pending instructions"); + } } } diff --git a/core/Fetch.hpp b/core/Fetch.hpp index 36b4c236..8eb46c38 100644 --- a/core/Fetch.hpp +++ b/core/Fetch.hpp @@ -57,6 +57,8 @@ namespace olympia PARAMETER(uint32_t, num_to_fetch, 4, "Number of instructions to fetch") PARAMETER(bool, skip_nonuser_mode, false, "For STF traces, skip system instructions if present") + PARAMETER(uint32_t, block_width, 16, "Block width of memory read requests, in bytes") + PARAMETER(uint32_t, fetch_buffer_size, 8, "Size of fetch buffer in blocks") }; /** @@ -103,6 +105,9 @@ namespace olympia //////////////////////////////////////////////////////////////////////////////// // Instruction fetch + + // Unit's clock + const sparta::Clock * my_clk_ = nullptr; // Number of instructions to fetch const uint32_t num_insts_to_fetch_; @@ -112,34 +117,45 @@ namespace olympia // Number of credits from decode that fetch has uint32_t credits_inst_queue_ = 0; + // Number of credits available in the ICache uint32_t credits_icache_ = 0; - // Unit's clock - const sparta::Clock * my_clk_ = nullptr; + // Amount to left shift an Instructions PC to get the ICache block number + const uint32_t icache_block_shift_; + + // Buffers up instructions read from the tracefile + std::deque ibuf_; // Size of trace buffer (must be sized >= L1ICache bandwidth / 2B) const uint32_t ibuf_capacity_; + // Fetch buffer: Holds a queue of instructions that are either + // waiting for an ICache hit response, or they're ready to be + // send to decode + std::deque fetch_buffer_; + + // Size of fetch buffer, tracked separately as it sized + // in terms of icache block requests, not instructions. + const uint32_t fetch_buffer_capacity_; + uint32_t fetch_buffer_occupancy_ = 0; + // allocator for ICache transactions MemoryAccessInfoAllocator & memory_access_allocator_; - uint32_t fetch_buffer_occupancy_ = 0; - const uint32_t fetch_buffer_capacity_ = 16; + // ROB terminated simulation + bool rob_stopped_simulation_ {false}; // Instruction generation std::unique_ptr inst_generator_; - // Fetch instruction event, triggered when there are credits - // from decode. The callback set is either to fetch random - // instructions or a perfect IPC set + // Fetch instruction event, the callback is set to request + // instructions from the instruction cache and place them in the + // fetch buffer. std::unique_ptr> ev_fetch_insts; - std::unique_ptr> ev_drive_insts; - // Buffers up instructions read from the tracefile - std::deque ibuf_; - - // Holds fetched instructions from the ICache - std::deque fetch_buffer_; + // Send instructions event, the callback is set to read instructions + // from the fetch buffer and send them to the decode unit + std::unique_ptr> ev_send_insts; //////////////////////////////////////////////////////////////////////////////// // Callbacks @@ -154,7 +170,7 @@ namespace olympia void fetchInstruction_(); // Read instructions from the fetch buffer and send them to decode - void driveInstructions_(); + void sendInstructions_(); // Receive flush from FlushManager void flushFetch_(const FlushManager::FlushingCriteria &); @@ -165,6 +181,8 @@ namespace olympia // Receive read data from the instruction cache void receiveCacheResponse_(const MemoryAccessInfoPtr &); + // Debug callbacks, used to log fetch buffer contents + void onROBTerminate_(const bool&); void onStartingTeardown_() override; void dumpDebugContent_(std::ostream&) const override final; diff --git a/core/ICache.cpp b/core/ICache.cpp index da338783..b78b8a58 100644 --- a/core/ICache.cpp +++ b/core/ICache.cpp @@ -12,7 +12,7 @@ namespace olympia { - const char ICache::name[] = "instruction_cache"; + const char ICache::name[] = "icache"; ICache::ICache(sparta::TreeNode *node, const ICacheParameterSet *p) : sparta::Unit(node), From 50acbb1347df636bd9e9ba96daf6170c3beda59f Mon Sep 17 00:00:00 2001 From: Daniel Bone Date: Mon, 26 Feb 2024 14:48:03 +0000 Subject: [PATCH 7/7] update dcache comments on L2Cache credits --- core/DCache.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/core/DCache.cpp b/core/DCache.cpp index e803108d..367bd60e 100644 --- a/core/DCache.cpp +++ b/core/DCache.cpp @@ -78,7 +78,9 @@ 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 + // DCache is blocking for now. Busy is set on miss, until the miss is + // resolved by the L2. + // For NB behaviour: 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_) { @@ -100,11 +102,6 @@ namespace olympia { } void DCache::getCreditsFromL2Cache_(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; }