diff --git a/core/CPUTopology.cpp b/core/CPUTopology.cpp index 4788af5f..22efddcc 100644 --- a/core/CPUTopology.cpp +++ b/core/CPUTopology.cpp @@ -259,11 +259,7 @@ olympia::CoreTopologySimple::CoreTopologySimple(){ }, { "cpu.core*.rob.ports.out_retire_flush", - "cpu.core*.flushmanager.ports.in_retire_flush" - }, - { - "cpu.core*.rob.ports.out_fetch_flush_redirect", - "cpu.core*.flushmanager.ports.in_fetch_flush_redirect" + "cpu.core*.flushmanager.ports.in_flush_request" }, { "cpu.core*.rob.ports.out_rob_retire_ack", @@ -274,27 +270,35 @@ olympia::CoreTopologySimple::CoreTopologySimple(){ "cpu.core*.rename.ports.in_rename_retire_ack" }, { - "cpu.core*.flushmanager.ports.out_retire_flush", + "cpu.core*.flushmanager.ports.out_flush_upper", "cpu.core*.dispatch.ports.in_reorder_flush" }, { - "cpu.core*.flushmanager.ports.out_retire_flush", + "cpu.core*.flushmanager.ports.out_flush_upper", + "cpu.core*.decode.ports.in_reorder_flush" + }, + { + "cpu.core*.flushmanager.ports.out_flush_lower", "cpu.core*.decode.ports.in_reorder_flush" }, { - "cpu.core*.flushmanager.ports.out_retire_flush", + "cpu.core*.flushmanager.ports.out_flush_upper", "cpu.core*.rename.ports.in_reorder_flush" }, { - "cpu.core*.flushmanager.ports.out_retire_flush", + "cpu.core*.flushmanager.ports.out_flush_upper", "cpu.core*.rob.ports.in_reorder_flush" }, { - "cpu.core*.flushmanager.ports.out_retire_flush", + "cpu.core*.flushmanager.ports.out_flush_upper", "cpu.core*.lsu.ports.in_reorder_flush" }, { - "cpu.core*.flushmanager.ports.out_fetch_flush_redirect", + "cpu.core*.flushmanager.ports.out_flush_upper", + "cpu.core*.fetch.ports.in_fetch_flush_redirect" + }, + { + "cpu.core*.flushmanager.ports.out_flush_lower", "cpu.core*.fetch.ports.in_fetch_flush_redirect" } }; @@ -344,8 +348,14 @@ void olympia::CoreTopologySimple::bindTree(sparta::RootTreeNode* root_node) // Bind flushing const std::string exe_flush_in = core_node + ".execute." + unit_name + ".ports.in_reorder_flush";; - const std::string flush_manager = flushmanager_ports + ".out_retire_flush"; + const std::string flush_manager = flushmanager_ports + ".out_flush_upper"; bind_ports(exe_flush_in, flush_manager); + + // Bind flush requests + const std::string exe_flush_out = + core_node + ".execute." + unit_name + ".ports.out_execute_flush";; + const std::string flush_manager_in = flushmanager_ports + ".in_flush_request"; + bind_ports(exe_flush_out, flush_manager_in); } } } diff --git a/core/Decode.cpp b/core/Decode.cpp index 1ddf0e98..62752ce5 100644 --- a/core/Decode.cpp +++ b/core/Decode.cpp @@ -95,10 +95,10 @@ namespace olympia uop_queue_outp_.send(insts); // Decrement internal Uop Queue credits - uop_queue_credits_ -= num_decode; + uop_queue_credits_ -= insts->size(); // Send credits back to Fetch to get more instructions - fetch_queue_credits_outp_.send(num_decode); + fetch_queue_credits_outp_.send(insts->size()); } // If we still have credits to send instructions as well as diff --git a/core/ExecutePipe.cpp b/core/ExecutePipe.cpp index 306c3fe8..6416d82f 100644 --- a/core/ExecutePipe.cpp +++ b/core/ExecutePipe.cpp @@ -27,6 +27,7 @@ namespace olympia execute_time_(p->execute_time), scheduler_size_(p->scheduler_size), in_order_issue_(p->in_order_issue), + enable_random_misprediction_(p->enable_random_misprediction), reg_file_(determineRegisterFile(node->getGroup())), collected_inst_(node, node->getName()) { @@ -43,7 +44,13 @@ namespace olympia // Complete should come before issue because it schedules issue with a 0 cycle delay // issue should always schedule complete with a non-zero delay (which corresponds to the // insturction latency) - complete_inst_ >> issue_inst_; + execute_inst_ >> issue_inst_; + + if (enable_random_misprediction_) + { + sparta_assert(node->getGroup() == "br", + "random branch misprediction can only be enabled on a branch unit"); + } ILOG("ExecutePipe construct: #" << node->getGroupIdx()); @@ -67,6 +74,14 @@ namespace olympia //////////////////////////////////////////////////////////////////////////////// // Callbacks void ExecutePipe::getInstsFromDispatch_(const InstPtr & ex_inst) + { + sparta_assert(ex_inst->getStatus() == Inst::Status::DISPATCHED, "Bad instruction status: " << ex_inst); + appendIssueQueue_(ex_inst); + handleOperandIssueCheck_(ex_inst); + } + + // Callback from Scoreboard to inform Operand Readiness + void ExecutePipe::handleOperandIssueCheck_(const InstPtr & ex_inst) { // FIXME: Now every source operand should be ready const auto & src_bits = ex_inst->getSrcRegisterBitMask(reg_file_); @@ -106,7 +121,7 @@ namespace olympia registerReadyCallback(src_bits, ex_inst->getUniqueID(), [this, ex_inst](const sparta::Scoreboard::RegisterBitMask&) { - this->getInstsFromDispatch_(ex_inst); + this->handleOperandIssueCheck_(ex_inst); }); ILOG("Instruction NOT ready: " << ex_inst << " Bits needed:" << sparta::printBitSet(src_bits)); } @@ -130,19 +145,19 @@ namespace olympia ++total_insts_issued_; // Mark the instruction complete later... - complete_inst_.preparePayload(ex_inst_ptr)->schedule(exe_time); + execute_inst_.preparePayload(ex_inst_ptr)->schedule(exe_time); // Mark the alu as busy unit_busy_ = true; - // Pop the insturction from the scheduler and send a credit back to dispatch + // Pop the insturction from the ready queue and send a credit back to dispatch + popIssueQueue_(ex_inst_ptr); ready_queue_.pop_front(); out_scheduler_credits_.send(1, 0); } // Called by the scheduler, scheduled by complete_inst_. - void ExecutePipe::completeInst_(const InstPtr & ex_inst) + void ExecutePipe::executeInst_(const InstPtr & ex_inst) { - ex_inst->setStatus(Inst::Status::COMPLETED); - ILOG("Completing inst: " << ex_inst); + ILOG("Executed inst: " << ex_inst); // set scoreboard if(SPARTA_EXPECT_FALSE(ex_inst->isTransfer())) @@ -170,6 +185,17 @@ namespace olympia scoreboard_views_[reg_file_]->setReady(dest_bits); } + // Testing mode to inject random branch misprediction to stress flushing mechanism + if (enable_random_misprediction_) + { + if (ex_inst->isBranch() && (std::rand() % 20) == 0) + { + ILOG("Randomly injecting a mispredicted branch: " << ex_inst); + FlushManager::FlushingCriteria criteria(FlushManager::FlushCause::MISPREDICTION, ex_inst); + out_execute_flush_.send(criteria); + } + } + // We're not busy anymore unit_busy_ = false; @@ -180,43 +206,108 @@ namespace olympia if (ready_queue_.size() > 0) { issue_inst_.schedule(sparta::Clock::Cycle(0)); } + + // Schedule completion + complete_inst_.preparePayload(ex_inst)->schedule(1); + + } + + // Called by the scheduler, scheduled by complete_inst_. + void ExecutePipe::completeInst_(const InstPtr & ex_inst) + { + ex_inst->setStatus(Inst::Status::COMPLETED); + ILOG("Completing inst: " << ex_inst); } void ExecutePipe::flushInst_(const FlushManager::FlushingCriteria & criteria) { ILOG("Got flush for criteria: " << criteria); - // Flush instructions in the ready queue - ReadyQueue::iterator it = ready_queue_.begin(); + // Flush instructions in the issue queue uint32_t credits_to_send = 0; - while(it != ready_queue_.end()) { - if((*it)->getUniqueID() >= uint64_t(criteria)) { - ready_queue_.erase(it++); + + auto issue_queue_iter = issue_queue_.begin(); + while (issue_queue_iter != issue_queue_.end()) + { + auto delete_iter = issue_queue_iter++; + + auto inst_ptr = *delete_iter; + + // Remove flushed instruction from issue queue and clear scoreboard callbacks + if (criteria.includedInFlush(inst_ptr)) + { + issue_queue_.erase(delete_iter); + + scoreboard_views_[reg_file_]->clearCallbacks(inst_ptr->getUniqueID()); + ++credits_to_send; - } - else { - ++it; + + ILOG("Flush Instruction ID: " << inst_ptr->getUniqueID() << " from issue queue"); } } + if(credits_to_send) { out_scheduler_credits_.send(credits_to_send, 0); + + ILOG("Flush " << credits_to_send << " instructions in issue queue!"); + } + + // Flush instructions in the ready queue + auto ready_queue_iter = ready_queue_.begin(); + while (ready_queue_iter != ready_queue_.end()) + { + auto delete_iter = ready_queue_iter++; + auto inst_ptr = *delete_iter; + if (criteria.includedInFlush(inst_ptr)) + { + ready_queue_.erase(delete_iter); + ILOG("Flush Instruction ID: " << inst_ptr->getUniqueID() << " from ready queue"); + } } // Cancel outstanding instructions awaiting completion and // instructions on their way to issue - auto cancel_critera = [criteria](const InstPtr & inst) -> bool { - if(inst->getUniqueID() >= uint64_t(criteria)) { - return true; - } - return false; + auto flush = [criteria](const InstPtr & inst) -> bool { + return criteria.includedInFlush(inst); }; - complete_inst_.cancelIf(cancel_critera); issue_inst_.cancel(); - - if(complete_inst_.getNumOutstandingEvents() == 0) { + complete_inst_.cancelIf(flush); + execute_inst_.cancelIf(flush); + if(execute_inst_.getNumOutstandingEvents() == 0) { unit_busy_ = false; collected_inst_.closeRecord(); + if (!ready_queue_.empty()) { + // issue non-flushed instructions + issue_inst_.schedule(sparta::Clock::Cycle(1)); + } + } + } + + //////////////////////////////////////////////////////////////////////////////// + // Regular Function/Subroutine Call + //////////////////////////////////////////////////////////////////////////////// + + // Append instruction into issue queue + void ExecutePipe::appendIssueQueue_(const InstPtr & inst_ptr) + { + sparta_assert(issue_queue_.size() < scheduler_size_, + "Appending issue queue causes overflows!"); + + issue_queue_.emplace_back(inst_ptr); + } + + // Pop completed instruction out of issue queue + void ExecutePipe::popIssueQueue_(const InstPtr & inst_ptr) + { + // Look for the instruction to be completed, and remove it from issue queue + for (auto iter = issue_queue_.begin(); iter != issue_queue_.end(); iter++) { + if (*iter == inst_ptr) { + issue_queue_.erase(iter); + return; + } } + sparta_assert(false, "Attempt to complete instruction no longer exiting in issue queue!"); } + } diff --git a/core/ExecutePipe.hpp b/core/ExecutePipe.hpp index 8dd1b529..dcc5ee2d 100644 --- a/core/ExecutePipe.hpp +++ b/core/ExecutePipe.hpp @@ -50,6 +50,8 @@ namespace olympia PARAMETER(uint32_t, execute_time, 1, "Time for execution") PARAMETER(uint32_t, scheduler_size, 8, "Scheduler queue size") PARAMETER(bool, in_order_issue, true, "Force in order issue") + HIDDEN_PARAMETER(bool, enable_random_misprediction, false, + "test mode to inject random branch mispredictions") }; /** @@ -71,10 +73,13 @@ namespace olympia sparta::DataOutPort out_scheduler_credits_{&unit_port_set_, "out_scheduler_credits"}; sparta::DataInPort in_reorder_flush_ {&unit_port_set_, "in_reorder_flush", sparta::SchedulingPhase::Flush, 1}; + sparta::DataOutPort out_execute_flush_ + {&unit_port_set_, "out_execute_flush"}; // Ready queue typedef std::list ReadyQueue; ReadyQueue ready_queue_; + ReadyQueue issue_queue_; // Scoreboards using ScoreboardViews = std::array, core_types::N_REGFILES>; @@ -87,14 +92,18 @@ namespace olympia const uint32_t execute_time_; const uint32_t scheduler_size_; const bool in_order_issue_; + const bool enable_random_misprediction_; const core_types::RegFile reg_file_; sparta::collection::IterableCollector> ready_queue_collector_ {getContainer(), "scheduler_queue", &ready_queue_, scheduler_size_}; - // Events used to issue and complete the instruction + // Events used to issue, execute and complete the instruction sparta::UniqueEvent<> issue_inst_{&unit_event_set_, getName() + "_issue_inst", CREATE_SPARTA_HANDLER(ExecutePipe, issueInst_)}; + sparta::PayloadEvent execute_inst_{ + &unit_event_set_, getName() + "_execute_inst", + CREATE_SPARTA_HANDLER_WITH_DATA(ExecutePipe, executeInst_, InstPtr)}; sparta::PayloadEvent complete_inst_{ &unit_event_set_, getName() + "_complete_inst", CREATE_SPARTA_HANDLER_WITH_DATA(ExecutePipe, completeInst_, InstPtr)}; @@ -119,11 +128,26 @@ namespace olympia void issueInst_(); void getInstsFromDispatch_(const InstPtr&); + // Callback from Scoreboard to inform Operand Readiness + void handleOperandIssueCheck_(const InstPtr &); + + // Write result to registers + void executeInst_(const InstPtr&); + // Used to complete the inst in the FPU void completeInst_(const InstPtr&); // Used to flush the ALU - void flushInst_(const FlushManager::FlushingCriteria & criteria); + void flushInst_(const FlushManager::FlushingCriteria &); + + //////////////////////////////////////////////////////////////////////////////// + // Regular Function/Subroutine Call + + // Append new instruction into issue queue + void appendIssueQueue_(const InstPtr &); + + // Pop completed instruction out of issue queue + void popIssueQueue_(const InstPtr &); // Friend class used in rename testing friend class ExecutePipeTester; diff --git a/core/Fetch.cpp b/core/Fetch.cpp index 3119f4b9..0629111f 100644 --- a/core/Fetch.cpp +++ b/core/Fetch.cpp @@ -27,13 +27,14 @@ namespace olympia in_fetch_queue_credits_. registerConsumerHandler(CREATE_SPARTA_HANDLER_WITH_DATA(Fetch, receiveFetchQueueCredits_, uint32_t)); + 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_))); // Schedule a single event to start reading from a trace file sparta::StartupEvent(node, CREATE_SPARTA_HANDLER(Fetch, initialize_)); - in_fetch_flush_redirect_.registerConsumerHandler(CREATE_SPARTA_HANDLER_WITH_DATA(Fetch, flushFetch_, uint64_t)); - } Fetch::~Fetch() {} @@ -105,16 +106,28 @@ namespace olympia fetch_inst_event_->schedule(sparta::Clock::Cycle(0)); } - // Called from Retire via in_fetch_flush_redirect_ port - void Fetch::flushFetch_(const uint64_t & new_addr) { - ILOG("Fetch: receive flush on new_addr=0x" - << std::hex << new_addr << std::dec); + // Called from FlushManager via in_fetch_flush_redirect_port + void Fetch::flushFetch_(const FlushManager::FlushingCriteria &criteria) + { + ILOG("Fetch: received flush " << criteria); + + auto flush_inst = criteria.getInstPtr(); + + // Rewind the tracefile + if (criteria.isInclusiveFlush()) + { + inst_generator_->reset(flush_inst, false); // Replay this instruction + } + else + { + inst_generator_->reset(flush_inst, true); // Skip to next instruction + } // Cancel all previously sent instructions on the outport out_fetch_queue_write_.cancel(); // No longer speculative - speculative_path_ = false; + // speculative_path_ = false; } } diff --git a/core/Fetch.hpp b/core/Fetch.hpp index 8836fc24..1538bb17 100644 --- a/core/Fetch.hpp +++ b/core/Fetch.hpp @@ -18,6 +18,7 @@ #include "CoreTypes.hpp" #include "InstGroup.hpp" +#include "FlushManager.hpp" namespace olympia { @@ -84,7 +85,7 @@ namespace olympia {&unit_port_set_, "in_fetch_queue_credits", sparta::SchedulingPhase::Tick, 0}; // Incoming flush from Retire w/ redirect - sparta::DataInPort in_fetch_flush_redirect_ + sparta::DataInPort in_fetch_flush_redirect_ {&unit_port_set_, "in_fetch_flush_redirect", sparta::SchedulingPhase::Flush, 1}; //////////////////////////////////////////////////////////////////////////////// @@ -121,8 +122,8 @@ namespace olympia // Read data from a trace void fetchInstruction_(); - // Receive flush from retire - void flushFetch_(const uint64_t & new_addr); + // Receive flush from FlushManager + void flushFetch_(const FlushManager::FlushingCriteria &); // Are we fetching a speculative path? bool speculative_path_ = false; diff --git a/core/FlushManager.hpp b/core/FlushManager.hpp index 39d7b004..a4ed855e 100644 --- a/core/FlushManager.hpp +++ b/core/FlushManager.hpp @@ -14,6 +14,10 @@ #include "sparta/simulation/Unit.hpp" #include "sparta/ports/DataPort.hpp" +#include "sparta/utils/LogUtils.hpp" +#include "sparta/utils/ValidValue.hpp" + +#include "Inst.hpp" namespace olympia { @@ -38,7 +42,66 @@ namespace olympia class FlushManager : public sparta::Unit { public: - typedef uint64_t FlushingCriteria; + + enum class FlushCause : std::uint16_t { + TRAP = 0, + __FIRST = TRAP, + MISPREDICTION, + TARGET_MISPREDICTION, + MISFETCH, + POST_SYNC, + UKNOWN, + __LAST + }; + + class FlushingCriteria + { + public: + FlushingCriteria(FlushCause cause, InstPtr inst_ptr) : + cause_(cause), + inst_ptr_(inst_ptr) {} + + FlushingCriteria() = default; + FlushingCriteria(const FlushingCriteria &rhs) = default; + FlushingCriteria &operator=(const FlushingCriteria &rhs) = default; + + FlushCause getCause() const { return cause_; } + const InstPtr & getInstPtr() const { return inst_ptr_; } + + bool isInclusiveFlush() const + { + static const std::map inclusive_flush_map = { + {FlushCause::TRAP, true}, + {FlushCause::MISFETCH, true}, + {FlushCause::MISPREDICTION, false}, + {FlushCause::TARGET_MISPREDICTION, false}, + {FlushCause::POST_SYNC, false} + }; + if(auto match = inclusive_flush_map.find(cause_); match != inclusive_flush_map.end()) { + return match->second; + } + sparta_assert(false, "Unknown flush cause: " << static_cast(cause_)); + return true; + } + + bool isLowerPipeFlush() const + { + return cause_ == FlushCause::MISFETCH; + } + + bool includedInFlush(const InstPtr& other) const + { + return isInclusiveFlush() ? + inst_ptr_->getUniqueID() <= other->getUniqueID() : + inst_ptr_->getUniqueID() < other->getUniqueID(); + } + + private: + FlushCause cause_ = FlushCause::UKNOWN; + InstPtr inst_ptr_; + }; + + static constexpr char name[] = "flushmanager"; class FlushManagerParameters : public sparta::ParameterSet @@ -57,42 +120,99 @@ namespace olympia */ FlushManager(sparta::TreeNode *rc, const FlushManagerParameters * params) : Unit(rc, name), - out_retire_flush_(getPortSet(), "out_retire_flush", false), - in_retire_flush_(getPortSet(), "in_retire_flush", 0), - out_fetch_flush_redirect_(getPortSet(), "out_fetch_flush_redirect", false), - in_fetch_flush_redirect_(getPortSet(), "in_fetch_flush_redirect", 0) + in_flush_request_(getPortSet(), "in_flush_request", 0), + out_flush_lower_(getPortSet(), "out_flush_lower", false), + out_flush_upper_(getPortSet(), "out_flush_upper", false) { (void)params; - in_retire_flush_. + + in_flush_request_. registerConsumerHandler(CREATE_SPARTA_HANDLER_WITH_DATA(FlushManager, - forwardRetireFlush_, + receiveFlush_, FlushingCriteria)); - in_fetch_flush_redirect_. - registerConsumerHandler(CREATE_SPARTA_HANDLER_WITH_DATA(FlushManager, - forwardFetchRedirectFlush_, - uint64_t)); + + in_flush_request_.registerConsumerEvent(ev_flush_); } private: // Flushing criteria - sparta::DataOutPort out_retire_flush_; - sparta::DataInPort in_retire_flush_; + sparta::DataInPort in_flush_request_; + sparta::DataOutPort out_flush_lower_; + sparta::DataOutPort out_flush_upper_; - // Flush redirect for Fetch - sparta::DataOutPort out_fetch_flush_redirect_; - sparta::DataInPort in_fetch_flush_redirect_; + sparta::UniqueEvent<> ev_flush_ {&unit_event_set_, + "flush_event", + CREATE_SPARTA_HANDLER(FlushManager, forwardFlush_)}; - // Internal method used to forward the flush to the attached - // listeners - void forwardRetireFlush_(const FlushingCriteria & flush_data) { - out_retire_flush_.send(flush_data); + // Hold oldest incoming flush request for forwarding + sparta::utils::ValidValue pending_flush_; + + // Arbitrates and forwards the flush request from the input flush ports, to the output ports + void forwardFlush_() + { + sparta_assert(pending_flush_.isValid(), "no flush to forward onwards?"); + auto flush_data = pending_flush_.getValue(); + if (flush_data.isLowerPipeFlush()) + { + ILOG("instigating lower pipeline flush for: " << flush_data); + out_flush_lower_.send(flush_data); + } + else + { + ILOG("instigating upper pipeline flush for: " << flush_data); + out_flush_upper_.send(flush_data); + } + pending_flush_.clearValid(); } - // Internal method used to forward the fetch redirect - void forwardFetchRedirectFlush_(const uint64_t & flush_data) { - out_fetch_flush_redirect_.send(flush_data); + void receiveFlush_(const FlushingCriteria & flush_data) + { + ev_flush_.schedule(); + + // Capture the oldest flush request only + if (pending_flush_.isValid()) + { + auto pending = pending_flush_.getValue(); + if (pending.includedInFlush(flush_data.getInstPtr())) + { + return; + } + } + pending_flush_ = flush_data; } + }; -} + + inline std::ostream & operator<<(std::ostream & os, const FlushManager::FlushCause & cause) { + switch(cause) { + case FlushManager::FlushCause::TRAP: + os << "TRAP"; + break; + case FlushManager::FlushCause::MISPREDICTION: + os << "MISPREDICTION"; + break; + case FlushManager::FlushCause::TARGET_MISPREDICTION: + os << "TARGET_MISPREDICTION"; + break; + case FlushManager::FlushCause::MISFETCH: + os << "MISFETCH"; + break; + case FlushManager::FlushCause::POST_SYNC: + os << "POST_SYNC"; + break; + case FlushManager::FlushCause::UKNOWN: + os << "UKNOWN"; + break; + case FlushManager::FlushCause::__LAST: + throw sparta::SpartaException("__LAST cannot be a valid enum state."); + } + return os; + } + + inline std::ostream & operator<<(std::ostream & os, const FlushManager::FlushingCriteria & criteria) { + os << criteria.getInstPtr() << " " << criteria.getCause(); + return os; + } +} diff --git a/core/Inst.cpp b/core/Inst.cpp index 96968e03..4ab93ee5 100644 --- a/core/Inst.cpp +++ b/core/Inst.cpp @@ -5,6 +5,27 @@ namespace olympia { + bool isCallInstruction(const mavis::OpcodeInfo::PtrType& opcode_info) + { + if (opcode_info->isInstType(mavis::OpcodeInfo::InstructionTypes::JAL) || + opcode_info->isInstType(mavis::OpcodeInfo::InstructionTypes::JALR)) { + const int dest = opcode_info->getDestOpInfo().getFieldValue(mavis::InstMetaData::OperandFieldID::RD); + return miscutils::isOneOf(dest, 1, 5); + } + return false; + } + + bool isReturnInstruction(const mavis::OpcodeInfo::PtrType& opcode_info) + { + if (opcode_info->isInstType(mavis::OpcodeInfo::InstructionTypes::JALR)) { + const int dest = opcode_info->getDestOpInfo().getFieldValue(mavis::InstMetaData::OperandFieldID::RD); + const int src = opcode_info->getSourceOpInfo().getFieldValue(mavis::InstMetaData::OperandFieldID::RS1); + if (dest != src) { + return miscutils::isOneOf(src, 1, 5); + } + } + return false; + } /*! * \brief Construct an Instruction * \param opcode_info Mavis Opcode information @@ -23,7 +44,10 @@ namespace olympia is_transfer_(miscutils::isOneOf(inst_arch_info_->getTargetPipe(), InstArchInfo::TargetPipe::I2F, InstArchInfo::TargetPipe::F2I)), - + is_branch_(opcode_info_->isInstType(mavis::OpcodeInfo::InstructionTypes::BRANCH)), + is_condbranch_(opcode_info_->isInstType(mavis::OpcodeInfo::InstructionTypes::CONDITIONAL)), + is_call_(isCallInstruction(opcode_info)), + is_return_(isReturnInstruction(opcode_info)), status_state_(Status::FETCHED) { sparta_assert(inst_arch_info_ != nullptr, diff --git a/core/Inst.hpp b/core/Inst.hpp index d18492cf..1d2bce30 100644 --- a/core/Inst.hpp +++ b/core/Inst.hpp @@ -11,6 +11,8 @@ #include "sparta/utils/SpartaSharedPointerAllocator.hpp" #include "mavis/OpcodeInfo.h" +#include "stf-inc/stf_inst_reader.hpp" + #include "InstArchInfo.hpp" #include "CoreTypes.hpp" #include "MiscUtils.hpp" @@ -18,6 +20,7 @@ #include #include #include +#include namespace olympia { @@ -47,6 +50,9 @@ namespace olympia void setOriginalDestination(const Reg & destination){ original_dest_ = destination; } + void setDestination(const Reg & destination){ + dest_ = destination; + } void setDataReg(const Reg & data_reg){ data_reg_ = data_reg; } @@ -59,11 +65,15 @@ namespace olympia const Reg & getOriginalDestination() const { return original_dest_; } + const Reg & getDestination() const { + return dest_; + } const Reg & getDataReg() const { return data_reg_; } private: Reg original_dest_; + Reg dest_; RegList src_; Reg data_reg_; }; @@ -85,6 +95,7 @@ namespace olympia SCHEDULED, COMPLETED, RETIRED, + FLUSHED, __LAST }; @@ -113,6 +124,10 @@ namespace olympia return getStatus() == olympia::Inst::Status::COMPLETED; } + bool getFlushedStatus() const { + return getStatus() == olympia::Inst::Status::FLUSHED; + } + void setStatus(Status status) { sparta_assert(status_state_ != status, "Status being set twice to the same value: " @@ -143,6 +158,12 @@ namespace olympia } bool isMarkedOldest() const { return is_oldest_; } + // Rewind iterator used for going back in program simulation after flushes + template + void setRewindIterator(T iter) { rewind_iter_ = iter; } + template + T getRewindIterator() const { return std::get(rewind_iter_); } + // 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) @@ -164,6 +185,9 @@ namespace olympia void setTargetVAddr(sparta::memory::addr_t target_vaddr) { target_vaddr_ = target_vaddr; } sparta::memory::addr_t getTargetVAddr() const { return target_vaddr_; } + // Branch instruction was taken (always set for JAL/JALR) + void setTakenBranch(bool taken) { is_taken_branch_ = taken; } + // TBD -- add branch prediction void setSpeculative(bool spec) { is_speculative_ = spec; } @@ -185,6 +209,11 @@ namespace olympia uint64_t getRAdr() const { return target_vaddr_ | 0x8000000; } // faked bool isSpeculative() const { return is_speculative_; } bool isTransfer() const { return is_transfer_; } + bool isTakenBranch() const { return is_taken_branch_; } + bool isBranch() const { return is_branch_; } + bool isCondBranch() const { return is_condbranch_; } + bool isCall() const { return is_call_; } + bool isReturn() const { return is_return_; } // Rename information core_types::RegisterBitMask & getSrcRegisterBitMask(const core_types::RegFile rf) { @@ -223,9 +252,18 @@ namespace olympia bool is_speculative_ = false; // Is this instruction soon to be flushed? const bool is_store_; const bool is_transfer_; // Is this a transfer instruction (F2I/I2F) + const bool is_branch_; + const bool is_condbranch_; + const bool is_call_; + const bool is_return_; + bool is_taken_branch_ = false; sparta::Scheduleable * ev_retire_ = nullptr; Status status_state_; + using JSONIterator = uint64_t; + using RewindIterator = std::variant; + RewindIterator rewind_iter_; + // Rename information using RegisterBitMaskArray = std::array; RegisterBitMaskArray src_reg_bit_masks_; @@ -260,6 +298,9 @@ namespace olympia case Inst::Status::RETIRED: os << "RETIRED"; break; + case Inst::Status::FLUSHED: + os << "FLUSHED"; + break; case Inst::Status::__LAST: throw sparta::SpartaException("__LAST cannot be a valid enum state."); } diff --git a/core/InstGenerator.cpp b/core/InstGenerator.cpp index 1c90f4da..c68c9e15 100644 --- a/core/InstGenerator.cpp +++ b/core/InstGenerator.cpp @@ -49,7 +49,18 @@ namespace olympia } bool JSONInstGenerator::isDone() const { - return (curr_inst_index_ == n_insts_); + return (curr_inst_index_ >= n_insts_); + } + + void JSONInstGenerator::reset(const InstPtr & inst_ptr, const bool skip = false) + { + curr_inst_index_ = inst_ptr->getRewindIterator(); + program_id_ = inst_ptr->getProgramID(); + if (skip) + { + ++curr_inst_index_; + ++program_id_; + } } InstPtr JSONInstGenerator::getNextInst(const sparta::Clock * clk) @@ -103,9 +114,15 @@ namespace olympia inst->setTargetVAddr(vaddr); } - ++curr_inst_index_; + if (jinst.find("taken") != jinst.end()) { + const bool taken = jinst["taken"].get(); + inst->setTakenBranch(taken); + } + + inst->setRewindIterator(curr_inst_index_); inst->setUniqueID(++unique_id_); - inst->setProgramID(unique_id_); + inst->setProgramID(program_id_++); + ++curr_inst_index_; return inst; } @@ -147,6 +164,18 @@ namespace olympia bool TraceInstGenerator::isDone() const { return next_it_ == reader_->end(); } + + void TraceInstGenerator::reset(const InstPtr & inst_ptr, const bool skip = false) + { + next_it_ = inst_ptr->getRewindIterator(); + program_id_ = inst_ptr->getProgramID(); + if (skip) + { + ++next_it_; + ++program_id_; + } + } + InstPtr TraceInstGenerator::getNextInst(const sparta::Clock * clk) { if(SPARTA_EXPECT_FALSE(isDone())) { @@ -159,7 +188,8 @@ namespace olympia InstPtr inst = mavis_facade_->makeInst(opcode, clk); inst->setPC(next_it_->pc()); inst->setUniqueID(++unique_id_); - inst->setProgramID(unique_id_); + inst->setProgramID(program_id_++); + inst->setRewindIterator(next_it_); if (const auto& mem_accesses = next_it_->getMemoryAccesses(); !mem_accesses.empty()) { using VectorAddrType = std::vector; @@ -173,6 +203,11 @@ namespace olympia //For misaligns, more than 1 address is provided //inst->setVAddrVector(std::move(addrs)); } + if (next_it_->isBranch()) + { + inst->setTakenBranch(next_it_->isTakenBranch()); + inst->setTargetVAddr(next_it_->branchTarget()); + } ++next_it_; return inst; } diff --git a/core/InstGenerator.hpp b/core/InstGenerator.hpp index e6988fe7..f4c21f9e 100644 --- a/core/InstGenerator.hpp +++ b/core/InstGenerator.hpp @@ -40,10 +40,12 @@ namespace olympia const std::string & filename, const bool skip_nonuser_mode); virtual bool isDone() const = 0; + virtual void reset(const InstPtr &, const bool) = 0; protected: MavisType * mavis_facade_ = nullptr; uint64_t unique_id_ = 0; + uint64_t program_id_ = 1; }; // Generates instructions from a JSON file @@ -55,6 +57,9 @@ namespace olympia InstPtr getNextInst(const sparta::Clock * clk) override final; bool isDone() const override final; + void reset(const InstPtr &, const bool) override final; + + private: std::unique_ptr jobj_; uint64_t curr_inst_index_ = 0; @@ -75,6 +80,7 @@ namespace olympia InstPtr getNextInst(const sparta::Clock * clk) override final; bool isDone() const override final; + void reset(const InstPtr &, const bool) override final; private: std::unique_ptr reader_; diff --git a/core/LSU.cpp b/core/LSU.cpp index ee5172d0..a97048ee 100644 --- a/core/LSU.cpp +++ b/core/LSU.cpp @@ -43,10 +43,10 @@ namespace olympia "Cache read stage should atleast be one cycle"); sparta_assert(p->cache_lookup_stage_length > 0, "Cache lookup stage should atleast be one cycle"); + // Pipeline collection config ldst_pipeline_.enableCollection(node); ldst_inst_queue_.enableCollection(node); - replay_buffer_.enableCollection(node); // Startup handler for sending initial credits @@ -94,15 +94,16 @@ namespace olympia ldst_pipeline_.registerHandlerAtStage(cache_lookup_stage_, CREATE_SPARTA_HANDLER(LSU, handleCacheLookupReq_)); - node->getParent()->registerForNotification( - this, "rob_stopped_notif_channel", false /* ROB maybe not be constructed yet */); - ldst_pipeline_.registerHandlerAtStage(cache_read_stage_, CREATE_SPARTA_HANDLER(LSU, handleCacheRead_)); ldst_pipeline_.registerHandlerAtStage(complete_stage_, CREATE_SPARTA_HANDLER(LSU, completeInst_)); + // 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 */); + uev_append_ready_ >> uev_issue_inst_; // NOTE: // To resolve the race condition when: @@ -385,11 +386,12 @@ namespace olympia { ILOG("MMU rehandling event is scheduled! " << memory_access_info_ptr); const auto & inst_ptr = memory_access_info_ptr->getInstPtr(); - if (mmu_pending_inst_flushed) + + // Update issue priority & Schedule an instruction (re-)issue event + updateIssuePriorityAfterTLBReload_(memory_access_info_ptr); + + if (inst_ptr->getFlushedStatus()) { - mmu_pending_inst_flushed = false; - // Update issue priority & Schedule an instruction (re-)issue event - updateIssuePriorityAfterTLBReload_(memory_access_info_ptr, true); if (isReadyToIssueInsts_()) { uev_issue_inst_.schedule(sparta::Clock::Cycle(0)); @@ -397,7 +399,6 @@ namespace olympia return; } - updateIssuePriorityAfterTLBReload_(memory_access_info_ptr); removeInstFromReplayQueue_(inst_ptr); if (isReadyToIssueInsts_()) @@ -514,15 +515,13 @@ namespace olympia void LSU::handleCacheReadyReq_(const MemoryAccessInfoPtr & memory_access_info_ptr) { auto inst_ptr = memory_access_info_ptr->getInstPtr(); - if (cache_pending_inst_flushed_) + if (inst_ptr->getFlushedStatus()) { - cache_pending_inst_flushed_ = false; ILOG("BIU Ack for a flushed cache miss is received!"); // Schedule an instruction (re-)issue event // Note: some younger load/store instruction(s) might have been blocked by // this outstanding miss - updateIssuePriorityAfterCacheReload_(memory_access_info_ptr, true); if (isReadyToIssueInsts_()) { uev_issue_inst_.schedule(sparta::Clock::Cycle(0)); @@ -740,29 +739,22 @@ namespace olympia { ILOG("Start Flushing!"); - // Flush criteria setup - auto flush = [criteria](const uint64_t & id) -> bool - { return id >= static_cast(criteria); }; - lsu_flushes_++; // Flush load/store pipeline entry - flushLSPipeline_(flush); - - // Mark flushed flag for unfinished speculative MMU access - if (mmu_busy_) - { - mmu_pending_inst_flushed = true; - } - - // Mark flushed flag for unfinished speculative cache access - if (cache_busy_) - { - cache_pending_inst_flushed_ = true; - } + flushLSPipeline_(criteria); // Flush instruction issue queue - flushIssueQueue_(flush); + flushIssueQueue_(criteria); + flushReplayBuffer_(criteria); + flushReadyQueue_(criteria); + + // Cancel replay events + auto flush = [&criteria](const LoadStoreInstInfoPtr & ldst_info_ptr) -> bool { + return criteria.includedInFlush(ldst_info_ptr->getInstPtr()); + }; + uev_append_ready_.cancelIf(flush); + uev_replay_ready_.cancelIf(flush); // Cancel issue event already scheduled if no ready-to-issue inst left after flush if (!isReadyToIssueInsts_()) @@ -1127,8 +1119,7 @@ namespace olympia } // Update issue priority after tlb reload - void LSU::updateIssuePriorityAfterTLBReload_(const MemoryAccessInfoPtr & mem_access_info_ptr, - const bool is_flushed_inst) + void LSU::updateIssuePriorityAfterTLBReload_(const MemoryAccessInfoPtr & mem_access_info_ptr) { const InstPtr & inst_ptr = mem_access_info_ptr->getInstPtr(); bool is_found = false; @@ -1168,15 +1159,20 @@ namespace olympia } } - sparta_assert(is_flushed_inst || is_found, + sparta_assert(inst_ptr->getFlushedStatus() || is_found, "Attempt to rehandle TLB lookup for instruction not yet in the issue queue! " << inst_ptr); } // Update issue priority after cache reload - void LSU::updateIssuePriorityAfterCacheReload_(const MemoryAccessInfoPtr & mem_access_info_ptr, - const bool is_flushed_inst) + void LSU::updateIssuePriorityAfterCacheReload_(const MemoryAccessInfoPtr & mem_access_info_ptr) { + const InstPtr & inst_ptr = mem_access_info_ptr->getInstPtr(); + + sparta_assert( + inst_ptr->getFlushedStatus() == false, + "Attempt to rehandle cache lookup for flushed instruction!"); + const LoadStoreInstIterator & iter = mem_access_info_ptr->getIssueQueueIterator(); sparta_assert( iter.isValid(), @@ -1235,27 +1231,32 @@ namespace olympia } // Flush instruction issue queue - template void LSU::flushIssueQueue_(const Comp & flush) + void LSU::flushIssueQueue_(const FlushCriteria & criteria) { uint32_t credits_to_send = 0; auto iter = ldst_inst_queue_.begin(); - while (iter != ldst_inst_queue_.end()) - { - auto inst_id = (*iter)->getInstPtr()->getUniqueID(); + while (iter != ldst_inst_queue_.end()) { + auto inst_ptr = (*iter)->getInstPtr(); auto delete_iter = iter++; - if (flush(inst_id)) - { + if (criteria.includedInFlush(inst_ptr)) { ldst_inst_queue_.erase(delete_iter); + // Clear any scoreboard callback + std::vector reg_files = {core_types::RF_INTEGER, core_types::RF_FLOAT}; + for(const auto rf : reg_files) + { + scoreboard_views_[rf]->clearCallbacks(inst_ptr->getUniqueID()); + } + // NOTE: // We cannot increment iter after erase because it's already invalidated by then ++credits_to_send; - ILOG("Flush Instruction ID: " << inst_id); + ILOG("Flush Instruction ID: " << inst_ptr->getUniqueID()); } } @@ -1268,23 +1269,51 @@ namespace olympia } // Flush load/store pipe - template void LSU::flushLSPipeline_(const Comp & flush) + void LSU::flushLSPipeline_(const FlushCriteria & criteria) { uint32_t stage_id = 0; - for (auto iter = ldst_pipeline_.begin(); iter != ldst_pipeline_.end(); iter++, stage_id++) - { - // If the pipe stage is already invalid, no need to flush - if (!iter.isValid()) - { + for (auto iter = ldst_pipeline_.begin(); iter != ldst_pipeline_.end(); iter++, stage_id++) { + // If the pipe stage is already invalid, no need to criteria + if (!iter.isValid()) { continue; } - auto inst_id = (*iter)->getInstPtr()->getUniqueID(); - if (flush(inst_id)) - { + auto inst_ptr = (*iter)->getInstPtr(); + if (criteria.includedInFlush(inst_ptr)) { ldst_pipeline_.flushStage(iter); - ILOG("Flush Pipeline Stage[" << stage_id << "], Instruction ID: " << inst_id); + ILOG("Flush Pipeline Stage[" << stage_id + << "], Instruction ID: " << inst_ptr->getUniqueID()); + } + } + } + + void LSU::flushReadyQueue_(const FlushCriteria & criteria) + { + auto iter = ready_queue_.begin(); + while (iter != ready_queue_.end()) { + auto inst_ptr = (*iter)->getInstPtr(); + + auto delete_iter = iter++; + + if (criteria.includedInFlush(inst_ptr)) { + ready_queue_.erase(delete_iter); + ILOG("Flushing from ready queue - Instruction ID: " << inst_ptr->getUniqueID()); + } + } + } + + void LSU::flushReplayBuffer_(const FlushCriteria & criteria) + { + auto iter = replay_buffer_.begin(); + while (iter != replay_buffer_.end()) { + auto inst_ptr = (*iter)->getInstPtr(); + + auto delete_iter = iter++; + + if (criteria.includedInFlush(inst_ptr)) { + replay_buffer_.erase(delete_iter); + ILOG("Flushing from replay buffer - Instruction ID: " << inst_ptr->getUniqueID()); } } } diff --git a/core/LSU.hpp b/core/LSU.hpp index c68a2196..0896169c 100644 --- a/core/LSU.hpp +++ b/core/LSU.hpp @@ -140,11 +140,9 @@ namespace olympia sparta::PriorityQueue ready_queue_; // MMU unit bool mmu_busy_ = false; - bool mmu_pending_inst_flushed = false; // L1 Data Cache bool cache_busy_ = false; - bool cache_pending_inst_flushed_ = false; sparta::collection::Collectable cache_busy_collectable_{getContainer(), "dcache_busy", &cache_busy_}; @@ -297,19 +295,25 @@ namespace olympia void updateIssuePriorityAfterNewDispatch_(const InstPtr &); // Update issue priority after TLB reload - void updateIssuePriorityAfterTLBReload_(const MemoryAccessInfoPtr &, const bool = false); + void updateIssuePriorityAfterTLBReload_(const MemoryAccessInfoPtr &); // Update issue priority after cache reload - void updateIssuePriorityAfterCacheReload_(const MemoryAccessInfoPtr &, const bool = false); + void updateIssuePriorityAfterCacheReload_(const MemoryAccessInfoPtr &); // Update issue priority after store instruction retires void updateIssuePriorityAfterStoreInstRetire_(const InstPtr &); // Flush instruction issue queue - template void flushIssueQueue_(const Comp &); + void flushIssueQueue_(const FlushCriteria &); // Flush load/store pipeline - template void flushLSPipeline_(const Comp &); + void flushLSPipeline_(const FlushCriteria &); + + // Flush Ready Queue + void flushReadyQueue_(const FlushCriteria &); + + // Flush Replay Buffer + void flushReplayBuffer_(const FlushCriteria &); // Counters sparta::Counter lsu_insts_dispatched_{getStatisticSet(), "lsu_insts_dispatched", diff --git a/core/ROB.cpp b/core/ROB.cpp index 76e33bf0..25a53169 100644 --- a/core/ROB.cpp +++ b/core/ROB.cpp @@ -92,11 +92,27 @@ namespace olympia ev_retire_.schedule(sparta::Clock::Cycle(0)); } - void ROB::handleFlush_(const FlushManager::FlushingCriteria &) + void ROB::handleFlush_(const FlushManager::FlushingCriteria & criteria) { + uint32_t credits_to_send = 0; + // Clean up internals and send new credit count - out_reorder_buffer_credits_.send(reorder_buffer_.size()); - reorder_buffer_.clear(); + while (reorder_buffer_.size()) + { + auto youngest_inst = reorder_buffer_.back(); + if (criteria.includedInFlush(youngest_inst)) + { + ILOG("flushing " << youngest_inst); + youngest_inst->setStatus(Inst::Status::FLUSHED); + reorder_buffer_.pop_back(); + ++credits_to_send; + } + else + { + break; + } + } + out_reorder_buffer_credits_.send(credits_to_send); } void ROB::retireInstructions_() @@ -129,6 +145,14 @@ namespace olympia ILOG("retiring " << ex_inst); + // Use the program ID to verify that the program order has been maintained. + sparta_assert(ex_inst.getProgramID() == expected_program_id_, + "Unexpected program ID when retiring instruction" + << "(suggests wrong program order)" + << " expected: " << expected_program_id_ + << " received: " << ex_inst.getProgramID()); + ++expected_program_id_; + if(SPARTA_EXPECT_FALSE((num_retired_ % retire_heartbeat_) == 0)) { std::cout << "olympia: Retired " << num_retired_.get() << " instructions in " << getClock()->currentCycle() @@ -149,11 +173,10 @@ namespace olympia if(SPARTA_EXPECT_FALSE(ex_inst.getUnit() == InstArchInfo::TargetUnit::ROB)) { ILOG("Instigating flush... " << ex_inst); - // Signal flush to the system - out_retire_flush_.send(ex_inst.getUniqueID()); - // Redirect fetch - out_fetch_flush_redirect_.send(ex_inst.getTargetVAddr() + 4); + FlushManager::FlushingCriteria criteria(FlushManager::FlushCause::POST_SYNC, ex_inst_ptr); + out_retire_flush_.send(criteria); + ++num_flushes_; break; diff --git a/core/ROB.hpp b/core/ROB.hpp index 4458ec42..8d11daff 100644 --- a/core/ROB.hpp +++ b/core/ROB.hpp @@ -91,16 +91,18 @@ namespace olympia // buffer, the machine probably has a lock up bool rob_stopped_simulation_{false}; + // Track a program ID to ensure the trace stream matches + // at retirement. + uint64_t expected_program_id_ = 1; + // Ports used by the ROB sparta::DataInPort in_reorder_buffer_write_{&unit_port_set_, "in_reorder_buffer_write", 1}; sparta::DataOutPort out_reorder_buffer_credits_{&unit_port_set_, "out_reorder_buffer_credits"}; sparta::DataInPort in_oldest_completed_ {&unit_port_set_, "in_reorder_oldest_completed"}; sparta::DataOutPort out_retire_flush_ {&unit_port_set_, "out_retire_flush"}; - sparta::DataOutPort out_fetch_flush_redirect_ {&unit_port_set_, "out_fetch_flush_redirect"}; - // UPDATE: - sparta::DataOutPort out_rob_retire_ack_ {&unit_port_set_, "out_rob_retire_ack"}; - sparta::DataOutPort out_rob_retire_ack_rename_ {&unit_port_set_, "out_rob_retire_ack_rename"}; + sparta::DataOutPort out_rob_retire_ack_ {&unit_port_set_, "out_rob_retire_ack"}; + sparta::DataOutPort out_rob_retire_ack_rename_ {&unit_port_set_, "out_rob_retire_ack_rename"}; // For flush sparta::DataInPort in_reorder_flush_ diff --git a/core/Rename.cpp b/core/Rename.cpp index f5f81ff0..4dcc2c8d 100644 --- a/core/Rename.cpp +++ b/core/Rename.cpp @@ -173,6 +173,17 @@ namespace olympia freelist_[src.rf].push(src.val); } } + // Instruction queue bookkeeping + if (SPARTA_EXPECT_TRUE(!inst_queue_.empty())) + { + const auto & oldest_inst = inst_queue_.front(); + sparta_assert(oldest_inst->getUniqueID() == inst_ptr->getUniqueID(), "ROB and rename inst_queue out of sync"); + inst_queue_.pop_front(); + } + else + { + sparta_assert(false, "ROB and rename inst_queue out of sync"); + } if(credits_dispatch_ > 0 && (uop_queue_.size() > 0)){ ev_schedule_rename_.schedule(); } @@ -184,6 +195,68 @@ namespace olympia void Rename::handleFlush_(const FlushManager::FlushingCriteria & criteria) { ILOG("Got a flush call for " << criteria); + // Restore the rename map, reference counters and freelist by walking through the inst_queue + while (inst_queue_.size()) + { + const auto & inst_ptr = inst_queue_.back(); + if (!criteria.includedInFlush(inst_ptr)) + { + break; + } + else + { + auto const & dests = inst_ptr->getDestOpInfoList(); + for (const auto & dest : dests) + { + // restore rename table following a flush + const auto rf = olympia::coreutils::determineRegisterFile(dest); + const auto num = dest.field_value; + const bool is_x0 = (num == 0 && rf == core_types::RF_INTEGER); + if (!is_x0) + { + auto const & original_dest = inst_ptr->getRenameData().getOriginalDestination(); + map_table_[rf][num] = original_dest.val; + + // free renamed PRF mapping when reference counter reaches zero + auto const & renamed_dest = inst_ptr->getRenameData().getDestination(); + --reference_counter_[renamed_dest.rf][renamed_dest.val]; + if(reference_counter_[renamed_dest.rf][renamed_dest.val] <= 0){ + freelist_[renamed_dest.rf].push(renamed_dest.val); + } + } + } + + const auto & srcs = inst_ptr->getRenameData().getSourceList(); + // decrement reference to data register + if(inst_ptr->isLoadStoreInst()){ + const auto & data_reg = inst_ptr->getRenameData().getDataReg(); + if(data_reg.field_id == mavis::InstMetaData::OperandFieldID::RS2 && data_reg.is_x0 != true){ + --reference_counter_[data_reg.rf][data_reg.val]; + if(reference_counter_[data_reg.rf][data_reg.val] <= 0){ + // freeing data register value, because it's not in the source list, so won't get caught below + freelist_[data_reg.rf].push(data_reg.val); + } + } + } + // freeing references to PRF + for(const auto & src: srcs){ + --reference_counter_[src.rf][src.val]; + if(reference_counter_[src.rf][src.val] <= 0){ + // freeing a register in the case where it still has references and has already been retired + // we wait until the last reference is retired to then free the prf + // any "valid" PRF that is the true mapping of an ARF will have a reference_counter of at least 1, + // and thus shouldn't be retired + freelist_[src.rf].push(src.val); + } + } + inst_queue_.pop_back(); + } + } + + current_stall_ = NO_DECODE_INSTS; + + // Clean up buffers + uop_queue_regcount_data_.clear(); out_uop_queue_credits_.send(uop_queue_.size()); uop_queue_.clear(); } @@ -349,19 +422,20 @@ namespace olympia << " for '" << rf << "' scoreboard"); } } - + for(const auto & dest : dests) { const auto rf = olympia::coreutils::determineRegisterFile(dest); const auto num = dest.field_value; - if (num == 0 && rf == core_types::RF_INTEGER) { - continue; + if (num == 0 && rf == core_types::RF_INTEGER) { + continue; } else{ auto & bitmask = renaming_inst->getDestRegisterBitMask(rf); const uint32_t prf = freelist_[rf].front(); freelist_[rf].pop(); renaming_inst->getRenameData().setOriginalDestination({map_table_[rf][num], rf, dest.field_id}); + renaming_inst->getRenameData().setDestination({prf, rf, dest.field_id}); map_table_[rf][num] = prf; // we increase reference_counter_ for destinations to mark them as "valid", // so the PRF in the reference_counter_ should have a value of 1 @@ -378,7 +452,8 @@ namespace olympia } } // Remove it from uop queue - insts->emplace_back(uop_queue_.read(0)); + insts->emplace_back(renaming_inst); + inst_queue_.emplace_back(renaming_inst); uop_queue_.pop(); } diff --git a/core/Rename.hpp b/core/Rename.hpp index ea7d234b..5f31eed3 100644 --- a/core/Rename.hpp +++ b/core/Rename.hpp @@ -103,6 +103,10 @@ namespace olympia }; std::deque uop_queue_regcount_data_; + // Used to track inflight instructions for the purpose of recovering + // the rename data structures + std::deque inst_queue_; + /////////////////////////////////////////////////////////////////////// // Stall counters @@ -156,7 +160,7 @@ namespace olympia // Get Retired Instructions void getAckFromROB_(const InstPtr &); - + // Friend class used in rename testing friend class RenameTester; diff --git a/test/sim/CMakeLists.txt b/test/sim/CMakeLists.txt index a458eb9d..d17dd2d3 100644 --- a/test/sim/CMakeLists.txt +++ b/test/sim/CMakeLists.txt @@ -64,6 +64,15 @@ foreach(ARCH_FULL_PATH ${TEST_YAML}) --arch ${ARCH_NAME}) endforeach() +## Test branch misprediction flushes +foreach(ARCH_FULL_PATH ${TEST_YAML}) + get_filename_component(ARCH_FILE_NAME ${ARCH_FULL_PATH} NAME) + string(REGEX REPLACE ".yaml" "" ARCH_NAME ${ARCH_FILE_NAME}) + sparta_named_test(olympia_arch_${ARCH_NAME}_random_branch_misprediction_test olympia + -i500K --workload traces/dhry_riscv.zstf + --arch ${ARCH_NAME} + -p top.cpu.core0.execute.br*.params.enable_random_misprediction 1) +endforeach() set(test_params_list) list(APPEND test_params_list "top.cpu.core0.lsu.params.mmu_lookup_stage_length 3")