/*
- * Copyright (c) 2011 ARM Limited
+ * Copyright (c) 2011-2012 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
*/
void regStats();
- void switchOut();
+ /** Perform sanity checks after a drain. */
+ void drainSanityCheck() const;
+ /** Take over execution from another CPU's thread. */
void takeOverFrom();
/**
/*
- * Copyright (c) 2011 ARM Limited
+ * Copyright (c) 2011-2012 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
template <class Impl>
void
-BPredUnit<Impl>::switchOut()
+BPredUnit<Impl>::drainSanityCheck() const
{
- // Clear any state upon switch out.
- for (int i = 0; i < Impl::MaxThreads; ++i) {
- squash(0, i);
- }
+ // We shouldn't have any outstanding requests when we resume from
+ // a drained system.
+ for (int i = 0; i < Impl::MaxThreads; ++i)
+ assert(predHist[i].empty());
}
template <class Impl>
void startupStage();
/** Initializes the draining of commit. */
- bool drain();
+ void drain();
/** Resumes execution after draining. */
- void resume();
+ void drainResume();
- /** Completes the switch out of commit. */
- void switchOut();
+ /** Perform sanity checks after a drain. */
+ void drainSanityCheck() const;
+
+ /** Has the stage drained? */
+ bool isDrained() const;
/** Takes over from another CPU's thread. */
void takeOverFrom();
/** Is a drain pending. */
bool drainPending;
- /** Is commit switched out. */
- bool switchedOut;
-
/** The latency to handle a trap. Used when scheduling trap
* squash event.
*/
#include "debug/Activity.hh"
#include "debug/Commit.hh"
#include "debug/CommitRate.hh"
+#include "debug/Drain.hh"
#include "debug/ExecFaulting.hh"
#include "params/DerivO3CPU.hh"
#include "sim/faults.hh"
commitWidth(params->commitWidth),
numThreads(params->numThreads),
drainPending(false),
- switchedOut(false),
trapLatency(params->trapLatency),
canHandleInterrupts(true)
{
}
template <class Impl>
-bool
+void
DefaultCommit<Impl>::drain()
{
drainPending = true;
-
- return false;
}
template <class Impl>
void
-DefaultCommit<Impl>::switchOut()
+DefaultCommit<Impl>::drainResume()
{
- switchedOut = true;
drainPending = false;
- rob->switchOut();
}
template <class Impl>
void
-DefaultCommit<Impl>::resume()
+DefaultCommit<Impl>::drainSanityCheck() const
{
- drainPending = false;
+ assert(isDrained());
+ rob->drainSanityCheck();
+}
+
+template <class Impl>
+bool
+DefaultCommit<Impl>::isDrained() const
+{
+ /* Make sure no one is executing microcode. There are two reasons
+ * for this:
+ * - Hardware virtualized CPUs can't switch into the middle of a
+ * microcode sequence.
+ * - The current fetch implementation will most likely get very
+ * confused if it tries to start fetching an instruction that
+ * is executing in the middle of a ucode sequence that changes
+ * address mappings. This can happen on for example x86.
+ */
+ for (ThreadID tid = 0; tid < numThreads; tid++) {
+ if (pc[tid].microPC() != 0)
+ return false;
+ }
+
+ /* Make sure that all instructions have finished committing before
+ * declaring the system as drained. We want the pipeline to be
+ * completely empty when we declare the CPU to be drained. This
+ * makes debugging easier since CPU handover and restoring from a
+ * checkpoint with a different CPU should have the same timing.
+ */
+ return rob->isEmpty() &&
+ interrupt == NoFault;
}
template <class Impl>
void
DefaultCommit<Impl>::takeOverFrom()
{
- switchedOut = false;
_status = Active;
_nextStatus = Inactive;
for (ThreadID tid = 0; tid < numThreads; tid++) {
wroteToTimeBuffer = false;
_nextStatus = Inactive;
- if (drainPending && cpu->instList.empty() && !iewStage->hasStoresToWB() &&
- interrupt == NoFault) {
- cpu->signalDrained();
- drainPending = false;
- return;
- }
-
if (activeThreads->empty())
return;
if (head_inst->isSquashAfter())
squashAfter(tid, head_inst);
+ if (drainPending) {
+ DPRINTF(Drain, "Draining: %i:%s\n", tid, pc[tid]);
+ if (pc[tid].microPC() == 0 && interrupt == NoFault) {
+ squashAfter(tid, head_inst);
+ cpu->commitDrained(tid);
+ }
+ }
+
int count = 0;
Addr oldpc;
// Debug statement. Checks to make sure we're not
globalSeqNum(1),
system(params->system),
- drainCount(0),
+ drainManager(NULL),
lastRunningCycle(curCycle())
{
if (!params->switched_out) {
FullO3CPU<Impl>::tick()
{
DPRINTF(O3CPU, "\n\nFullO3CPU: Ticking main, FullO3CPU.\n");
+ assert(!switchedOut());
+ assert(getDrainState() != Drainable::Drained);
++numCycles;
}
if (!tickEvent.scheduled()) {
- if (_status == SwitchedOut ||
- getDrainState() == Drainable::Drained) {
+ if (_status == SwitchedOut) {
DPRINTF(O3CPU, "Switched out!\n");
// increment stat
lastRunningCycle = curCycle();
if (!FullSystem)
updateThreadPriority();
+
+ tryDrain();
}
template <class Impl>
thread[tid]->initMemProxies(thread[tid]->getTC());
}
- // this CPU could still be unconnected if we are restoring from a
- // checkpoint and this CPU is to be switched in, thus we can only
- // do this here if the instruction port is actually connected, if
- // not we have to do it as part of takeOverFrom
- if (icachePort.isConnected())
- fetch.setIcache();
-
if (FullSystem && !params()->switched_out) {
for (ThreadID tid = 0; tid < numThreads; tid++) {
ThreadContext *src_tc = threadContexts[tid];
FullO3CPU<Impl>::startup()
{
fetch.startupStage();
+ decode.startupStage();
iew.startupStage();
rename.startupStage();
commit.startupStage();
std::find(activeThreads.begin(), activeThreads.end(), tid);
DPRINTF(O3CPU, "[tid:%i]: Calling activate thread.\n", tid);
+ assert(!switchedOut());
if (isActive == activeThreads.end()) {
DPRINTF(O3CPU, "[tid:%i]: Adding to active threads list\n",
std::find(activeThreads.begin(), activeThreads.end(), tid);
DPRINTF(O3CPU, "[tid:%i]: Calling deactivate thread.\n", tid);
+ assert(!switchedOut());
if (thread_it != activeThreads.end()) {
DPRINTF(O3CPU,"[tid:%i]: Removing from active threads list\n",
void
FullO3CPU<Impl>::activateContext(ThreadID tid, Cycles delay)
{
+ assert(!switchedOut());
+
// Needs to set each stage to running as well.
if (delay){
DPRINTF(O3CPU, "[tid:%i]: Scheduling thread context to activate "
activateThread(tid);
}
+ // We don't want to wake the CPU if it is drained. In that case,
+ // we just want to flag the thread as active and schedule the tick
+ // event from drainResume() instead.
+ if (getDrainState() == Drainable::Drained)
+ return;
+
// If we are time 0 or if the last activation time is in the past,
// schedule the next tick and wake up the fetch unit
if (lastActivatedCycle == 0 || lastActivatedCycle < curTick()) {
FullO3CPU<Impl>::suspendContext(ThreadID tid)
{
DPRINTF(O3CPU,"[tid: %i]: Suspending Thread Context.\n", tid);
+ assert(!switchedOut());
bool deallocated = scheduleDeallocateContext(tid, false, Cycles(1));
// If this was the last thread then unschedule the tick event.
if ((activeThreads.size() == 1 && !deallocated) ||
{
//For now, this is the same as deallocate
DPRINTF(O3CPU,"[tid:%i]: Halt Context called. Deallocating", tid);
+ assert(!switchedOut());
scheduleDeallocateContext(tid, true, Cycles(1));
}
unsigned int
FullO3CPU<Impl>::drain(DrainManager *drain_manager)
{
- DPRINTF(O3CPU, "Switching out\n");
-
// If the CPU isn't doing anything, then return immediately.
- if (_status == SwitchedOut)
+ if (switchedOut()) {
+ setDrainState(Drainable::Drained);
return 0;
+ }
+
+ DPRINTF(Drain, "Draining...\n");
+ setDrainState(Drainable::Draining);
- drainCount = 0;
- fetch.drain();
- decode.drain();
- rename.drain();
- iew.drain();
+ // We only need to signal a drain to the commit stage as this
+ // initiates squashing controls the draining. Once the commit
+ // stage commits an instruction where it is safe to stop, it'll
+ // squash the rest of the instructions in the pipeline and force
+ // the fetch stage to stall. The pipeline will be drained once all
+ // in-flight instructions have retired.
commit.drain();
// Wake the CPU and record activity so everything can drain out if
// the CPU was not able to immediately drain.
- if (getDrainState() != Drainable::Drained) {
- // A bit of a hack...set the drainManager after all the drain()
- // calls have been made, that way if all of the stages drain
- // immediately, the signalDrained() function knows not to call
- // process on the drain event.
+ if (!isDrained()) {
drainManager = drain_manager;
wakeCPU();
return 1;
} else {
+ setDrainState(Drainable::Drained);
+ DPRINTF(Drain, "CPU is already drained\n");
+ if (tickEvent.scheduled())
+ deschedule(tickEvent);
+
+ // Flush out any old data from the time buffers. In
+ // particular, there might be some data in flight from the
+ // fetch stage that isn't visible in any of the CPU buffers we
+ // test in isDrained().
+ for (int i = 0; i < timeBuffer.getSize(); ++i) {
+ timeBuffer.advance();
+ fetchQueue.advance();
+ decodeQueue.advance();
+ renameQueue.advance();
+ iewQueue.advance();
+ }
+
+ drainSanityCheck();
return 0;
}
}
+template <class Impl>
+bool
+FullO3CPU<Impl>::tryDrain()
+{
+ if (!drainManager || !isDrained())
+ return false;
+
+ if (tickEvent.scheduled())
+ deschedule(tickEvent);
+
+ DPRINTF(Drain, "CPU done draining, processing drain event\n");
+ drainManager->signalDrainDone();
+ drainManager = NULL;
+
+ return true;
+}
+
template <class Impl>
void
-FullO3CPU<Impl>::drainResume()
+FullO3CPU<Impl>::drainSanityCheck() const
{
- fetch.resume();
- decode.resume();
- rename.resume();
- iew.resume();
- commit.resume();
+ assert(isDrained());
+ fetch.drainSanityCheck();
+ decode.drainSanityCheck();
+ rename.drainSanityCheck();
+ iew.drainSanityCheck();
+ commit.drainSanityCheck();
+}
- setDrainState(Drainable::Running);
+template <class Impl>
+bool
+FullO3CPU<Impl>::isDrained() const
+{
+ bool drained(true);
- if (_status == SwitchedOut)
- return;
+ for (ThreadID i = 0; i < thread.size(); ++i) {
+ if (activateThreadEvent[i].scheduled()) {
+ DPRINTF(Drain, "CPU not drained, tread %i has a "
+ "pending activate event\n", i);
+ drained = false;
+ }
+ if (deallocateContextEvent[i].scheduled()) {
+ DPRINTF(Drain, "CPU not drained, tread %i has a "
+ "pending deallocate context event\n", i);
+ drained = false;
+ }
+ }
- if (system->getMemoryMode() != Enums::timing) {
- fatal("The O3 CPU requires the memory system to be in "
- "'timing' mode.\n");
+ if (!instList.empty() || !removeList.empty()) {
+ DPRINTF(Drain, "Main CPU structures not drained.\n");
+ drained = false;
}
- if (!tickEvent.scheduled())
- schedule(tickEvent, nextCycle());
- _status = Running;
+ if (!fetch.isDrained()) {
+ DPRINTF(Drain, "Fetch not drained.\n");
+ drained = false;
+ }
+
+ if (!decode.isDrained()) {
+ DPRINTF(Drain, "Decode not drained.\n");
+ drained = false;
+ }
+
+ if (!rename.isDrained()) {
+ DPRINTF(Drain, "Rename not drained.\n");
+ drained = false;
+ }
+
+ if (!iew.isDrained()) {
+ DPRINTF(Drain, "IEW not drained.\n");
+ drained = false;
+ }
+
+ if (!commit.isDrained()) {
+ DPRINTF(Drain, "Commit not drained.\n");
+ drained = false;
+ }
+
+ return drained;
}
template <class Impl>
void
-FullO3CPU<Impl>::signalDrained()
+FullO3CPU<Impl>::commitDrained(ThreadID tid)
{
- if (++drainCount == NumStages) {
- if (tickEvent.scheduled())
- tickEvent.squash();
+ fetch.drainStall(tid);
+}
- setDrainState(Drainable::Drained);
+template <class Impl>
+void
+FullO3CPU<Impl>::drainResume()
+{
+ setDrainState(Drainable::Running);
+ if (switchedOut())
+ return;
+
+ DPRINTF(Drain, "Resuming...\n");
- if (drainManager) {
- DPRINTF(Drain, "CPU done draining, processing drain event\n");
- drainManager->signalDrainDone();
- drainManager = NULL;
+ if (system->getMemoryMode() != Enums::timing) {
+ fatal("The O3 CPU requires the memory system to be in "
+ "'timing' mode.\n");
+ }
+
+ fetch.drainResume();
+ commit.drainResume();
+
+ _status = Idle;
+ for (ThreadID i = 0; i < thread.size(); i++) {
+ if (thread[i]->status() == ThreadContext::Active) {
+ DPRINTF(Drain, "Activating thread: %i\n", i);
+ activateThread(i);
+ _status = Running;
}
}
- assert(drainCount <= 5);
+
+ assert(!tickEvent.scheduled());
+ if (_status == Running)
+ schedule(tickEvent, nextCycle());
}
template <class Impl>
void
FullO3CPU<Impl>::switchOut()
{
+ DPRINTF(O3CPU, "Switching out\n");
BaseCPU::switchOut();
- fetch.switchOut();
- rename.switchOut();
- iew.switchOut();
- commit.switchOut();
- instList.clear();
- while (!removeList.empty()) {
- removeList.pop();
- }
+ activityRec.reset();
_status = SwitchedOut;
if (checker)
checker->switchOut();
-
- if (tickEvent.scheduled())
- tickEvent.squash();
}
template <class Impl>
void
FullO3CPU<Impl>::takeOverFrom(BaseCPU *oldCPU)
{
- // Flush out any old data from the time buffers.
- for (int i = 0; i < timeBuffer.getSize(); ++i) {
- timeBuffer.advance();
- fetchQueue.advance();
- decodeQueue.advance();
- renameQueue.advance();
- iewQueue.advance();
- }
-
- activityRec.reset();
-
BaseCPU::takeOverFrom(oldCPU);
fetch.takeOverFrom();
iew.takeOverFrom();
commit.takeOverFrom();
- assert(!tickEvent.scheduled() || tickEvent.squashed());
+ assert(!tickEvent.scheduled());
FullO3CPU<Impl> *oldO3CPU = dynamic_cast<FullO3CPU<Impl>*>(oldCPU);
if (oldO3CPU)
globalSeqNum = oldO3CPU->globalSeqNum;
- // @todo: Figure out how to properly select the tid to put onto
- // the active threads list.
- ThreadID tid = 0;
-
- list<ThreadID>::iterator isActive =
- std::find(activeThreads.begin(), activeThreads.end(), tid);
-
- if (isActive == activeThreads.end()) {
- //May Need to Re-code this if the delay variable is the delay
- //needed for thread to activate
- DPRINTF(O3CPU, "Adding Thread %i to active threads list\n",
- tid);
-
- activeThreads.push_back(tid);
- }
-
- // Set all statuses to active, schedule the CPU's tick event.
- // @todo: Fix up statuses so this is handled properly
- ThreadID size = threadContexts.size();
- for (ThreadID i = 0; i < size; ++i) {
- ThreadContext *tc = threadContexts[i];
- if (tc->status() == ThreadContext::Active && _status != Running) {
- _status = Running;
- reschedule(tickEvent, nextCycle(), true);
- }
- }
- if (!tickEvent.scheduled())
- schedule(tickEvent, nextCycle());
-
lastRunningCycle = curCycle();
+ _status = Idle;
}
template <class Impl>
/*
- * Copyright (c) 2011 ARM Limited
+ * Copyright (c) 2011-2012 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
/** The tick event used for scheduling CPU ticks. */
DeallocateContextEvent deallocateContextEvent[Impl::MaxThreads];
+ /**
+ * Check if the pipeline has drained and signal the DrainManager.
+ *
+ * This method checks if a drain has been requested and if the CPU
+ * has drained successfully (i.e., there are no instructions in
+ * the pipeline). If the CPU has drained, it deschedules the tick
+ * event and signals the drain manager.
+ *
+ * @return False if a drain hasn't been requested or the CPU
+ * hasn't drained, true otherwise.
+ */
+ bool tryDrain();
+
+ /**
+ * Perform sanity checks after a drain.
+ *
+ * This method is called from drain() when it has determined that
+ * the CPU is fully drained when gem5 is compiled with the NDEBUG
+ * macro undefined. The intention of this method is to do more
+ * extensive tests than the isDrained() method to weed out any
+ * draining bugs.
+ */
+ void drainSanityCheck() const;
+
+ /** Check if a system is in a drained state. */
+ bool isDrained() const;
+
public:
/** Constructs a CPU with the given parameters. */
FullO3CPU(DerivO3CPUParams *params);
/** Update The Order In Which We Process Threads. */
void updateThreadPriority();
+ /** Is the CPU draining? */
+ bool isDraining() const { return getDrainState() == Drainable::Draining; }
+
/** Serialize state. */
virtual void serialize(std::ostream &os);
/** Resumes execution after a drain. */
void drainResume();
- /** Signals to this CPU that a stage has completed switching out. */
- void signalDrained();
+ /**
+ * Commit has reached a safe point to drain a thread.
+ *
+ * Commit calls this method to inform the pipeline that it has
+ * reached a point where it is not executed microcode and is about
+ * to squash uncommitted instructions to fully drain the pipeline.
+ */
+ void commitDrained(ThreadID tid);
/** Switches out this CPU. */
virtual void switchOut();
/** DrainManager to notify when draining has completed. */
DrainManager *drainManager;
- /** Counter of how many stages have completed draining. */
- int drainCount;
-
/** Pointers to all of the threads in the CPU. */
std::vector<Thread *> thread;
/*
+ * Copyright (c) 2012 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
* Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
/** DefaultDecode constructor. */
DefaultDecode(O3CPU *_cpu, DerivO3CPUParams *params);
+ void startupStage();
+ void resetStage();
+
/** Returns the name of decode. */
std::string name() const;
/** Sets pointer to list of active threads. */
void setActiveThreads(std::list<ThreadID> *at_ptr);
- /** Drains the decode stage. */
- bool drain();
+ /** Perform sanity checks after a drain. */
+ void drainSanityCheck() const;
- /** Resumes execution after a drain. */
- void resume() { }
-
- /** Switches out the decode stage. */
- void switchOut() { }
+ /** Has the stage drained? */
+ bool isDrained() const { return true; }
/** Takes over from another CPU's thread. */
- void takeOverFrom();
+ void takeOverFrom() { resetStage(); }
/** Ticks decode, processing all input signals and decoding as many
* instructions as possible.
/** List of active thread ids */
std::list<ThreadID> *activeThreads;
- /** Number of branches in flight. */
- unsigned branchCount[Impl::MaxThreads];
-
/** Maximum size of the skid buffer. */
unsigned skidBufferMax;
-/*
+/*
+ * Copyright (c) 2012 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
* Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
fetchToDecodeDelay(params->fetchToDecodeDelay),
decodeWidth(params->decodeWidth),
numThreads(params->numThreads)
+{
+ // @todo: Make into a parameter
+ skidBufferMax = (fetchToDecodeDelay + 1) * params->fetchWidth;
+}
+
+template<class Impl>
+void
+DefaultDecode<Impl>::startupStage()
+{
+ resetStage();
+}
+
+template<class Impl>
+void
+DefaultDecode<Impl>::resetStage()
{
_status = Inactive;
stalls[tid].iew = false;
stalls[tid].commit = false;
}
-
- // @todo: Make into a parameter
- skidBufferMax = (fetchToDecodeDelay + 1) * params->fetchWidth;
}
template <class Impl>
activeThreads = at_ptr;
}
-template <class Impl>
-bool
-DefaultDecode<Impl>::drain()
-{
- // Decode is done draining at any time.
- cpu->signalDrained();
- return true;
-}
-
template <class Impl>
void
-DefaultDecode<Impl>::takeOverFrom()
+DefaultDecode<Impl>::drainSanityCheck() const
{
- _status = Inactive;
-
- // Be sure to reset state and clear out any old instructions.
for (ThreadID tid = 0; tid < numThreads; ++tid) {
- decodeStatus[tid] = Idle;
-
- stalls[tid].rename = false;
- stalls[tid].iew = false;
- stalls[tid].commit = false;
- while (!insts[tid].empty())
- insts[tid].pop();
- while (!skidBuffer[tid].empty())
- skidBuffer[tid].pop();
- branchCount[tid] = 0;
+ assert(insts[tid].empty());
+ assert(skidBuffer[tid].empty());
}
- wroteToTimeBuffer = false;
}
template<class Impl>
/*
+ * Copyright (c) 2012 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
* Copyright (c) 2006 The Regents of The University of Michigan
* All rights reserved.
*
/** Removes and returns the newest dependent of a specific register. */
DynInstPtr pop(PhysRegIndex idx);
+ /** Checks if the entire dependency graph is empty. */
+ bool empty() const;
+
/** Checks if there are any dependents on a specific register. */
- bool empty(PhysRegIndex idx) { return !dependGraph[idx].next; }
+ bool empty(PhysRegIndex idx) const { return !dependGraph[idx].next; }
/** Debugging function to dump out the dependency graph.
*/
return inst;
}
+template <class DynInstPtr>
+bool
+DependencyGraph<DynInstPtr>::empty() const
+{
+ for (int i = 0; i < numEntries; ++i) {
+ if (!empty(i))
+ return false;
+ }
+ return true;
+}
+
template <class DynInstPtr>
void
DependencyGraph<DynInstPtr>::dump()
/*
- * Copyright (c) 2010-2011 ARM Limited
+ * Copyright (c) 2010-2012 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
Fetching,
TrapPending,
QuiescePending,
- SwitchOut,
ItlbWait,
IcacheWaitResponse,
IcacheWaitRetry,
/** Processes cache completion event. */
void processCacheCompletion(PacketPtr pkt);
- /** Begins the drain of the fetch stage. */
- bool drain();
+ /** Resume after a drain. */
+ void drainResume();
- /** Resumes execution after a drain. */
- void resume();
+ /** Perform sanity checks after a drain. */
+ void drainSanityCheck() const;
- /** Tells fetch stage to prepare to be switched out. */
- void switchOut();
+ /** Has the stage drained? */
+ bool isDrained() const;
/** Takes over from another CPU's thread. */
void takeOverFrom();
- /** Checks if the fetch stage is switched out. */
- bool isSwitchedOut() { return switchedOut; }
+ /**
+ * Stall the fetch stage after reaching a safe drain point.
+ *
+ * The CPU uses this method to stop fetching instructions from a
+ * thread that has been drained. The drain stall is different from
+ * all other stalls in that it is signaled instantly from the
+ * commit stage (without the normal communication delay) when it
+ * has reached a safe point to drain from.
+ */
+ void drainStall(ThreadID tid);
/** Tells fetch to wake up from a quiesce instruction. */
void wakeFromQuiesce();
private:
+ /** Reset this pipeline stage */
+ void resetStage();
+
/** Changes the status of this stage to active, and indicates this
* to the CPU.
*/
bool rename;
bool iew;
bool commit;
+ bool drain;
};
/** Tracks which stages are telling fetch to stall. */
*/
bool interruptPending;
- /** Is there a drain pending. */
- bool drainPending;
-
- /** Records if fetch is switched out. */
- bool switchedOut;
-
/** Set to true if a pipelined I-cache request should be issued. */
bool issuePipelinedIfetch[Impl::MaxThreads];
/*
- * Copyright (c) 2010-2011 ARM Limited
+ * Copyright (c) 2010-2012 ARM Limited
* All rights reserved.
*
* The license below extends only to copyright in the software and shall
#include "cpu/o3/fetch.hh"
#include "cpu/exetrace.hh"
#include "debug/Activity.hh"
+#include "debug/Drain.hh"
#include "debug/Fetch.hh"
#include "mem/packet.hh"
#include "params/DerivO3CPU.hh"
DefaultFetch<Impl>::DefaultFetch(O3CPU *_cpu, DerivO3CPUParams *params)
: cpu(_cpu),
branchPred(params),
- numInst(0),
decodeToFetchDelay(params->decodeToFetchDelay),
renameToFetchDelay(params->renameToFetchDelay),
iewToFetchDelay(params->iewToFetchDelay),
commitToFetchDelay(params->commitToFetchDelay),
fetchWidth(params->fetchWidth),
- cacheBlocked(false),
retryPkt(NULL),
retryTid(InvalidThreadID),
numThreads(params->numThreads),
numFetchingThreads(params->smtNumFetchingThreads),
- interruptPending(false),
- drainPending(false),
- switchedOut(false),
finishTranslationEvent(this)
{
if (numThreads > Impl::MaxThreads)
"\tincrease MaxWidth in src/cpu/o3/impl.hh\n",
fetchWidth, static_cast<int>(Impl::MaxWidth));
- // Set fetch stage's status to inactive.
- _status = Inactive;
-
std::string policy = params->smtFetchPolicy;
// Convert string to lowercase
void
DefaultFetch<Impl>::startupStage()
{
+ assert(priorityList.empty());
+ resetStage();
+
+ // Fetch needs to start fetching instructions at the very beginning,
+ // so it must start up in active state.
+ switchToActive();
+}
+
+template<class Impl>
+void
+DefaultFetch<Impl>::resetStage()
+{
+ numInst = 0;
+ interruptPending = false;
+ cacheBlocked = false;
+
+ priorityList.clear();
+
// Setup PC and nextPC with initial state.
for (ThreadID tid = 0; tid < numThreads; tid++) {
+ fetchStatus[tid] = Running;
pc[tid] = cpu->pcState(tid);
fetchOffset[tid] = 0;
macroop[tid] = NULL;
- delayedCommit[tid] = false;
- }
-
- for (ThreadID tid = 0; tid < numThreads; tid++) {
-
- fetchStatus[tid] = Running;
-
- priorityList.push_back(tid);
+ delayedCommit[tid] = false;
memReq[tid] = NULL;
stalls[tid].decode = false;
stalls[tid].rename = false;
stalls[tid].iew = false;
stalls[tid].commit = false;
+ stalls[tid].drain = false;
+
+ priorityList.push_back(tid);
}
- // Schedule fetch to get the correct PC from the CPU
- // scheduleFetchStartupEvent(1);
+ wroteToTimeBuffer = false;
+ _status = Inactive;
- // Fetch needs to start fetching instructions at the very beginning,
- // so it must start up in active state.
- switchToActive();
+ // this CPU could still be unconnected if we are restoring from a
+ // checkpoint and this CPU is to be switched in, thus we can only
+ // do this here if the instruction port is actually connected, if
+ // not we have to do it as part of takeOverFrom.
+ if (cpu->getInstPort().isConnected())
+ setIcache();
}
template<class Impl>
ThreadID tid = pkt->req->threadId();
DPRINTF(Fetch, "[tid:%u] Waking up from cache miss.\n", tid);
+ assert(!cpu->switchedOut());
// Only change the status if it's still waiting on the icache access
// to return.
if (fetchStatus[tid] != IcacheWaitResponse ||
- pkt->req != memReq[tid] ||
- isSwitchedOut()) {
+ pkt->req != memReq[tid]) {
++fetchIcacheSquashes;
delete pkt->req;
delete pkt;
memcpy(cacheData[tid], pkt->getPtr<uint8_t>(), cacheBlkSize);
cacheDataValid[tid] = true;
- if (!drainPending) {
- // Wake up the CPU (if it went to sleep and was waiting on
- // this completion event).
- cpu->wakeCPU();
+ // Wake up the CPU (if it went to sleep and was waiting on
+ // this completion event).
+ cpu->wakeCPU();
- DPRINTF(Activity, "[tid:%u] Activating fetch due to cache completion\n",
- tid);
+ DPRINTF(Activity, "[tid:%u] Activating fetch due to cache completion\n",
+ tid);
- switchToActive();
- }
+ switchToActive();
// Only switch to IcacheAccessComplete if we're not stalled as well.
if (checkStall(tid)) {
}
template <class Impl>
-bool
-DefaultFetch<Impl>::drain()
+void
+DefaultFetch<Impl>::drainResume()
{
- // Fetch is ready to drain at any time.
- cpu->signalDrained();
- drainPending = true;
- return true;
+ for (ThreadID i = 0; i < Impl::MaxThreads; ++i)
+ stalls[i].drain = false;
}
template <class Impl>
void
-DefaultFetch<Impl>::resume()
+DefaultFetch<Impl>::drainSanityCheck() const
{
- drainPending = false;
+ assert(isDrained());
+ assert(retryPkt == NULL);
+ assert(retryTid == InvalidThreadID);
+ assert(cacheBlocked == false);
+ assert(interruptPending == false);
+
+ for (ThreadID i = 0; i < numThreads; ++i) {
+ assert(!memReq[i]);
+ assert(!stalls[i].decode);
+ assert(!stalls[i].rename);
+ assert(!stalls[i].iew);
+ assert(!stalls[i].commit);
+ assert(fetchStatus[i] == Idle || stalls[i].drain);
+ }
+
+ branchPred.drainSanityCheck();
}
template <class Impl>
-void
-DefaultFetch<Impl>::switchOut()
+bool
+DefaultFetch<Impl>::isDrained() const
{
- switchedOut = true;
- // Branch predictor needs to have its state cleared.
- branchPred.switchOut();
+ /* Make sure that threads are either idle of that the commit stage
+ * has signaled that draining has completed by setting the drain
+ * stall flag. This effectively forces the pipeline to be disabled
+ * until the whole system is drained (simulation may continue to
+ * drain other components).
+ */
+ for (ThreadID i = 0; i < numThreads; ++i) {
+ if (!(fetchStatus[i] == Idle ||
+ (fetchStatus[i] == Blocked && stalls[i].drain)))
+ return false;
+ }
+
+ /* The pipeline might start up again in the middle of the drain
+ * cycle if the finish translation event is scheduled, so make
+ * sure that's not the case.
+ */
+ return !finishTranslationEvent.scheduled();
}
template <class Impl>
void
DefaultFetch<Impl>::takeOverFrom()
{
- // the instruction port is now connected so we can get the block
- // size
- setIcache();
+ assert(cpu->getInstPort().isConnected());
+ resetStage();
- // Reset all state
- for (ThreadID i = 0; i < Impl::MaxThreads; ++i) {
- stalls[i].decode = 0;
- stalls[i].rename = 0;
- stalls[i].iew = 0;
- stalls[i].commit = 0;
- pc[i] = cpu->pcState(i);
- fetchStatus[i] = Running;
- }
- numInst = 0;
- wroteToTimeBuffer = false;
- _status = Inactive;
- switchedOut = false;
- interruptPending = false;
branchPred.takeOverFrom();
}
+template <class Impl>
+void
+DefaultFetch<Impl>::drainStall(ThreadID tid)
+{
+ assert(cpu->isDraining());
+ assert(!stalls[tid].drain);
+ DPRINTF(Drain, "%i: Thread drained.\n", tid);
+ stalls[tid].drain = true;
+}
+
template <class Impl>
void
DefaultFetch<Impl>::wakeFromQuiesce()
{
Fault fault = NoFault;
+ assert(!cpu->switchedOut());
+
// @todo: not sure if these should block translation.
//AlphaDep
if (cacheBlocked) {
DPRINTF(Fetch, "[tid:%i] Can't fetch cache line, cache blocked\n",
tid);
return false;
- } else if (isSwitchedOut()) {
- DPRINTF(Fetch, "[tid:%i] Can't fetch cache line, switched out\n",
- tid);
- return false;
} else if (checkInterrupt(pc) && !delayedCommit[tid]) {
// Hold off fetch from getting new instructions when:
// Cache is blocked, or
ThreadID tid = mem_req->threadId();
Addr block_PC = mem_req->getVaddr();
+ assert(!cpu->switchedOut());
+
// Wake up CPU if it was idle
cpu->wakeCPU();
if (fetchStatus[tid] != ItlbWait || mem_req != memReq[tid] ||
- mem_req->getVaddr() != memReq[tid]->getVaddr() || isSwitchedOut()) {
+ mem_req->getVaddr() != memReq[tid]->getVaddr()) {
DPRINTF(Fetch, "[tid:%i] Ignoring itlb completed after squash\n",
tid);
++fetchTlbSquashes;
if (cpu->contextSwitch) {
DPRINTF(Fetch,"[tid:%i]: Stalling for a context switch.\n",tid);
ret_val = true;
+ } else if (stalls[tid].drain) {
+ assert(cpu->isDraining());
+ DPRINTF(Fetch,"[tid:%i]: Drain stall detected.\n",tid);
+ ret_val = true;
} else if (stalls[tid].decode) {
DPRINTF(Fetch,"[tid:%i]: Stall from Decode stage detected.\n",tid);
ret_val = true;
//////////////////////////////////////////
ThreadID tid = getFetchingThread(fetchPolicy);
- if (tid == InvalidThreadID || drainPending) {
+ assert(!cpu->switchedOut());
+
+ if (tid == InvalidThreadID) {
// Breaks looping condition in tick()
threadFetched = numFetchingThreads;
else
++fetchMiscStallCycles;
return;
- } else if ((checkInterrupt(thisPC.instAddr()) && !delayedCommit[tid])
- || isSwitchedOut()) {
+ } else if ((checkInterrupt(thisPC.instAddr()) && !delayedCommit[tid])) {
// Stall CPU if an interrupt is posted and we're not issuing
// an delayed commit micro-op currently (delayed commit instructions
// are not interruptable by interrupts, only faults)
// @todo Per-thread stats
- if (drainPending) {
+ if (stalls[tid].drain) {
++fetchPendingDrainCycles;
DPRINTF(Fetch, "Fetch is waiting for a drain!\n");
} else if (activeThreads->empty()) {
/*
+ * Copyright (c) 2012 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
* Copyright (c) 2006 The Regents of The University of Michigan
* All rights reserved.
*
}
void
-FUPool::switchOut()
+FUPool::drainSanityCheck() const
{
-}
-
-void
-FUPool::takeOver()
-{
- for (int i = 0; i < numFU; i++) {
- unitBusy[i] = false;
- }
- unitsToBeFreed.clear();
+ assert(unitsToBeFreed.empty());
+ for (int i = 0; i < numFU; i++)
+ assert(!unitBusy[i]);
}
//
/*
+ * Copyright (c) 2012 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
* Copyright (c) 2006 The Regents of The University of Michigan
* All rights reserved.
*
return maxIssueLatencies[capability];
}
- /** Switches out functional unit pool. */
- void switchOut();
+ /** Perform sanity checks after a drain. */
+ void drainSanityCheck() const;
/** Takes over from another CPU's thread. */
- void takeOver();
+ void takeOverFrom() {};
};
#endif // __CPU_O3_FU_POOL_HH__
/*
- * Copyright (c) 2010 ARM Limited
+ * Copyright (c) 2010-2012 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
/** Sets pointer to the scoreboard. */
void setScoreboard(Scoreboard *sb_ptr);
- /** Drains IEW stage. */
- bool drain();
+ /** Perform sanity checks after a drain. */
+ void drainSanityCheck() const;
- /** Resumes execution after a drain. */
- void resume();
-
- /** Completes switch out of IEW stage. */
- void switchOut();
+ /** Has the stage drained? */
+ bool isDrained() const;
/** Takes over from another CPU's thread. */
void takeOverFrom();
- /** Returns if IEW is switched out. */
- bool isSwitchedOut() { return switchedOut; }
-
/** Squashes instructions in IEW for a specific thread. */
void squash(ThreadID tid);
/** Maximum size of the skid buffer. */
unsigned skidBufferMax;
- /** Is this stage switched out. */
- bool switchedOut;
-
/** Stat for total number of idle cycles. */
Stats::Scalar iewIdleCycles;
/** Stat for total number of squashing cycles. */
/*
- * Copyright (c) 2010-2011 ARM Limited
+ * Copyright (c) 2010-2012 ARM Limited
* All rights reserved.
*
* The license below extends only to copyright in the software and shall
#include "cpu/timebuf.hh"
#include "debug/Activity.hh"
#include "debug/Decode.hh"
+#include "debug/Drain.hh"
#include "debug/IEW.hh"
#include "params/DerivO3CPU.hh"
issueWidth(params->issueWidth),
wbOutstanding(0),
wbWidth(params->wbWidth),
- numThreads(params->numThreads),
- switchedOut(false)
+ numThreads(params->numThreads)
{
_status = Active;
exeStatus = Running;
template <class Impl>
bool
-DefaultIEW<Impl>::drain()
+DefaultIEW<Impl>::isDrained() const
{
- // IEW is ready to drain at any time.
- cpu->signalDrained();
- return true;
-}
+ bool drained(ldstQueue.isDrained());
-template <class Impl>
-void
-DefaultIEW<Impl>::resume()
-{
+ for (ThreadID tid = 0; tid < numThreads; tid++) {
+ if (!insts[tid].empty()) {
+ DPRINTF(Drain, "%i: Insts not empty.\n", tid);
+ drained = false;
+ }
+ if (!skidBuffer[tid].empty()) {
+ DPRINTF(Drain, "%i: Skid buffer not empty.\n", tid);
+ drained = false;
+ }
+ }
+
+ return drained;
}
template <class Impl>
void
-DefaultIEW<Impl>::switchOut()
+DefaultIEW<Impl>::drainSanityCheck() const
{
- // Clear any state.
- switchedOut = true;
- assert(insts[0].empty());
- assert(skidBuffer[0].empty());
+ assert(isDrained());
- instQueue.switchOut();
- ldstQueue.switchOut();
- fuPool->switchOut();
-
- for (ThreadID tid = 0; tid < numThreads; tid++) {
- while (!insts[tid].empty())
- insts[tid].pop();
- while (!skidBuffer[tid].empty())
- skidBuffer[tid].pop();
- }
+ instQueue.drainSanityCheck();
+ ldstQueue.drainSanityCheck();
+ fuPool->drainSanityCheck();
}
template <class Impl>
_status = Active;
exeStatus = Running;
wbStatus = Idle;
- switchedOut = false;
instQueue.takeOverFrom();
ldstQueue.takeOverFrom();
- fuPool->takeOver();
+ fuPool->takeOverFrom();
startupStage();
cpu->activityThisCycle();
/*
- * Copyright (c) 2011 ARM Limited
+ * Copyright (c) 2011-2012 ARM Limited
* All rights reserved.
*
* The license below extends only to copyright in the software and shall
/** Sets the global time buffer. */
void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr);
- /** Switches out the instruction queue. */
- void switchOut();
+ /** Perform sanity checks after a drain. */
+ void drainSanityCheck() const;
/** Takes over execution from another CPU's thread. */
void takeOverFrom();
- /** Returns if the IQ is switched out. */
- bool isSwitchedOut() { return switchedOut; }
-
/** Number of entries needed for given amount of threads. */
int entryAmount(ThreadID num_threads);
*/
Cycles commitToIEWDelay;
- /** Is the IQ switched out. */
- bool switchedOut;
-
/** The sequence number of the squashed instruction. */
InstSeqNum squashedSeqNum[Impl::MaxThreads];
/*
- * Copyright (c) 2011 ARM Limited
+ * Copyright (c) 2011-2012 ARM Limited
* All rights reserved.
*
* The license below extends only to copyright in the software and shall
{
assert(fuPool);
- switchedOut = false;
-
numThreads = params->numThreads;
// Set the number of physical registers as the number of int + float
template <class Impl>
void
-InstructionQueue<Impl>::switchOut()
+InstructionQueue<Impl>::drainSanityCheck() const
{
-/*
- if (!instList[0].empty() || (numEntries != freeEntries) ||
- !readyInsts[0].empty() || !nonSpecInsts.empty() || !listOrder.empty()) {
- dumpInsts();
-// assert(0);
- }
-*/
- resetState();
- dependGraph.reset();
- instsToExecute.clear();
- switchedOut = true;
- for (ThreadID tid = 0; tid < numThreads; ++tid) {
- memDepUnit[tid].switchOut();
- }
+ assert(dependGraph.empty());
+ assert(instsToExecute.empty());
+ for (ThreadID tid = 0; tid < numThreads; ++tid)
+ memDepUnit[tid].drainSanityCheck();
}
template <class Impl>
void
InstructionQueue<Impl>::takeOverFrom()
{
- switchedOut = false;
+ resetState();
}
template <class Impl>
InstructionQueue<Impl>::processFUCompletion(DynInstPtr &inst, int fu_idx)
{
DPRINTF(IQ, "Processing FU completion [sn:%lli]\n", inst->seqNum);
+ assert(!cpu->switchedOut());
// The CPU could have been sleeping until this op completed (*extremely*
// long latency op). Wake it if it was. This may be overkill.
- if (isSwitchedOut()) {
- DPRINTF(IQ, "FU completion not processed, IQ is switched out [sn:%lli]\n",
- inst->seqNum);
- return;
- }
-
iewStage->wakeCPU();
if (fu_idx > -1)
/*
- * Copyright (c) 2011 ARM Limited
+ * Copyright (c) 2011-2012 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
/** Sets the pointer to the list of active threads. */
void setActiveThreads(std::list<ThreadID> *at_ptr);
- /** Switches out the LSQ. */
- void switchOut();
+
+ /** Perform sanity checks after a drain. */
+ void drainSanityCheck() const;
+ /** Has the LSQ drained? */
+ bool isDrained() const;
/** Takes over execution from another CPU's thread. */
void takeOverFrom();
*/
bool isFull(ThreadID tid);
+ /** Returns if the LSQ is empty (both LQ and SQ are empty). */
+ bool isEmpty() const;
+ /** Returns if all of the LQs are empty. */
+ bool lqEmpty() const;
+ /** Returns if all of the SQs are empty. */
+ bool sqEmpty() const;
+
/** Returns if any of the LQs are full. */
bool lqFull();
/** Returns if the LQ of a given thread is full. */
{ return thread[tid].willWB(); }
/** Returns if the cache is currently blocked. */
- bool cacheBlocked()
+ bool cacheBlocked() const
{ return retryTid != InvalidThreadID; }
/** Sets the retry thread id, indicating that one of the LSQUnits
#include <string>
#include "cpu/o3/lsq.hh"
+#include "debug/Drain.hh"
#include "debug/Fetch.hh"
#include "debug/LSQ.hh"
#include "debug/Writeback.hh"
template <class Impl>
void
-LSQ<Impl>::switchOut()
+LSQ<Impl>::drainSanityCheck() const
{
- for (ThreadID tid = 0; tid < numThreads; tid++) {
- thread[tid].switchOut();
+ assert(isDrained());
+
+ for (ThreadID tid = 0; tid < numThreads; tid++)
+ thread[tid].drainSanityCheck();
+}
+
+template <class Impl>
+bool
+LSQ<Impl>::isDrained() const
+{
+ bool drained(true);
+
+ if (!lqEmpty()) {
+ DPRINTF(Drain, "Not drained, LQ not empty.\n");
+ drained = false;
+ }
+
+ if (!sqEmpty()) {
+ DPRINTF(Drain, "Not drained, SQ not empty.\n");
+ drained = false;
+ }
+
+ if (retryTid != InvalidThreadID) {
+ DPRINTF(Drain, "Not drained, the LSQ has blocked the caches.\n");
+ drained = false;
}
+
+ return drained;
}
template <class Impl>
return thread[tid].lqFull() || thread[tid].sqFull();
}
+template<class Impl>
+bool
+LSQ<Impl>::isEmpty() const
+{
+ return lqEmpty() && sqEmpty();
+}
+
+template<class Impl>
+bool
+LSQ<Impl>::lqEmpty() const
+{
+ list<ThreadID>::const_iterator threads = activeThreads->begin();
+ list<ThreadID>::const_iterator end = activeThreads->end();
+
+ while (threads != end) {
+ ThreadID tid = *threads++;
+
+ if (!thread[tid].lqEmpty())
+ return false;
+ }
+
+ return true;
+}
+
+template<class Impl>
+bool
+LSQ<Impl>::sqEmpty() const
+{
+ list<ThreadID>::const_iterator threads = activeThreads->begin();
+ list<ThreadID>::const_iterator end = activeThreads->end();
+
+ while (threads != end) {
+ ThreadID tid = *threads++;
+
+ if (!thread[tid].sqEmpty())
+ return false;
+ }
+
+ return true;
+}
+
template<class Impl>
bool
LSQ<Impl>::lqFull()
/*
+ * Copyright (c) 2012 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
* Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
/** Sets the pointer to the dcache port. */
void setDcachePort(MasterPort *dcache_port);
- /** Switches out LSQ unit. */
- void switchOut();
+ /** Perform sanity checks after a drain. */
+ void drainSanityCheck() const;
/** Takes over from another CPU's thread. */
void takeOverFrom();
- /** Returns if the LSQ is switched out. */
- bool isSwitchedOut() { return switchedOut; }
-
/** Ticks the LSQ unit, which in this case only resets the number of
* used cache ports.
* @todo: Move the number of used ports up to the LSQ level so it can
/** Returns if either the LQ or SQ is full. */
bool isFull() { return lqFull() || sqFull(); }
+ /** Returns if both the LQ and SQ are empty. */
+ bool isEmpty() const { return lqEmpty() && sqEmpty(); }
+
/** Returns if the LQ is full. */
bool lqFull() { return loads >= (LQEntries - 1); }
/** Returns if the SQ is full. */
bool sqFull() { return stores >= (SQEntries - 1); }
+ /** Returns if the LQ is empty. */
+ bool lqEmpty() const { return loads == 0; }
+
+ /** Returns if the SQ is empty. */
+ bool sqEmpty() const { return stores == 0; }
+
/** Returns the number of instructions in the LSQ. */
unsigned getCount() { return loads + stores; }
void recvRetry();
private:
+ /** Reset the LSQ state */
+ void resetState();
+
/** Writes back the instruction, sending it to IEW. */
void writeback(DynInstPtr &inst, PacketPtr pkt);
/** The number of used cache ports in this cycle. */
int usedPorts;
- /** Is the LSQ switched out. */
- bool switchedOut;
-
//list<InstSeqNum> mshrSeqNums;
/** Address Mask for a cache block (e.g. ~(cache_block_size-1)) */
void
LSQUnit<Impl>::WritebackEvent::process()
{
- if (!lsqPtr->isSwitchedOut()) {
- lsqPtr->writeback(inst, pkt);
- }
+ assert(!lsqPtr->cpu->switchedOut());
+
+ lsqPtr->writeback(inst, pkt);
if (pkt->senderState)
delete pkt->senderState;
return;
}
- if (isSwitchedOut() || inst->isSquashed()) {
+ assert(!cpu->switchedOut());
+ if (inst->isSquashed()) {
iewStage->decrWb(inst->seqNum);
} else {
if (!state->noWB) {
DPRINTF(LSQUnit, "Creating LSQUnit%i object.\n",id);
- switchedOut = false;
-
- cacheBlockMask = 0;
-
lsq = lsq_ptr;
lsqID = id;
depCheckShift = params->LSQDepCheckShift;
checkLoads = params->LSQCheckLoads;
+ cachePorts = params->cachePorts;
+ needsTSO = params->needsTSO;
+
+ resetState();
+}
+
+
+template<class Impl>
+void
+LSQUnit<Impl>::resetState()
+{
+ loads = stores = storesToWB = 0;
loadHead = loadTail = 0;
storeHead = storeWBIdx = storeTail = 0;
usedPorts = 0;
- cachePorts = params->cachePorts;
retryPkt = NULL;
memDepViolator = NULL;
blockedLoadSeqNum = 0;
- needsTSO = params->needsTSO;
+
+ stalled = false;
+ isLoadBlocked = false;
+ loadBlockedHandled = false;
+
+ cacheBlockMask = 0;
}
template<class Impl>
template<class Impl>
void
-LSQUnit<Impl>::switchOut()
+LSQUnit<Impl>::drainSanityCheck() const
{
- switchedOut = true;
- for (int i = 0; i < loadQueue.size(); ++i) {
+ for (int i = 0; i < loadQueue.size(); ++i)
assert(!loadQueue[i]);
- loadQueue[i] = NULL;
- }
assert(storesToWB == 0);
+ assert(!retryPkt);
}
template<class Impl>
void
LSQUnit<Impl>::takeOverFrom()
{
- switchedOut = false;
- loads = stores = storesToWB = 0;
-
- loadHead = loadTail = 0;
-
- storeHead = storeWBIdx = storeTail = 0;
-
- usedPorts = 0;
-
- memDepViolator = NULL;
-
- blockedLoadSeqNum = 0;
-
- stalled = false;
- isLoadBlocked = false;
- loadBlockedHandled = false;
-
- // Just incase the memory system changed out from under us
- cacheBlockMask = 0;
+ resetState();
}
template<class Impl>
/*
+ * Copyright (c) 2012 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
* Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
/** Registers statistics. */
void regStats();
- /** Switches out the memory dependence predictor. */
- void switchOut();
+ /** Perform sanity checks after a drain. */
+ void drainSanityCheck() const;
/** Takes over from another CPU's thread. */
void takeOverFrom();
/*
+ * Copyright (c) 2012 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
* Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
template <class MemDepPred, class Impl>
void
-MemDepUnit<MemDepPred, Impl>::switchOut()
+MemDepUnit<MemDepPred, Impl>::drainSanityCheck() const
{
- assert(instList[0].empty());
assert(instsToReplay.empty());
assert(memDepHash.empty());
- // Clear any state.
- for (int i = 0; i < Impl::MaxThreads; ++i) {
- instList[i].clear();
- }
- instsToReplay.clear();
- memDepHash.clear();
+ for (int i = 0; i < Impl::MaxThreads; ++i)
+ assert(instList[i].empty());
+ assert(instsToReplay.empty());
+ assert(memDepHash.empty());
}
template <class MemDepPred, class Impl>
/*
+ * Copyright (c) 2012 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
* Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
/** Sets pointer to the scoreboard. */
void setScoreboard(Scoreboard *_scoreboard);
- /** Drains the rename stage. */
- bool drain();
+ /** Perform sanity checks after a drain. */
+ void drainSanityCheck() const;
- /** Resumes execution after a drain. */
- void resume() { }
-
- /** Switches out the rename stage. */
- void switchOut();
+ /** Has the stage drained? */
+ bool isDrained() const;
/** Takes over from another CPU's thread. */
void takeOverFrom();
void dumpHistory();
private:
+ /** Reset this pipeline stage */
+ void resetStage();
+
/** Determines what to do based on rename's current status.
* @param status_change rename() sets this variable if there was a status
* change (ie switching from blocking to unblocking).
/*
- * Copyright (c) 2010 ARM Limited
+ * Copyright (c) 2010-2012 ARM Limited
* All rights reserved.
*
* The license below extends only to copyright in the software and shall
commitToRenameDelay(params->commitToRenameDelay),
renameWidth(params->renameWidth),
commitWidth(params->commitWidth),
- resumeSerialize(false),
- resumeUnblocking(false),
numThreads(params->numThreads),
maxPhysicalRegs(params->numPhysIntRegs + params->numPhysFloatRegs)
{
- _status = Inactive;
-
- for (ThreadID tid = 0; tid < numThreads; tid++) {
- renameStatus[tid] = Idle;
-
- freeEntries[tid].iqEntries = 0;
- freeEntries[tid].lsqEntries = 0;
- freeEntries[tid].robEntries = 0;
-
- stalls[tid].iew = false;
- stalls[tid].commit = false;
- serializeInst[tid] = NULL;
-
- instsInProgress[tid] = 0;
-
- emptyROB[tid] = true;
-
- serializeOnNextInst[tid] = false;
- }
-
// @todo: Make into a parameter.
skidBufferMax = (2 * (decodeToRenameDelay * params->decodeWidth)) + renameWidth;
}
void
DefaultRename<Impl>::startupStage()
{
+ resetStage();
+}
+
+template <class Impl>
+void
+DefaultRename<Impl>::resetStage()
+{
+ _status = Inactive;
+
+ resumeSerialize = false;
+ resumeUnblocking = false;
+
// Grab the number of free entries directly from the stages.
for (ThreadID tid = 0; tid < numThreads; tid++) {
+ renameStatus[tid] = Idle;
+
freeEntries[tid].iqEntries = iew_ptr->instQueue.numFreeEntries(tid);
freeEntries[tid].lsqEntries = iew_ptr->ldstQueue.numFreeEntries(tid);
freeEntries[tid].robEntries = commit_ptr->numROBFreeEntries(tid);
emptyROB[tid] = true;
+
+ stalls[tid].iew = false;
+ stalls[tid].commit = false;
+ serializeInst[tid] = NULL;
+
+ instsInProgress[tid] = 0;
+
+ serializeOnNextInst[tid] = false;
}
}
template <class Impl>
bool
-DefaultRename<Impl>::drain()
+DefaultRename<Impl>::isDrained() const
{
- // Rename is ready to switch out at any time.
- cpu->signalDrained();
+ for (ThreadID tid = 0; tid < numThreads; tid++) {
+ if (instsInProgress[tid] != 0 ||
+ !historyBuffer[tid].empty() ||
+ !skidBuffer[tid].empty() ||
+ !insts[tid].empty())
+ return false;
+ }
return true;
}
template <class Impl>
void
-DefaultRename<Impl>::switchOut()
+DefaultRename<Impl>::takeOverFrom()
{
- // Clear any state, fix up the rename map.
- for (ThreadID tid = 0; tid < numThreads; tid++) {
- typename std::list<RenameHistory>::iterator hb_it =
- historyBuffer[tid].begin();
-
- while (!historyBuffer[tid].empty()) {
- assert(hb_it != historyBuffer[tid].end());
-
- DPRINTF(Rename, "[tid:%u]: Removing history entry with sequence "
- "number %i.\n", tid, (*hb_it).instSeqNum);
-
- // Tell the rename map to set the architected register to the
- // previous physical register that it was renamed to.
- renameMap[tid]->setEntry(hb_it->archReg, hb_it->prevPhysReg);
-
- // Put the renamed physical register back on the free list.
- freeList->addReg(hb_it->newPhysReg);
-
- // Be sure to mark its register as ready if it's a misc register.
- if (hb_it->newPhysReg >= maxPhysicalRegs) {
- scoreboard->setReg(hb_it->newPhysReg);
- }
-
- historyBuffer[tid].erase(hb_it++);
- }
- insts[tid].clear();
- skidBuffer[tid].clear();
- }
+ resetStage();
}
template <class Impl>
void
-DefaultRename<Impl>::takeOverFrom()
+DefaultRename<Impl>::drainSanityCheck() const
{
- _status = Inactive;
- startupStage();
-
- // Reset all state prior to taking over from the other CPU.
for (ThreadID tid = 0; tid < numThreads; tid++) {
- renameStatus[tid] = Idle;
-
- stalls[tid].iew = false;
- stalls[tid].commit = false;
- serializeInst[tid] = NULL;
-
- instsInProgress[tid] = 0;
-
- emptyROB[tid] = true;
-
- serializeOnNextInst[tid] = false;
+ assert(historyBuffer[tid].empty());
+ assert(insts[tid].empty());
+ assert(skidBuffer[tid].empty());
+ assert(instsInProgress[tid] == 0);
}
}
/*
+ * Copyright (c) 2012 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
* Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
*/
void setActiveThreads(std::list<ThreadID> *at_ptr);
- /** Switches out the ROB. */
- void switchOut();
+ /** Perform sanity checks after a drain. */
+ void drainSanityCheck() const;
/** Takes over another CPU's thread. */
void takeOverFrom();
{ return threadEntries[tid] == numEntries; }
/** Returns if the ROB is empty. */
- bool isEmpty()
+ bool isEmpty() const
{ return numInstsInROB == 0; }
/** Returns if a specific thread's partition is empty. */
- bool isEmpty(ThreadID tid)
+ bool isEmpty(ThreadID tid) const
{ return threadEntries[tid] == 0; }
/** Executes the squash, marking squashed instructions. */
void regStats();
private:
+ /** Reset the ROB state */
+ void resetState();
+
/** Pointer to the CPU. */
O3CPU *cpu;
/*
+ * Copyright (c) 2012 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
* Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
numInstsInROB(0),
numThreads(_numThreads)
{
- for (ThreadID tid = 0; tid < numThreads; tid++) {
- squashedSeqNum[tid] = 0;
- doneSquashing[tid] = true;
- threadEntries[tid] = 0;
- }
-
std::string policy = _smtROBPolicy;
//Convert string to lowercase
"Partitioned, Threshold}");
}
- // Set the per-thread iterators to the end of the instruction list.
- for (ThreadID tid = 0; tid < numThreads; tid++) {
+ resetState();
+}
+
+template <class Impl>
+void
+ROB<Impl>::resetState()
+{
+ for (ThreadID tid = 0; tid < numThreads; tid++) {
+ doneSquashing[tid] = true;
+ threadEntries[tid] = 0;
squashIt[tid] = instList[tid].end();
+ squashedSeqNum[tid] = 0;
}
+ numInstsInROB = 0;
// Initialize the "universal" ROB head & tail point to invalid
// pointers
template <class Impl>
void
-ROB<Impl>::switchOut()
+ROB<Impl>::drainSanityCheck() const
{
- for (ThreadID tid = 0; tid < numThreads; tid++) {
- instList[tid].clear();
- }
+ for (ThreadID tid = 0; tid < numThreads; tid++)
+ assert(instList[tid].empty());
+ assert(isEmpty());
}
template <class Impl>
void
ROB<Impl>::takeOverFrom()
{
- for (ThreadID tid = 0; tid < numThreads; tid++) {
- doneSquashing[tid] = true;
- threadEntries[tid] = 0;
- squashIt[tid] = instList[tid].end();
- }
- numInstsInROB = 0;
-
- // Initialize the "universal" ROB head & tail point to invalid
- // pointers
- head = instList[0].end();
- tail = instList[0].end();
+ resetState();
}
template <class Impl>