From 34360a99eec517eb843a844e9bdcc94ca24f2c5b Mon Sep 17 00:00:00 2001 From: Suraj Shirvankar Date: Thu, 21 Dec 2023 09:43:28 -0600 Subject: [PATCH] Raise exception in LSU when ROB is drained (#108) This PR adds a notification from ROB to the entire simulation that the ROB is about to or is expecting simulation to correctly terminate. If a unit listens for that notification, it can use that information to determine if simulation ended improperly, meaning, it might still have work to do, but didn't expect simulation to stop. An example of how this can happen: a unit is NOT scheduling an event to do work (a bug) because of a missed path to "wake" it up. If the sparta::Scheduler ends simulation because it has no events to run, the unit will not receive the end-of-simulation notification from the ROB and can throw an exception in this case. --------- Signed-off-by: Suraj Shirvankar Co-authored-by: Knute Lingaard --- core/Fetch.hpp | 1 - core/Inst.hpp | 10 ++++++++-- core/InstGenerator.cpp | 10 +++++++--- core/LSU.cpp | 25 +++++++++++++++++++++++++ core/LSU.hpp | 10 +++++++--- core/ROB.cpp | 19 +++++++++++++++++-- core/ROB.hpp | 2 ++ test/core/rename/Rename_test.cpp | 10 ++++++++++ 8 files changed, 76 insertions(+), 11 deletions(-) diff --git a/core/Fetch.hpp b/core/Fetch.hpp index 6a34e9ef..8836fc24 100644 --- a/core/Fetch.hpp +++ b/core/Fetch.hpp @@ -92,7 +92,6 @@ namespace olympia // Number of instructions to fetch const uint32_t num_insts_to_fetch_; - // For traces with system instructions, skip them const bool skip_nonuser_mode_; diff --git a/core/Inst.hpp b/core/Inst.hpp index 7aca33c9..4fcd61aa 100644 --- a/core/Inst.hpp +++ b/core/Inst.hpp @@ -136,13 +136,18 @@ namespace olympia return inst_arch_info_->getTargetPipe(); } + // ROB handling -- mark this instruction as the oldest in the machine void setOldest(bool oldest, sparta::Scheduleable * rob_retire_event) { ev_retire_ = rob_retire_event; is_oldest_ = oldest; } - bool isMarkedOldest() const { return is_oldest_; } + // Instruction trace/JSON generation -- mark instruction as + // last in trace/JSON file. + void setLast() { is_last_ = true; } + bool getLast() const { return is_last_; } + // Set the instructions unique ID. This ID in constantly // incremented and does not repeat. The same instruction in a // trace can have different unique IDs (due to flushing) @@ -217,7 +222,8 @@ namespace olympia sparta::memory::addr_t inst_pc_ = 0; // Instruction's PC sparta::memory::addr_t target_vaddr_ = 0; // Instruction's Target PC (for branches, loads/stores) - bool is_oldest_ = false; + bool is_oldest_ = false; + bool is_last_ = false; // Is last intruction of trace uint64_t unique_id_ = 0; // Supplied by Fetch uint64_t program_id_ = 0; // Supplied by a trace Reader or execution backend bool is_speculative_ = false; // Is this instruction soon to be flushed? diff --git a/core/InstGenerator.cpp b/core/InstGenerator.cpp index 096bcd3b..2d5eaba7 100644 --- a/core/InstGenerator.cpp +++ b/core/InstGenerator.cpp @@ -104,9 +104,10 @@ namespace olympia } ++curr_inst_index_; - if (inst != nullptr) { - inst->setUniqueID(++unique_id_); - inst->setProgramID(unique_id_); + inst->setUniqueID(++unique_id_); + inst->setProgramID(unique_id_); + if(isDone()) { + inst->setLast(); } return inst; @@ -176,6 +177,9 @@ namespace olympia //inst->setVAddrVector(std::move(addrs)); } ++next_it_; + if(isDone()) { + inst->setLast(); + } return inst; } catch(std::exception & excpt) { diff --git a/core/LSU.cpp b/core/LSU.cpp index 382a0eb8..630f89bd 100644 --- a/core/LSU.cpp +++ b/core/LSU.cpp @@ -94,6 +94,9 @@ namespace olympia ldst_pipeline_.registerHandlerAtStage(cache_lookup_stage_, CREATE_SPARTA_HANDLER(LSU, handleCacheLookupReq_)); + node->getParent()->registerForNotification( + this, "rob_notif_channel", false /* ROB maybe not be constructed yet */); + ldst_pipeline_.registerHandlerAtStage(cache_read_stage_, CREATE_SPARTA_HANDLER(LSU, handleCacheRead_)); @@ -108,6 +111,8 @@ namespace olympia ILOG("LSU construct: #" << node->getGroupIdx()); } + void LSU::onRobDrained_(const bool & val) { retire_done_ = val; } + LSU::~LSU() { DLOG(getContainer()->getLocation() << ": " << load_store_info_allocator_.getNumAllocated() @@ -116,6 +121,17 @@ namespace olympia << " MemoryAccessInfo objects allocated/created"); } + void LSU::onStartingTeardown_() + { + // If ROB has not stopped the simulation & + // the ldst has entries to process we should fail + if ((false == retire_done_) && (false == ldst_inst_queue_.empty())) + { + dumpDebugContent_(std::cerr); + sparta_assert(false, "Issue queue has pending instructions"); + } + } + //////////////////////////////////////////////////////////////////////////////// // Callbacks //////////////////////////////////////////////////////////////////////////////// @@ -761,6 +777,15 @@ namespace olympia // Otherwise, assertion error is fired inside arbitrateInstIssue_() } + void LSU::dumpDebugContent_(std::ostream & output) const + { + output << "LSU Contents" << std::endl; + for (const auto & entry : ldst_inst_queue_) + { + output << '\t' << entry << std::endl; + } + } + void LSU::replayReady_(const LoadStoreInstInfoPtr & replay_inst_ptr) { ILOG("Replay inst ready " << replay_inst_ptr); diff --git a/core/LSU.hpp b/core/LSU.hpp index 674db63c..b73f6508 100644 --- a/core/LSU.hpp +++ b/core/LSU.hpp @@ -77,14 +77,18 @@ namespace olympia // Type Name/Alias Declaration //////////////////////////////////////////////////////////////////////////////// + bool retire_done_ = false; using LoadStoreInstInfoPtr = sparta::SpartaSharedPointer; + using LoadStoreInstIterator = sparta::Buffer::const_iterator; + using FlushCriteria = FlushManager::FlushingCriteria; - using LoadStoreInstIterator = sparta::Buffer::const_iterator; + void onRobDrained_(const bool & val); private: using ScoreboardViews = std::array, core_types::N_REGFILES>; + ScoreboardViews scoreboard_views_; //////////////////////////////////////////////////////////////////////////////// // Input Ports @@ -190,7 +194,6 @@ namespace olympia //////////////////////////////////////////////////////////////////////////////// // Callbacks //////////////////////////////////////////////////////////////////////////////// - // Send initial credits (ldst_inst_queue_size_) to Dispatch Unit void sendInitialCredits_(); @@ -229,6 +232,7 @@ namespace olympia // Handle instruction flush in LSU void handleFlush_(const FlushCriteria &); + void dumpDebugContent_(std::ostream & output) const override final; // Instructions in the replay ready to issue void replayReady_(const LoadStoreInstInfoPtr &); @@ -319,7 +323,7 @@ namespace olympia // When simulation is ending (error or not), this function // will be called - void onStartingTeardown_() override {} + void onStartingTeardown_() override; bool olderStoresExists_(const InstPtr & inst_ptr); diff --git a/core/ROB.cpp b/core/ROB.cpp index 16dcf3d0..ab9a5032 100644 --- a/core/ROB.cpp +++ b/core/ROB.cpp @@ -49,10 +49,16 @@ namespace olympia registerConsumerHandler(CREATE_SPARTA_HANDLER_WITH_DATA(ROB, handleFlush_, FlushManager::FlushingCriteria)); - // This event is ALWAYS scheduled, but it should not keep - // simulation continuing on. + // Do not allow this event to keep simulation alive ev_ensure_forward_progress_.setContinuing(false); + // Notify other components when ROB stops the simulation + rob_drained_notif_source_.reset(new sparta::NotificationSource( + this->getContainer(), + "rob_notif_channel", + "Notification channel for rob", + "rob_notif_channel" + )); // Send initial credits to anyone that cares. Probably Dispatch. sparta::StartupEvent(node, CREATE_SPARTA_HANDLER(ROB, sendInitialCredits_)); } @@ -133,6 +139,7 @@ namespace olympia // Will be true if the user provides a -i option if (SPARTA_EXPECT_FALSE((num_retired_ == num_insts_to_retire_))) { rob_stopped_simulation_ = true; + rob_drained_notif_source_->postNotification(true); getScheduler()->stopRunning(); break; } @@ -151,6 +158,14 @@ namespace olympia break; } + // Check to see if this is the last instruction of the + // trace + if(ex_inst.getLast()) { + rob_stopped_simulation_ = true; + rob_drained_notif_source_->postNotification(true); + // No need to stop the scheduler -- let simulation + // drain normally. Also, don't need to check forward progress + } } else { break; diff --git a/core/ROB.hpp b/core/ROB.hpp index f6a828cc..89aa83d4 100644 --- a/core/ROB.hpp +++ b/core/ROB.hpp @@ -116,6 +116,8 @@ namespace olympia sparta::Event<> ev_ensure_forward_progress_{&unit_event_set_, "forward_progress_check", CREATE_SPARTA_HANDLER(ROB, checkForwardProgress_)}; + std::unique_ptr> rob_drained_notif_source_; + void sendInitialCredits_(); void retireEvent_(); void robAppended_(const InstGroup &); diff --git a/test/core/rename/Rename_test.cpp b/test/core/rename/Rename_test.cpp index a86b6680..72241ba5 100644 --- a/test/core/rename/Rename_test.cpp +++ b/test/core/rename/Rename_test.cpp @@ -150,6 +150,14 @@ class olympia::LSUTester // while the ADD instruction is running, the STORE instruction should NOT issue EXPECT_TRUE(lsu.lsu_insts_issued_ == 0); } + + void clear_entries(olympia::LSU &lsu){ + auto iter = lsu.ldst_inst_queue_.begin(); + while(iter != lsu.ldst_inst_queue_.end()){ + auto x(iter++); + lsu.ldst_inst_queue_.erase(x); + } + } }; // @@ -444,6 +452,7 @@ void runTest(int argc, char **argv) cls.runSimulator(&sim, 6); executepipe_tester.test_dependent_integer_first_instruction(*my_executepipe); lsu_tester.test_dependent_lsu_instruction(*my_lsu); + lsu_tester.clear_entries(*my_lsu); } else if(input_file == "raw_float_lsu.json"){ // testing RAW dependency for data operand @@ -457,6 +466,7 @@ void runTest(int argc, char **argv) cls.runSimulator(&sim, 6); executepipe_tester.test_dependent_integer_first_instruction(*my_executepipe); lsu_tester.test_dependent_lsu_instruction(*my_lsu); + lsu_tester.clear_entries(*my_lsu); } else if(input_file == "amoadd.json"){ sparta::Scheduler sched;