diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cpu/o3/commit.hh | 3 | ||||
-rw-r--r-- | src/cpu/o3/commit_impl.hh | 23 | ||||
-rw-r--r-- | src/cpu/o3/cpu.cc | 132 | ||||
-rw-r--r-- | src/cpu/o3/cpu.hh | 25 | ||||
-rw-r--r-- | src/cpu/o3/decode.hh | 4 | ||||
-rw-r--r-- | src/cpu/o3/decode_impl.hh | 8 | ||||
-rw-r--r-- | src/cpu/o3/fetch.hh | 3 | ||||
-rw-r--r-- | src/cpu/o3/fetch_impl.hh | 20 | ||||
-rw-r--r-- | src/cpu/o3/iew.hh | 3 | ||||
-rw-r--r-- | src/cpu/o3/iew_impl.hh | 13 | ||||
-rw-r--r-- | src/cpu/o3/rename.hh | 3 | ||||
-rw-r--r-- | src/cpu/o3/rename_impl.hh | 22 | ||||
-rw-r--r-- | src/cpu/o3/thread_context_impl.hh | 13 | ||||
-rw-r--r-- | src/cpu/thread_context.hh | 4 | ||||
-rw-r--r-- | src/sim/eventq.hh | 3 |
15 files changed, 232 insertions, 47 deletions
diff --git a/src/cpu/o3/commit.hh b/src/cpu/o3/commit.hh index 2d7a23e5d..4e32f865d 100644 --- a/src/cpu/o3/commit.hh +++ b/src/cpu/o3/commit.hh @@ -187,6 +187,9 @@ class DefaultCommit /** Initializes stage by sending back the number of free entries. */ void startupStage(); + /** Clear all thread-specific states */ + void clearStates(ThreadID tid); + /** Initializes the draining of commit. */ void drain(); diff --git a/src/cpu/o3/commit_impl.hh b/src/cpu/o3/commit_impl.hh index 879fad292..2891ce331 100644 --- a/src/cpu/o3/commit_impl.hh +++ b/src/cpu/o3/commit_impl.hh @@ -366,6 +366,22 @@ DefaultCommit<Impl>::startupStage() template <class Impl> void +DefaultCommit<Impl>::clearStates(ThreadID tid) +{ + commitStatus[tid] = Idle; + changedROBNumEntries[tid] = false; + checkEmptyROB[tid] = false; + trapInFlight[tid] = false; + committedStores[tid] = false; + trapSquash[tid] = false; + tcSquash[tid] = false; + pc[tid].set(0); + lastCommitedSeqNum[tid] = 0; + squashAfterInst[tid] = NULL; +} + +template <class Impl> +void DefaultCommit<Impl>::drain() { drainPending = true; @@ -813,6 +829,13 @@ DefaultCommit<Impl>::commit() if (trapSquash[tid]) { assert(!tcSquash[tid]); squashFromTrap(tid); + + // If the thread is trying to exit (i.e., an exit syscall was + // executed), this trapSquash was originated by the exit + // syscall earlier. In this case, schedule an exit event in + // the next cycle to fully terminate this thread + if (cpu->isThreadExiting(tid)) + cpu->scheduleThreadExitEvent(tid); } else if (tcSquash[tid]) { assert(commitStatus[tid] != TrapPending); squashFromTC(tid); diff --git a/src/cpu/o3/cpu.cc b/src/cpu/o3/cpu.cc index f5aa9f712..e50741ec0 100644 --- a/src/cpu/o3/cpu.cc +++ b/src/cpu/o3/cpu.cc @@ -143,6 +143,8 @@ FullO3CPU<Impl>::FullO3CPU(DerivO3CPUParams *params) dtb(params->dtb), tickEvent([this]{ tick(); }, "FullO3CPU tick", false, Event::CPU_Tick_Pri), + threadExitEvent([this]{ exitThreads(); }, "FullO3CPU exit threads", + false, Event::CPU_Exit_Pri), #ifndef NDEBUG instcount(0), #endif @@ -810,7 +812,7 @@ void FullO3CPU<Impl>::haltContext(ThreadID tid) { //For now, this is the same as deallocate - DPRINTF(O3CPU,"[tid:%i]: Halt Context called. Deallocating", tid); + DPRINTF(O3CPU,"[tid:%i]: Halt Context called. Deallocating\n", tid); assert(!switchedOut()); deactivateThread(tid); @@ -886,51 +888,20 @@ FullO3CPU<Impl>::removeThread(ThreadID tid) // here to alleviate the case for double-freeing registers // in SMT workloads. - // Unbind Int Regs from Rename Map - for (RegId reg_id(IntRegClass, 0); reg_id.index() < TheISA::NumIntRegs; - reg_id.index()++) { - PhysRegIdPtr phys_reg = renameMap[tid].lookup(reg_id); - scoreboard.unsetReg(phys_reg); - freeList.addReg(phys_reg); - } - - // Unbind Float Regs from Rename Map - for (RegId reg_id(FloatRegClass, 0); reg_id.index() < TheISA::NumFloatRegs; - reg_id.index()++) { - PhysRegIdPtr phys_reg = renameMap[tid].lookup(reg_id); - scoreboard.unsetReg(phys_reg); - freeList.addReg(phys_reg); - } - - // Unbind Float Regs from Rename Map - for (unsigned preg = 0; preg < TheISA::NumVecPredRegs; preg++) { - PhysRegIdPtr phys_reg = renameMap[tid].lookup( - RegId(VecPredRegClass, preg)); - scoreboard.unsetReg(phys_reg); - freeList.addReg(phys_reg); - } - - // Unbind condition-code Regs from Rename Map - for (RegId reg_id(CCRegClass, 0); reg_id.index() < TheISA::NumCCRegs; - reg_id.index()++) { - PhysRegIdPtr phys_reg = renameMap[tid].lookup(reg_id); - scoreboard.unsetReg(phys_reg); - freeList.addReg(phys_reg); - } - - // Squash Throughout Pipeline - DynInstPtr inst = commit.rob->readHeadInst(tid); - InstSeqNum squash_seq_num = inst->seqNum; - fetch.squash(0, squash_seq_num, inst, tid); - decode.squash(tid); - rename.squash(squash_seq_num, tid); - iew.squash(tid); - iew.ldstQueue.squash(squash_seq_num, tid); - commit.rob->squash(squash_seq_num, tid); - - + // clear all thread-specific states in each stage of the pipeline + // since this thread is going to be completely removed from the CPU + commit.clearStates(tid); + fetch.clearStates(tid); + decode.clearStates(tid); + rename.clearStates(tid); + iew.clearStates(tid); + + // at this step, all instructions in the pipeline should be already + // either committed successfully or squashed. All thread-specific + // queues in the pipeline must be empty. assert(iew.instQueue.getCount(tid) == 0); assert(iew.ldstQueue.getCount(tid) == 0); + assert(commit.rob->isEmpty(tid)); // Reset ROB/IQ/LSQ Entries @@ -1884,5 +1855,78 @@ FullO3CPU<Impl>::updateThreadPriority() } } +template <class Impl> +void +FullO3CPU<Impl>::addThreadToExitingList(ThreadID tid) +{ + DPRINTF(O3CPU, "Thread %d is inserted to exitingThreads list\n", tid); + + // make sure the thread is Active + assert(std::find(activeThreads.begin(), activeThreads.end(), tid) + != activeThreads.end()); + + // make sure the thread has not been added to the list yet + assert(exitingThreads.count(tid) == 0); + + // add the thread to exitingThreads list to mark that this thread is + // trying to exit. The boolean value in the pair denotes if a thread is + // ready to exit. The thread is not ready to exit until the corresponding + // exit trap event is processed in the future. Until then, it'll be still + // an active thread that is trying to exit. + exitingThreads.emplace(std::make_pair(tid, false)); +} + +template <class Impl> +bool +FullO3CPU<Impl>::isThreadExiting(ThreadID tid) const +{ + return exitingThreads.count(tid) == 1; +} + +template <class Impl> +void +FullO3CPU<Impl>::scheduleThreadExitEvent(ThreadID tid) +{ + assert(exitingThreads.count(tid) == 1); + + // exit trap event has been processed. Now, the thread is ready to exit + // and be removed from the CPU. + exitingThreads[tid] = true; + + // we schedule a threadExitEvent in the next cycle to properly clean + // up the thread's states in the pipeline. threadExitEvent has lower + // priority than tickEvent, so the cleanup will happen at the very end + // of the next cycle after all pipeline stages complete their operations. + // We want all stages to complete squashing instructions before doing + // the cleanup. + if (!threadExitEvent.scheduled()) { + schedule(threadExitEvent, nextCycle()); + } +} + +template <class Impl> +void +FullO3CPU<Impl>::exitThreads() +{ + // there must be at least one thread trying to exit + assert(exitingThreads.size() > 0); + + // terminate all threads that are ready to exit + auto it = exitingThreads.begin(); + while (it != exitingThreads.end()) { + ThreadID thread_id = it->first; + bool readyToExit = it->second; + + if (readyToExit) { + DPRINTF(O3CPU, "Exiting thread %d\n", thread_id); + haltContext(thread_id); + tcBase(thread_id)->setStatus(ThreadContext::Halted); + it = exitingThreads.erase(it); + } else { + it++; + } + } +} + // Forward declaration of FullO3CPU. template class FullO3CPU<O3CPUImpl>; diff --git a/src/cpu/o3/cpu.hh b/src/cpu/o3/cpu.hh index aabac5fea..ec6be657a 100644 --- a/src/cpu/o3/cpu.hh +++ b/src/cpu/o3/cpu.hh @@ -205,6 +205,9 @@ class FullO3CPU : public BaseO3CPU /** The tick event used for scheduling CPU ticks. */ EventFunctionWrapper tickEvent; + /** The exit event used for terminating all ready-to-exit threads */ + EventFunctionWrapper threadExitEvent; + /** Schedule tick event, regardless of its current state. */ void scheduleTickEvent(Cycles delay) { @@ -331,6 +334,21 @@ class FullO3CPU : public BaseO3CPU void serializeThread(CheckpointOut &cp, ThreadID tid) const override; void unserializeThread(CheckpointIn &cp, ThreadID tid) override; + /** Insert tid to the list of threads trying to exit */ + void addThreadToExitingList(ThreadID tid); + + /** Is the thread trying to exit? */ + bool isThreadExiting(ThreadID tid) const; + + /** + * If a thread is trying to exit and its corresponding trap event + * has been completed, schedule an event to terminate the thread. + */ + void scheduleThreadExitEvent(ThreadID tid); + + /** Terminate all threads that are ready to exit */ + void exitThreads(); + public: /** Executes a syscall. * @todo: Determine if this needs to be virtual. @@ -648,6 +666,13 @@ class FullO3CPU : public BaseO3CPU /** Active Threads List */ std::list<ThreadID> activeThreads; + /** + * This is a list of threads that are trying to exit. Each thread id + * is mapped to a boolean value denoting whether the thread is ready + * to exit. + */ + std::unordered_map<ThreadID, bool> exitingThreads; + /** Integer Register Scoreboard */ Scoreboard scoreboard; diff --git a/src/cpu/o3/decode.hh b/src/cpu/o3/decode.hh index a1e29a6b5..4cd318404 100644 --- a/src/cpu/o3/decode.hh +++ b/src/cpu/o3/decode.hh @@ -102,6 +102,10 @@ class DefaultDecode DefaultDecode(O3CPU *_cpu, DerivO3CPUParams *params); void startupStage(); + + /** Clear all thread-specific states */ + void clearStates(ThreadID tid); + void resetStage(); /** Returns the name of decode. */ diff --git a/src/cpu/o3/decode_impl.hh b/src/cpu/o3/decode_impl.hh index 63b180ef9..27b3c30a1 100644 --- a/src/cpu/o3/decode_impl.hh +++ b/src/cpu/o3/decode_impl.hh @@ -93,6 +93,14 @@ DefaultDecode<Impl>::startupStage() template<class Impl> void +DefaultDecode<Impl>::clearStates(ThreadID tid) +{ + decodeStatus[tid] = Idle; + stalls[tid].rename = false; +} + +template<class Impl> +void DefaultDecode<Impl>::resetStage() { _status = Inactive; diff --git a/src/cpu/o3/fetch.hh b/src/cpu/o3/fetch.hh index ee1932bec..3cf0773fd 100644 --- a/src/cpu/o3/fetch.hh +++ b/src/cpu/o3/fetch.hh @@ -216,6 +216,9 @@ class DefaultFetch /** Initialize stage. */ void startupStage(); + /** Clear all thread-specific states*/ + void clearStates(ThreadID tid); + /** Handles retrying the fetch access. */ void recvReqRetry(); diff --git a/src/cpu/o3/fetch_impl.hh b/src/cpu/o3/fetch_impl.hh index 73c1ed156..8afe62335 100644 --- a/src/cpu/o3/fetch_impl.hh +++ b/src/cpu/o3/fetch_impl.hh @@ -328,6 +328,26 @@ DefaultFetch<Impl>::startupStage() template<class Impl> void +DefaultFetch<Impl>::clearStates(ThreadID tid) +{ + fetchStatus[tid] = Running; + pc[tid] = cpu->pcState(tid); + fetchOffset[tid] = 0; + macroop[tid] = NULL; + delayedCommit[tid] = false; + memReq[tid] = NULL; + stalls[tid].decode = false; + stalls[tid].drain = false; + fetchBufferPC[tid] = 0; + fetchBufferValid[tid] = false; + fetchQueue[tid].clear(); + + // TODO not sure what to do with priorityList for now + // priorityList.push_back(tid); +} + +template<class Impl> +void DefaultFetch<Impl>::resetStage() { numInst = 0; diff --git a/src/cpu/o3/iew.hh b/src/cpu/o3/iew.hh index b15521f5d..363c645c3 100644 --- a/src/cpu/o3/iew.hh +++ b/src/cpu/o3/iew.hh @@ -147,6 +147,9 @@ class DefaultIEW /** Initializes stage; sends back the number of free IQ and LSQ entries. */ void startupStage(); + /** Clear all thread-specific states */ + void clearStates(ThreadID tid); + /** Sets main time buffer used for backwards communication. */ void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr); diff --git a/src/cpu/o3/iew_impl.hh b/src/cpu/o3/iew_impl.hh index 3d5d84886..251389631 100644 --- a/src/cpu/o3/iew_impl.hh +++ b/src/cpu/o3/iew_impl.hh @@ -325,6 +325,19 @@ DefaultIEW<Impl>::startupStage() template<class Impl> void +DefaultIEW<Impl>::clearStates(ThreadID tid) +{ + toRename->iewInfo[tid].usedIQ = true; + toRename->iewInfo[tid].freeIQEntries = + instQueue.numFreeEntries(tid); + + toRename->iewInfo[tid].usedLSQ = true; + toRename->iewInfo[tid].freeLQEntries = ldstQueue.numFreeLoadEntries(tid); + toRename->iewInfo[tid].freeSQEntries = ldstQueue.numFreeStoreEntries(tid); +} + +template<class Impl> +void DefaultIEW<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) { timeBuffer = tb_ptr; diff --git a/src/cpu/o3/rename.hh b/src/cpu/o3/rename.hh index a091c0908..572790991 100644 --- a/src/cpu/o3/rename.hh +++ b/src/cpu/o3/rename.hh @@ -169,6 +169,9 @@ class DefaultRename /** Initializes variables for the stage. */ void startupStage(); + /** Clear all thread-specific states */ + void clearStates(ThreadID tid); + /** Sets pointer to list of active threads. */ void setActiveThreads(std::list<ThreadID> *at_ptr); diff --git a/src/cpu/o3/rename_impl.hh b/src/cpu/o3/rename_impl.hh index b63163f04..fd9b09e20 100644 --- a/src/cpu/o3/rename_impl.hh +++ b/src/cpu/o3/rename_impl.hh @@ -256,6 +256,28 @@ DefaultRename<Impl>::startupStage() template <class Impl> void +DefaultRename<Impl>::clearStates(ThreadID tid) +{ + renameStatus[tid] = Idle; + + freeEntries[tid].iqEntries = iew_ptr->instQueue.numFreeEntries(tid); + freeEntries[tid].lqEntries = iew_ptr->ldstQueue.numFreeLoadEntries(tid); + freeEntries[tid].sqEntries = iew_ptr->ldstQueue.numFreeStoreEntries(tid); + freeEntries[tid].robEntries = commit_ptr->numROBFreeEntries(tid); + emptyROB[tid] = true; + + stalls[tid].iew = false; + serializeInst[tid] = NULL; + + instsInProgress[tid] = 0; + loadsInProgress[tid] = 0; + storesInProgress[tid] = 0; + + serializeOnNextInst[tid] = false; +} + +template <class Impl> +void DefaultRename<Impl>::resetStage() { _status = Inactive; diff --git a/src/cpu/o3/thread_context_impl.hh b/src/cpu/o3/thread_context_impl.hh index 473e2e28e..57e0a45f5 100644 --- a/src/cpu/o3/thread_context_impl.hh +++ b/src/cpu/o3/thread_context_impl.hh @@ -128,11 +128,18 @@ O3ThreadContext<Impl>::halt() { DPRINTF(O3CPU, "Calling halt on Thread Context %d\n", threadId()); - if (thread->status() == ThreadContext::Halted) + if (thread->status() == ThreadContext::Halting || + thread->status() == ThreadContext::Halted) return; - thread->setStatus(ThreadContext::Halted); - cpu->haltContext(thread->threadId()); + // the thread is not going to halt/terminate immediately in this cycle. + // The thread will be removed after an exit trap is processed + // (e.g., after trapLatency cycles). Until then, the thread's status + // will be Halting. + thread->setStatus(ThreadContext::Halting); + + // add this thread to the exiting list to mark that it is trying to exit. + cpu->addThreadToExitingList(thread->threadId()); } template <class Impl> diff --git a/src/cpu/thread_context.hh b/src/cpu/thread_context.hh index a570b9a00..6b9ff1a12 100644 --- a/src/cpu/thread_context.hh +++ b/src/cpu/thread_context.hh @@ -111,6 +111,10 @@ class ThreadContext /// synchronization, etc. Suspended, + /// Trying to exit and waiting for an event to completely exit. + /// Entered when target executes an exit syscall. + Halting, + /// Permanently shut down. Entered when target executes /// m5exit pseudo-instruction. When all contexts enter /// this state, the simulation will terminate. diff --git a/src/sim/eventq.hh b/src/sim/eventq.hh index 6e8e63338..895f69424 100644 --- a/src/sim/eventq.hh +++ b/src/sim/eventq.hh @@ -161,6 +161,9 @@ class EventBase /// (such as writebacks). static const Priority CPU_Tick_Pri = 50; + /// If we want to exit a thread in a CPU, it comes after CPU_Tick_Pri + static const Priority CPU_Exit_Pri = 64; + /// Statistics events (dump, reset, etc.) come after /// everything else, but before exit. static const Priority Stat_Event_Pri = 90; |